diff --git a/api/api/versions.json b/api/api/versions.json index 44b475d44..661d16565 100644 --- a/api/api/versions.json +++ b/api/api/versions.json @@ -3,11 +3,11 @@ { "version": "v1", "status": "active", - "release_date": "2025-10-11T20:19:39.955308752+05:30", + "release_date": "2025-10-15T21:35:07.482629937+05:30", "end_of_life": "0001-01-01T00:00:00Z", "changes": [ "Initial API version" ] } ] -} +} \ No newline at end of file diff --git a/api/doc/openapi.json b/api/doc/openapi.json index 3231ec28d..0e32869e3 100644 --- a/api/doc/openapi.json +++ b/api/doc/openapi.json @@ -1 +1 @@ -{"components":{"schemas":{"ContainerLogsRequest":{"description":"ContainerLogsRequest schema","properties":{"follow":{"type":"boolean"},"id":{"type":"string"},"since":{"type":"string"},"stderr":{"type":"boolean"},"stdout":{"type":"boolean"},"tail":{"type":"integer"},"until":{"type":"string"}},"type":"object"},"CopyDirectory":{"description":"CopyDirectory schema","properties":{"from_path":{"type":"string"},"to_path":{"type":"string"}},"type":"object"},"CreateDeploymentRequest":{"description":"CreateDeploymentRequest schema","properties":{"base_path":{"nullable":true,"type":"string"},"branch":{"type":"string"},"build_pack":{"type":"string"},"build_variables":{"additionalProperties":{"type":"string"},"type":"object"},"dockerfile_path":{"nullable":true,"type":"string"},"domain":{"type":"string"},"environment":{"type":"string"},"environment_variables":{"additionalProperties":{"type":"string"},"type":"object"},"name":{"type":"string"},"port":{"type":"integer"},"post_run_command":{"type":"string"},"pre_run_command":{"type":"string"},"repository":{"type":"string"}},"type":"object"},"CreateDirectoryRequest":{"description":"CreateDirectoryRequest schema","properties":{"path":{"type":"string"}},"type":"object"},"CreateDomainRequest":{"description":"CreateDomainRequest schema","properties":{"name":{"type":"string"},"organization_id":{}},"type":"object"},"CreateGithubConnectorRequest":{"description":"CreateGithubConnectorRequest schema","properties":{"app_id":{"type":"string"},"client_id":{"type":"string"},"client_secret":{"type":"string"},"pem":{"type":"string"},"slug":{"type":"string"},"webhook_secret":{"type":"string"}},"type":"object"},"CreateOrganizationRequest":{"description":"CreateOrganizationRequest schema","properties":{"description":{"type":"string"},"name":{"type":"string"}},"type":"object"},"CreateSMTPConfigRequest":{"description":"CreateSMTPConfigRequest schema","properties":{"from_email":{"type":"string"},"from_name":{"type":"string"},"host":{"type":"string"},"organization_id":{},"password":{"type":"string"},"port":{"type":"integer"},"username":{"type":"string"}},"type":"object"},"CreateWebhookConfigRequest":{"description":"CreateWebhookConfigRequest schema","properties":{"type":{"type":"string"},"webhook_url":{"type":"string"}},"required":["type"],"type":"object"},"DeleteDeploymentRequest":{"description":"DeleteDeploymentRequest schema","properties":{"id":{}},"type":"object"},"DeleteDirectoryRequest":{"description":"DeleteDirectoryRequest schema","properties":{"path":{"type":"string"}},"type":"object"},"DeleteDomainRequest":{"description":"DeleteDomainRequest schema","properties":{"id":{"type":"string"}},"type":"object"},"DeleteOrganizationRequest":{"description":"DeleteOrganizationRequest schema","properties":{"id":{"type":"string"}},"type":"object"},"DeleteSMTPConfigRequest":{"description":"DeleteSMTPConfigRequest schema","properties":{"id":{}},"type":"object"},"DeleteWebhookConfigRequest":{"description":"DeleteWebhookConfigRequest schema","properties":{"type":{"type":"string"}},"required":["type"],"type":"object"},"GetApplicationDeploymentsRequest":{"description":"GetApplicationDeploymentsRequest schema","properties":{"id":{"type":"string"},"limit":{"type":"string"},"page":{"type":"string"}},"type":"object"},"GetApplicationsRequest":{"description":"GetApplicationsRequest schema","properties":{"page":{"type":"string"},"page_size":{"type":"string"},"repository":{"type":"string"}},"type":"object"},"GetGithubRepositoryBranchesRequest":{"description":"GetGithubRepositoryBranchesRequest schema","properties":{"repository_name":{"type":"string"}},"required":["repository_name"],"type":"object"},"GetOrganizationUsersRequest":{"description":"GetOrganizationUsersRequest schema","properties":{"id":{"type":"string"}},"type":"object"},"HTTPError":{"description":"HTTPError schema","properties":{"detail":{"description":"Human readable error message","nullable":true,"type":"string"},"errors":{"items":{"properties":{"more":{"additionalProperties":{},"type":"object"},"name":{"type":"string"},"reason":{"type":"string"}},"type":"object"},"nullable":true,"type":"array"},"instance":{"nullable":true,"type":"string"},"status":{"description":"HTTP status code","example":403,"nullable":true,"type":"integer"},"title":{"description":"Short title of the error","nullable":true,"type":"string"},"type":{"description":"URL of the error type. Can be used to lookup the error in a documentation","nullable":true,"type":"string"}},"type":"object"},"InviteResendRequest":{"description":"InviteResendRequest schema","properties":{"email":{"type":"string"},"organization_id":{"type":"string"},"role":{"type":"string"}},"type":"object"},"InviteSendRequest":{"description":"InviteSendRequest schema","properties":{"email":{"type":"string"},"organization_id":{"type":"string"},"role":{"type":"string"}},"type":"object"},"ListFilesRequest":{"description":"ListFilesRequest schema","properties":{"path":{"type":"string"}},"type":"object"},"ListImagesRequest":{"description":"ListImagesRequest schema","properties":{"all":{"nullable":true,"type":"boolean"},"container_id":{"nullable":true,"type":"string"},"image_prefix":{"nullable":true,"type":"string"}},"type":"object"},"LogoutRequest":{"description":"LogoutRequest schema","properties":{"refresh_token":{"type":"string"}},"type":"object"},"MoveDirectory":{"description":"MoveDirectory schema","properties":{"from_path":{"type":"string"},"to_path":{"type":"string"}},"type":"object"},"PruneBuildCacheRequest":{"description":"PruneBuildCacheRequest schema","properties":{"all":{"nullable":true,"type":"boolean"},"filters":{"nullable":true,"type":"string"}},"type":"object"},"PruneImagesRequest":{"description":"PruneImagesRequest schema","properties":{"dangling":{"nullable":true,"type":"boolean"},"label":{"nullable":true,"type":"string"},"until":{"nullable":true,"type":"string"}},"type":"object"},"ReDeployApplicationRequest":{"description":"ReDeployApplicationRequest schema","properties":{"force":{"type":"boolean"},"force_without_cache":{"type":"boolean"},"id":{}},"type":"object"},"RegisterRequest":{"description":"RegisterRequest schema","properties":{"email":{"type":"string"},"organization":{"type":"string"},"password":{"type":"string"},"type":{"type":"string"},"username":{"type":"string"}},"type":"object"},"RemoveUserFromOrganizationRequest":{"description":"RemoveUserFromOrganizationRequest schema","properties":{"organization_id":{"type":"string"},"user_id":{"type":"string"}},"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":{"id":{}},"type":"object"},"RollbackDeploymentRequest":{"description":"RollbackDeploymentRequest schema","properties":{"id":{}},"type":"object"},"TwoFactorLoginRequest":{"description":"TwoFactorLoginRequest schema","properties":{"code":{"type":"string"},"email":{"type":"string"},"password":{"type":"string"}},"type":"object"},"TwoFactorVerifyRequest":{"description":"TwoFactorVerifyRequest schema","properties":{"code":{"type":"string"}},"type":"object"},"UpdateAutoUpdateRequest":{"description":"UpdateAutoUpdateRequest schema","properties":{"auto_update":{"type":"boolean"}},"type":"object"},"UpdateAvatarRequest":{"description":"UpdateAvatarRequest schema","properties":{"avatarData":{"type":"string"}},"type":"object"},"UpdateCheckResponse":{"description":"UpdateCheckResponse schema","properties":{"current_version":{"type":"string"},"environment":{"type":"string"},"last_checked":{"format":"date-time","type":"string"},"latest_version":{"type":"string"},"update_available":{"type":"boolean"}},"type":"object"},"UpdateDeploymentRequest":{"description":"UpdateDeploymentRequest schema","properties":{"base_path":{"nullable":true,"type":"string"},"build_variables":{"additionalProperties":{"type":"string"},"nullable":true,"type":"object"},"dockerfile_path":{"nullable":true,"type":"string"},"environment_variables":{"additionalProperties":{"type":"string"},"nullable":true,"type":"object"},"force":{"nullable":true,"type":"boolean"},"id":{"nullable":true},"name":{"nullable":true,"type":"string"},"port":{"nullable":true,"type":"integer"},"post_run_command":{"nullable":true,"type":"string"},"pre_run_command":{"nullable":true,"type":"string"}},"type":"object"},"UpdateDomainRequest":{"description":"UpdateDomainRequest schema","properties":{"id":{"type":"string"},"name":{"type":"string"}},"type":"object"},"UpdateFeatureFlagRequest":{"description":"UpdateFeatureFlagRequest schema","properties":{"feature_name":{"type":"string"},"is_enabled":{"type":"boolean"}},"required":["feature_name"],"type":"object"},"UpdateFontRequest":{"description":"UpdateFontRequest schema","properties":{"font_family":{"type":"string"},"font_size":{"type":"integer"}},"type":"object"},"UpdateGithubConnectorRequest":{"description":"UpdateGithubConnectorRequest schema","properties":{"installation_id":{"type":"string"}},"type":"object"},"UpdateLanguageRequest":{"description":"UpdateLanguageRequest schema","properties":{"language":{"type":"string"}},"type":"object"},"UpdateOrganizationRequest":{"description":"UpdateOrganizationRequest schema","properties":{"description":{"type":"string"},"id":{"type":"string"},"name":{"type":"string"}},"type":"object"},"UpdatePreferenceRequest":{"description":"UpdatePreferenceRequest schema","properties":{"category":{"type":"string"},"enabled":{"type":"boolean"},"type":{"type":"string"}},"required":["category","type"],"type":"object"},"UpdateRequest":{"description":"UpdateRequest schema","properties":{"force":{"type":"boolean"}},"type":"object"},"UpdateResponse":{"description":"UpdateResponse schema","properties":{"message":{"type":"string"},"success":{"type":"boolean"}},"type":"object"},"UpdateSMTPConfigRequest":{"description":"UpdateSMTPConfigRequest schema","properties":{"from_email":{"nullable":true,"type":"string"},"from_name":{"nullable":true,"type":"string"},"host":{"nullable":true,"type":"string"},"id":{},"organization_id":{},"password":{"nullable":true,"type":"string"},"port":{"nullable":true,"type":"integer"},"username":{"nullable":true,"type":"string"}},"type":"object"},"UpdateThemeRequest":{"description":"UpdateThemeRequest schema","properties":{"theme":{"type":"string"}},"type":"object"},"UpdateUserNameRequest":{"description":"UpdateUserNameRequest schema","properties":{"name":{"type":"string"}},"type":"object"},"UpdateUserRoleRequest":{"description":"UpdateUserRoleRequest schema","properties":{"organization_id":{"type":"string"},"role":{"type":"string"},"user_id":{"type":"string"}},"type":"object"},"UpdateWebhookConfigRequest":{"description":"UpdateWebhookConfigRequest schema","properties":{"is_active":{"nullable":true,"type":"boolean"},"type":{"type":"string"},"webhook_url":{"nullable":true,"type":"string"}},"required":["type"],"type":"object"},"unknown-interface":{"description":"unknown-interface schema"}},"securitySchemes":{"bearerAuth":{"bearerFormat":"JWT","description":"Enter your JWT token in the format: Bearer \u003ctoken\u003e","scheme":"bearer","type":"http"}}},"info":{"description":"\nThis is the autogenerated OpenAPI documentation for your [Fuego](https://github.com/go-fuego/fuego) API.\n\nBelow is a Fuego Cheatsheet to help you get started. Don't hesitate to check the [Fuego documentation](https://go-fuego.dev) for more details.\n\nHappy coding! 🔥\n\n## Usage\n\n### Route registration\n\n```go\nfunc main() {\n\t// Create a new server\n\ts := fuego.NewServer()\n\n\t// Register some routes\n\tfuego.Post(s, \"/hello\", myController)\n\tfuego.Get(s, \"/myPath\", otherController)\n\tfuego.Put(s, \"/hello\", thirdController)\n\n\tadminRoutes := fuego.Group(s, \"/admin\")\n\tfuego.Use(adminRoutes, myMiddleware) // This middleware (for authentication, etc...) will be available for routes starting by /admin/*, \n\tfuego.Get(adminRoutes, \"/hello\", groupController) // This route will be available at /admin/hello\n\n\t// Start the server\n\ts.Start()\n}\n```\n\n### Basic controller\n\n```go\ntype MyBody struct {\n\tName string `json:\"name\" validate:\"required,max=30\"`\n}\n\ntype MyResponse struct {\n\tAnswer string `json:\"answer\"`\n}\n\nfunc hello(ctx fuego.ContextWithBody[MyBody]) (*MyResponse, error) {\n\tbody, err := ctx.Body()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn \u0026MyResponse{Answer: \"Hello \" + body.Name}, nil\n}\n```\n\n### Add openAPI information to the route\n\n```go\nimport (\n\t\"github.com/go-fuego/fuego\"\n\t\"github.com/go-fuego/fuego/option\"\n\t\"github.com/go-fuego/fuego/param\"\n)\n\nfunc main() {\n\ts := fuego.NewServer()\n\n\t// Custom OpenAPI options\n\tfuego.Post(s, \"/\", myController\n\t\toption.Description(\"This route does something...\"),\n\t\toption.Summary(\"This is my summary\"),\n\t\toption.Tags(\"MyTag\"), // A tag is set by default according to the return type (can be deactivated)\n\t\toption.Deprecated(), // Marks the route as deprecated in the OpenAPI spec\n\n\t\toption.Query(\"name\", \"Declares a query parameter with default value\", param.Default(\"Carmack\")),\n\t\toption.Header(\"Authorization\", \"Bearer token\", param.Required()),\n\t\toptionPagination,\n\t\toptionCustomBehavior,\n\t)\n\n\ts.Run()\n}\n\nvar optionPagination = option.Group(\n\toption.QueryInt(\"page\", \"Page number\", param.Default(1), param.Example(\"1st page\", 1), param.Example(\"42nd page\", 42)),\n\toption.QueryInt(\"perPage\", \"Number of items per page\"),\n)\n\nvar optionCustomBehavior = func(r *fuego.BaseRoute) {\n\tr.XXX = \"YYY\"\n}\n```\n\nThen, in the controller\n\n```go\ntype MyResponse struct {\n\tAnswer string `json:\"answer\"`\n}\n\nfunc getAllPets(ctx fuego.ContextNoBody) (*MyResponse, error) {\n\tname := ctx.QueryParam(\"name\")\n\tperPage, _ := ctx.QueryParamIntErr(\"per_page\")\n\n\treturn \u0026MyResponse{Answer: \"Hello \" + name}, nil\n}\n```\n","title":"OpenAPI","version":"0.0.1"},"openapi":"3.1.0","paths":{"/api/v1/audit/logs":{"get":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/audit/controller.(*AuditController).GetRecentAuditLogs`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func23`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func24`\n\n---\n\n","operationId":"GET_/api/v1/audit/logs","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 recent audit logs","tags":["api/v1/audit"]}},"/api/v1/auth/2fa-login":{"post":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/auth/controller.(*AuthController).TwoFactorLogin`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n\n---\n\n","operationId":"POST_/api/v1/auth/2fa-login","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/TwoFactorLoginRequest"}}},"description":"Request body for types.TwoFactorLoginRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"two factor login","tags":["api/v1/auth"]}},"/api/v1/auth/create-user":{"post":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/auth/controller.(*AuthController).CreateUser`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n\n---\n\n","operationId":"POST_/api/v1/auth/create-user","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/RegisterRequest"}}},"description":"Request body for types.RegisterRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"create user","tags":["api/v1/auth"]}},"/api/v1/auth/disable-2fa":{"post":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/auth/controller.(*AuthController).DisableTwoFactor`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n\n---\n\n","operationId":"POST_/api/v1/auth/disable-2fa","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"disable two factor","tags":["api/v1/auth"]}},"/api/v1/auth/is-admin-registered":{"get":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/auth/controller.(*AuthController).IsAdminRegistered`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n\n---\n\n","operationId":"GET_/api/v1/auth/is-admin-registered","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"is admin registered","tags":["api/v1/auth"]}},"/api/v1/auth/logout":{"post":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/auth/controller.(*AuthController).Logout`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n\n---\n\n","operationId":"POST_/api/v1/auth/logout","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/LogoutRequest"}}},"description":"Request body for types.LogoutRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"logout","tags":["api/v1/auth"]}},"/api/v1/auth/send-verification-email":{"post":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/auth/controller.(*AuthController).SendVerificationEmail`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n\n---\n\n","operationId":"POST_/api/v1/auth/send-verification-email","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"send verification email","tags":["api/v1/auth"]}},"/api/v1/auth/setup-2fa":{"post":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/auth/controller.(*AuthController).SetupTwoFactor`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n\n---\n\n","operationId":"POST_/api/v1/auth/setup-2fa","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"setup two factor","tags":["api/v1/auth"]}},"/api/v1/auth/verify-2fa":{"post":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/auth/controller.(*AuthController).VerifyTwoFactor`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n\n---\n\n","operationId":"POST_/api/v1/auth/verify-2fa","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/TwoFactorVerifyRequest"}}},"description":"Request body for types.TwoFactorVerifyRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"verify two factor","tags":["api/v1/auth"]}},"/api/v1/auth/verify-email":{"get":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/auth/controller.(*AuthController).VerifyEmail`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n\n---\n\n","operationId":"GET_/api/v1/auth/verify-email","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"verify email","tags":["api/v1/auth"]}},"/api/v1/container":{"get":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/container/controller.(*ContainerController).ListContainers`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func26`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func27`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func28`\n\n---\n\n","operationId":"GET_/api/v1/container","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"list containers","tags":["api/v1/container"]}},"/api/v1/container/images":{"post":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/container/controller.(*ContainerController).ListImages`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func26`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func27`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func28`\n\n---\n\n","operationId":"POST_/api/v1/container/images","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/ListImagesRequest"}}},"description":"Request body for controller.ListImagesRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"list images","tags":["api/v1/container"]}},"/api/v1/container/prune/build-cache":{"post":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/container/controller.(*ContainerController).PruneBuildCache`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func26`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func27`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func28`\n\n---\n\n","operationId":"POST_/api/v1/container/prune/build-cache","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/PruneBuildCacheRequest"}}},"description":"Request body for controller.PruneBuildCacheRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"prune build cache","tags":["api/v1/container"]}},"/api/v1/container/prune/images":{"post":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/container/controller.(*ContainerController).PruneImages`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func26`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func27`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func28`\n\n---\n\n","operationId":"POST_/api/v1/container/prune/images","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/PruneImagesRequest"}}},"description":"Request body for controller.PruneImagesRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"prune images","tags":["api/v1/container"]}},"/api/v1/container/{container_id}":{"delete":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/container/controller.(*ContainerController).RemoveContainer`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func26`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func27`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func28`\n\n---\n\n","operationId":"DELETE_/api/v1/container/:container_id","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}},{"in":"path","name":"container_id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"remove container","tags":["api/v1/container"]},"get":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/container/controller.(*ContainerController).GetContainer`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func26`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func27`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func28`\n\n---\n\n","operationId":"GET_/api/v1/container/:container_id","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}},{"in":"path","name":"container_id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 container","tags":["api/v1/container"]}},"/api/v1/container/{container_id}/logs":{"post":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/container/controller.(*ContainerController).GetContainerLogs`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func26`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func27`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func28`\n\n---\n\n","operationId":"POST_/api/v1/container/:container_id/logs","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}},{"in":"path","name":"container_id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/ContainerLogsRequest"}}},"description":"Request body for types.ContainerLogsRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 container logs","tags":["api/v1/container"]}},"/api/v1/container/{container_id}/restart":{"post":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/container/controller.(*ContainerController).RestartContainer`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func26`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func27`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func28`\n\n---\n\n","operationId":"POST_/api/v1/container/:container_id/restart","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}},{"in":"path","name":"container_id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"restart container","tags":["api/v1/container"]}},"/api/v1/container/{container_id}/start":{"post":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/container/controller.(*ContainerController).StartContainer`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func26`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func27`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func28`\n\n---\n\n","operationId":"POST_/api/v1/container/:container_id/start","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}},{"in":"path","name":"container_id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"start container","tags":["api/v1/container"]}},"/api/v1/container/{container_id}/stop":{"post":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/container/controller.(*ContainerController).StopContainer`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func26`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func27`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func28`\n\n---\n\n","operationId":"POST_/api/v1/container/:container_id/stop","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}},{"in":"path","name":"container_id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"stop container","tags":["api/v1/container"]}},"/api/v1/deploy/application":{"delete":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/deploy/controller.(*DeployController).DeleteApplication`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func20`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func21`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func22`\n\n---\n\n","operationId":"DELETE_/api/v1/deploy/application","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/DeleteDeploymentRequest"}}},"description":"Request body for types.DeleteDeploymentRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 application","tags":["api/v1/deploy","application"]},"get":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/deploy/controller.(*DeployController).GetApplicationById`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func20`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func21`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func22`\n\n---\n\n","operationId":"GET_/api/v1/deploy/application","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 application by id","tags":["api/v1/deploy","application"]},"post":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/deploy/controller.(*DeployController).HandleDeploy`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func20`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func21`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func22`\n\n---\n\n","operationId":"POST_/api/v1/deploy/application","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/CreateDeploymentRequest"}}},"description":"Request body for types.CreateDeploymentRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"handle deploy","tags":["api/v1/deploy","application"]},"put":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/deploy/controller.(*DeployController).UpdateApplication`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func20`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func21`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func22`\n\n---\n\n","operationId":"PUT_/api/v1/deploy/application","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/UpdateDeploymentRequest"}}},"description":"Request body for types.UpdateDeploymentRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 application","tags":["api/v1/deploy","application"]}},"/api/v1/deploy/application/deployments":{"get":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/deploy/controller.(*DeployController).GetApplicationDeployments`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func20`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func21`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func22`\n\n---\n\n","operationId":"GET_/api/v1/deploy/application/deployments","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/GetApplicationDeploymentsRequest"}}},"description":"Request body for controller.GetApplicationDeploymentsRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 application deployments","tags":["api/v1/deploy","application"]}},"/api/v1/deploy/application/deployments/{deployment_id}":{"get":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/deploy/controller.(*DeployController).GetDeploymentById`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func20`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func21`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func22`\n\n---\n\n","operationId":"GET_/api/v1/deploy/application/deployments/:deployment_id","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}},{"in":"path","name":"deployment_id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 deployment by id","tags":["api/v1/deploy","application"]}},"/api/v1/deploy/application/deployments/{deployment_id}/logs":{"get":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/deploy/controller.(*DeployController).GetDeploymentLogs`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func20`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func21`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func22`\n\n---\n\n","operationId":"GET_/api/v1/deploy/application/deployments/:deployment_id/logs","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}},{"in":"path","name":"deployment_id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 deployment logs","tags":["api/v1/deploy","application"]}},"/api/v1/deploy/application/logs/{application_id}":{"get":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/deploy/controller.(*DeployController).GetLogs`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func20`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func21`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func22`\n\n---\n\n","operationId":"GET_/api/v1/deploy/application/logs/:application_id","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}},{"in":"path","name":"application_id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 logs","tags":["api/v1/deploy","application"]}},"/api/v1/deploy/application/redeploy":{"post":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/deploy/controller.(*DeployController).ReDeployApplication`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func20`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func21`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func22`\n\n---\n\n","operationId":"POST_/api/v1/deploy/application/redeploy","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/ReDeployApplicationRequest"}}},"description":"Request body for types.ReDeployApplicationRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"re deploy application","tags":["api/v1/deploy","application"]}},"/api/v1/deploy/application/restart":{"post":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/deploy/controller.(*DeployController).HandleRestart`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func20`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func21`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func22`\n\n---\n\n","operationId":"POST_/api/v1/deploy/application/restart","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/RestartDeploymentRequest"}}},"description":"Request body for types.RestartDeploymentRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"handle restart","tags":["api/v1/deploy","application"]}},"/api/v1/deploy/application/rollback":{"post":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/deploy/controller.(*DeployController).HandleRollback`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func20`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func21`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func22`\n\n---\n\n","operationId":"POST_/api/v1/deploy/application/rollback","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/RollbackDeploymentRequest"}}},"description":"Request body for types.RollbackDeploymentRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"handle rollback","tags":["api/v1/deploy","application"]}},"/api/v1/deploy/applications":{"get":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/deploy/controller.(*DeployController).GetApplications`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func20`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func21`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func22`\n\n---\n\n","operationId":"GET_/api/v1/deploy/applications","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/GetApplicationsRequest"}}},"description":"Request body for controller.GetApplicationsRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 applications","tags":["api/v1/deploy"]}},"/api/v1/domain":{"delete":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/domain/controller.(*DomainsController).DeleteDomain`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func3`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func5`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func7`\n\n---\n\n","operationId":"DELETE_/api/v1/domain","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/DeleteDomainRequest"}}},"description":"Request body for types.DeleteDomainRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 domain","tags":["api/v1/domain"]},"post":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/domain/controller.(*DomainsController).CreateDomain`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func3`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func5`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func7`\n\n---\n\n","operationId":"POST_/api/v1/domain","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/CreateDomainRequest"}}},"description":"Request body for types.CreateDomainRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"create domain","tags":["api/v1/domain"]},"put":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/domain/controller.(*DomainsController).UpdateDomain`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func3`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func5`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func7`\n\n---\n\n","operationId":"PUT_/api/v1/domain","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/UpdateDomainRequest"}}},"description":"Request body for types.UpdateDomainRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 domain","tags":["api/v1/domain"]}},"/api/v1/domain/generate":{"get":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/domain/controller.(*DomainsController).GenerateRandomSubDomain`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func3`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func5`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func7`\n\n---\n\n","operationId":"GET_/api/v1/domain/generate","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"generate random sub domain","tags":["api/v1/domain"]}},"/api/v1/domains":{"get":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/domain/controller.(*DomainsController).GetDomains`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func4`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func6`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func8`\n\n---\n\n","operationId":"GET_/api/v1/domains","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 domains","tags":["api/v1/domains"]}},"/api/v1/feature-flags":{"get":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/feature-flags/controller.(*FeatureFlagController).GetFeatureFlags`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n\n---\n\n","operationId":"GET_/api/v1/feature-flags","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 feature flags","tags":["api/v1/feature-flags"]},"put":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/feature-flags/controller.(*FeatureFlagController).UpdateFeatureFlag`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func25`\n\n---\n\n","operationId":"PUT_/api/v1/feature-flags","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/UpdateFeatureFlagRequest"}}},"description":"Request body for types.UpdateFeatureFlagRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 feature flag","tags":["api/v1/feature-flags"]}},"/api/v1/feature-flags/check":{"get":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/feature-flags/controller.(*FeatureFlagController).IsFeatureEnabled`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n\n---\n\n","operationId":"GET_/api/v1/feature-flags/check","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"is feature enabled","tags":["api/v1/feature-flags"]}},"/api/v1/file-manager":{"get":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/file-manager/controller.(*FileManagerController).ListFiles`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func17`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func18`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func19`\n\n---\n\n","operationId":"GET_/api/v1/file-manager","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/ListFilesRequest"}}},"description":"Request body for controller.ListFilesRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"list files","tags":["api/v1/file-manager"]}},"/api/v1/file-manager/copy-directory":{"post":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/file-manager/controller.(*FileManagerController).CopyDirectory`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func17`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func18`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func19`\n\n---\n\n","operationId":"POST_/api/v1/file-manager/copy-directory","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/CopyDirectory"}}},"description":"Request body for controller.CopyDirectory","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"copy directory","tags":["api/v1/file-manager"]}},"/api/v1/file-manager/create-directory":{"post":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/file-manager/controller.(*FileManagerController).CreateDirectory`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func17`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func18`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func19`\n\n---\n\n","operationId":"POST_/api/v1/file-manager/create-directory","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/CreateDirectoryRequest"}}},"description":"Request body for controller.CreateDirectoryRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"create directory","tags":["api/v1/file-manager"]}},"/api/v1/file-manager/delete-directory":{"delete":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/file-manager/controller.(*FileManagerController).DeleteDirectory`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func17`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func18`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func19`\n\n---\n\n","operationId":"DELETE_/api/v1/file-manager/delete-directory","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/DeleteDirectoryRequest"}}},"description":"Request body for controller.DeleteDirectoryRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 directory","tags":["api/v1/file-manager"]}},"/api/v1/file-manager/move-directory":{"post":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/file-manager/controller.(*FileManagerController).MoveDirectory`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func17`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func18`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func19`\n\n---\n\n","operationId":"POST_/api/v1/file-manager/move-directory","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/MoveDirectory"}}},"description":"Request body for controller.MoveDirectory","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"move directory","tags":["api/v1/file-manager"]}},"/api/v1/file-manager/upload":{"post":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/file-manager/controller.(*FileManagerController).UploadFile`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func17`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func18`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func19`\n\n---\n\n","operationId":"POST_/api/v1/file-manager/upload","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"upload file","tags":["api/v1/file-manager"]}},"/api/v1/github-connector":{"post":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/github-connector/controller.(*GithubConnectorController).CreateGithubConnector`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func9`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func10`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func11`\n\n---\n\n","operationId":"POST_/api/v1/github-connector","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/CreateGithubConnectorRequest"}}},"description":"Request body for types.CreateGithubConnectorRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"create github connector","tags":["api/v1/github-connector"]},"put":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/github-connector/controller.(*GithubConnectorController).UpdateGithubConnectorRequest`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func9`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func10`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func11`\n\n---\n\n","operationId":"PUT_/api/v1/github-connector","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/UpdateGithubConnectorRequest"}}},"description":"Request body for types.UpdateGithubConnectorRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 github connector request","tags":["api/v1/github-connector"]}},"/api/v1/github-connector/all":{"get":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/github-connector/controller.(*GithubConnectorController).GetGithubConnectors`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func9`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func10`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func11`\n\n---\n\n","operationId":"GET_/api/v1/github-connector/all","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 github connectors","tags":["api/v1/github-connector"]}},"/api/v1/github-connector/repositories":{"get":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/github-connector/controller.(*GithubConnectorController).GetGithubRepositories`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func9`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func10`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func11`\n\n---\n\n","operationId":"GET_/api/v1/github-connector/repositories","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 github repositories","tags":["api/v1/github-connector"]}},"/api/v1/github-connector/repository/branches":{"post":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/github-connector/controller.(*GithubConnectorController).GetGithubRepositoryBranches`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func9`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func10`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func11`\n\n---\n\n","operationId":"POST_/api/v1/github-connector/repository/branches","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/GetGithubRepositoryBranchesRequest"}}},"description":"Request body for controller.GetGithubRepositoryBranchesRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 github repository branches","tags":["api/v1/github-connector"]}},"/api/v1/health":{"get":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/health.HealthCheck`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n\n---\n\n","operationId":"GET_/api/v1/health","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"health check","tags":["api/v1/health"]}},"/api/v1/health/versions":{"get":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal.(*Router).BasicRoutes.func1`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n\n---\n\n","operationId":"GET_/api/v1/health/versions","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/unknown-interface"}},"application/xml":{"schema":{"$ref":"#/components/schemas/unknown-interface"}}},"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":"func1","tags":["api/v1/health","versions"]}},"/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.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func12`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func13`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func14`\n\n---\n\n","operationId":"GET_/api/v1/notification/preferences","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func12`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func13`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func14`\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/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func12`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func13`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func14`\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/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func12`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func13`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func14`\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/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func12`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func13`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func14`\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/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func12`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func13`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func14`\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/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func12`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func13`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func14`\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/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func12`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func13`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func14`\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/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"create webhook config","tags":["api/v1/notification","webhook"]},"put":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/notification/controller.(*NotificationController).UpdateWebhookConfig`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func12`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func13`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func14`\n\n---\n\n","operationId":"PUT_/api/v1/notification/webhook","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/UpdateWebhookConfigRequest"}}},"description":"Request body for notification.UpdateWebhookConfigRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 webhook config","tags":["api/v1/notification","webhook"]}},"/api/v1/notification/webhook/{type}":{"get":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/notification/controller.(*NotificationController).GetWebhookConfig`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func12`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func13`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func14`\n\n---\n\n","operationId":"GET_/api/v1/notification/webhook/:type","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}},{"in":"path","name":"type","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 webhook config","tags":["api/v1/notification","webhook"]}},"/api/v1/organizations":{"delete":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/organization/controller.(*OrganizationsController).DeleteOrganization`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func15`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func16`\n\n---\n\n","operationId":"DELETE_/api/v1/organizations","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/DeleteOrganizationRequest"}}},"description":"Request body for types.DeleteOrganizationRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 organization","tags":["api/v1/organizations"]},"get":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/organization/controller.(*OrganizationsController).GetOrganization`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func15`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func16`\n\n---\n\n","operationId":"GET_/api/v1/organizations","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 organization","tags":["api/v1/organizations"]},"post":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/organization/controller.(*OrganizationsController).CreateOrganization`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func15`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func16`\n\n---\n\n","operationId":"POST_/api/v1/organizations","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/CreateOrganizationRequest"}}},"description":"Request body for types.CreateOrganizationRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"create organization","tags":["api/v1/organizations"]},"put":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/organization/controller.(*OrganizationsController).UpdateOrganization`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func15`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func16`\n\n---\n\n","operationId":"PUT_/api/v1/organizations","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/UpdateOrganizationRequest"}}},"description":"Request body for types.UpdateOrganizationRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 organization","tags":["api/v1/organizations"]}},"/api/v1/organizations/all":{"get":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/organization/controller.(*OrganizationsController).GetOrganizations`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func15`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func16`\n\n---\n\n","operationId":"GET_/api/v1/organizations/all","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 organizations","tags":["api/v1/organizations"]}},"/api/v1/organizations/invite/resend":{"post":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/organization/controller.(*OrganizationsController).ResendInvite`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func15`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func16`\n\n---\n\n","operationId":"POST_/api/v1/organizations/invite/resend","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/InviteResendRequest"}}},"description":"Request body for types.InviteResendRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"resend invite","tags":["api/v1/organizations"]}},"/api/v1/organizations/invite/send":{"post":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/organization/controller.(*OrganizationsController).SendInvite`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func15`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func16`\n\n---\n\n","operationId":"POST_/api/v1/organizations/invite/send","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/InviteSendRequest"}}},"description":"Request body for types.InviteSendRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"send invite","tags":["api/v1/organizations"]}},"/api/v1/organizations/remove-user":{"post":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/organization/controller.(*OrganizationsController).RemoveUserFromOrganization`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func15`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func16`\n\n---\n\n","operationId":"POST_/api/v1/organizations/remove-user","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/RemoveUserFromOrganizationRequest"}}},"description":"Request body for types.RemoveUserFromOrganizationRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"remove user from organization","tags":["api/v1/organizations"]}},"/api/v1/organizations/update-user-role":{"post":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/organization/controller.(*OrganizationsController).UpdateUserRole`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func15`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func16`\n\n---\n\n","operationId":"POST_/api/v1/organizations/update-user-role","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/UpdateUserRoleRequest"}}},"description":"Request body for types.UpdateUserRoleRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 user role","tags":["api/v1/organizations"]}},"/api/v1/organizations/users":{"get":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/organization/controller.(*OrganizationsController).GetOrganizationUsers`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func15`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func16`\n\n---\n\n","operationId":"GET_/api/v1/organizations/users","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/GetOrganizationUsersRequest"}}},"description":"Request body for controller.GetOrganizationUsersRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 organization users","tags":["api/v1/organizations"]}},"/api/v1/update":{"post":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/update/controller.(*UpdateController).PerformUpdate`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n\n---\n\n","operationId":"POST_/api/v1/update","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/UpdateRequest"}}},"description":"Request body for types.UpdateRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateResponse"}},"application/xml":{"schema":{"$ref":"#/components/schemas/UpdateResponse"}}},"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":"perform update","tags":["api/v1/update"]}},"/api/v1/update/check":{"get":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/update/controller.(*UpdateController).CheckForUpdates`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n\n---\n\n","operationId":"GET_/api/v1/update/check","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCheckResponse"}},"application/xml":{"schema":{"$ref":"#/components/schemas/UpdateCheckResponse"}}},"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":"check for updates","tags":["api/v1/update"]}},"/api/v1/user":{"get":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/user/controller.(*UserController).GetUserDetails`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func2`\n\n---\n\n","operationId":"GET_/api/v1/user","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 user details","tags":["api/v1/user"]}},"/api/v1/user/avatar":{"patch":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/user/controller.(*UserController).UpdateAvatar`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func2`\n\n---\n\n","operationId":"PATCH_/api/v1/user/avatar","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/UpdateAvatarRequest"}}},"description":"Request body for types.UpdateAvatarRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 avatar","tags":["api/v1/user"]}},"/api/v1/user/name":{"patch":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/user/controller.(*UserController).UpdateUserName`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func2`\n\n---\n\n","operationId":"PATCH_/api/v1/user/name","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/UpdateUserNameRequest"}}},"description":"Request body for types.UpdateUserNameRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 user name","tags":["api/v1/user"]}},"/api/v1/user/organizations":{"get":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/user/controller.(*UserController).GetUserOrganizations`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func2`\n\n---\n\n","operationId":"GET_/api/v1/user/organizations","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 user organizations","tags":["api/v1/user"]}},"/api/v1/user/settings":{"get":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/user/controller.(*UserController).GetSettings`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func2`\n\n---\n\n","operationId":"GET_/api/v1/user/settings","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 settings","tags":["api/v1/user"]}},"/api/v1/user/settings/auto-update":{"patch":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/user/controller.(*UserController).UpdateAutoUpdate`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func2`\n\n---\n\n","operationId":"PATCH_/api/v1/user/settings/auto-update","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/UpdateAutoUpdateRequest"}}},"description":"Request body for controller.UpdateAutoUpdateRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 auto update","tags":["api/v1/user"]}},"/api/v1/user/settings/font":{"patch":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/user/controller.(*UserController).UpdateFont`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func2`\n\n---\n\n","operationId":"PATCH_/api/v1/user/settings/font","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/UpdateFontRequest"}}},"description":"Request body for controller.UpdateFontRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 font","tags":["api/v1/user"]}},"/api/v1/user/settings/language":{"patch":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/user/controller.(*UserController).UpdateLanguage`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func2`\n\n---\n\n","operationId":"PATCH_/api/v1/user/settings/language","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/UpdateLanguageRequest"}}},"description":"Request body for controller.UpdateLanguageRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 language","tags":["api/v1/user"]}},"/api/v1/user/settings/theme":{"patch":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/user/controller.(*UserController).UpdateTheme`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func2`\n\n---\n\n","operationId":"PATCH_/api/v1/user/settings/theme","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/UpdateThemeRequest"}}},"description":"Request body for controller.UpdateThemeRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 theme","tags":["api/v1/user"]}},"/api/v1/webhook":{"post":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/deploy/controller.(*DeployController).HandleGithubWebhook`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n\n---\n\n","operationId":"POST_/api/v1/webhook","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"handle github webhook","tags":["api/v1/webhook"]}},"/ws":{"get":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal.(*Router).WebSocketServer.func1`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n\n---\n\n","operationId":"GET_/ws","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/unknown-interface"}},"application/xml":{"schema":{"$ref":"#/components/schemas/unknown-interface"}}},"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":"func1"}}},"servers":[{"description":"local server","url":"http://:8080"}],"tags":[{"name":"api/v1/audit"},{"name":"api/v1/auth"},{"name":"api/v1/container"},{"name":"api/v1/deploy"},{"name":"api/v1/domain"},{"name":"api/v1/domains"},{"name":"api/v1/feature-flags"},{"name":"api/v1/file-manager"},{"name":"api/v1/github-connector"},{"name":"api/v1/health"},{"name":"api/v1/notification"},{"name":"api/v1/organizations"},{"name":"api/v1/update"},{"name":"api/v1/user"},{"name":"api/v1/webhook"},{"name":"application"},{"name":"preferences"},{"name":"smtp"},{"name":"versions"},{"name":"webhook"}]} \ No newline at end of file +{"components":{"schemas":{"":{"description":" schema","properties":{"status":{"type":"string"}},"type":"object"},"ContainerLogsRequest":{"description":"ContainerLogsRequest schema","properties":{"follow":{"type":"boolean"},"id":{"type":"string"},"since":{"type":"string"},"stderr":{"type":"boolean"},"stdout":{"type":"boolean"},"tail":{"type":"integer"},"until":{"type":"string"}},"type":"object"},"CopyDirectory":{"description":"CopyDirectory schema","properties":{"from_path":{"type":"string"},"to_path":{"type":"string"}},"type":"object"},"CreateDeploymentRequest":{"description":"CreateDeploymentRequest schema","properties":{"base_path":{"nullable":true,"type":"string"},"branch":{"type":"string"},"build_pack":{"type":"string"},"build_variables":{"additionalProperties":{"type":"string"},"type":"object"},"dockerfile_path":{"nullable":true,"type":"string"},"domain":{"type":"string"},"environment":{"type":"string"},"environment_variables":{"additionalProperties":{"type":"string"},"type":"object"},"name":{"type":"string"},"port":{"type":"integer"},"post_run_command":{"type":"string"},"pre_run_command":{"type":"string"},"repository":{"type":"string"}},"type":"object"},"CreateDirectoryRequest":{"description":"CreateDirectoryRequest schema","properties":{"path":{"type":"string"}},"type":"object"},"CreateDomainRequest":{"description":"CreateDomainRequest schema","properties":{"name":{"type":"string"},"organization_id":{}},"type":"object"},"CreateGithubConnectorRequest":{"description":"CreateGithubConnectorRequest schema","properties":{"app_id":{"type":"string"},"client_id":{"type":"string"},"client_secret":{"type":"string"},"pem":{"type":"string"},"slug":{"type":"string"},"webhook_secret":{"type":"string"}},"type":"object"},"CreateOrganizationRequest":{"description":"CreateOrganizationRequest schema","properties":{"description":{"type":"string"},"name":{"type":"string"}},"type":"object"},"CreateSMTPConfigRequest":{"description":"CreateSMTPConfigRequest schema","properties":{"from_email":{"type":"string"},"from_name":{"type":"string"},"host":{"type":"string"},"organization_id":{},"password":{"type":"string"},"port":{"type":"integer"},"username":{"type":"string"}},"type":"object"},"CreateWebhookConfigRequest":{"description":"CreateWebhookConfigRequest schema","properties":{"type":{"type":"string"},"webhook_url":{"type":"string"}},"required":["type"],"type":"object"},"DeleteDeploymentRequest":{"description":"DeleteDeploymentRequest schema","properties":{"id":{}},"type":"object"},"DeleteDirectoryRequest":{"description":"DeleteDirectoryRequest schema","properties":{"path":{"type":"string"}},"type":"object"},"DeleteDomainRequest":{"description":"DeleteDomainRequest schema","properties":{"id":{"type":"string"}},"type":"object"},"DeleteOrganizationRequest":{"description":"DeleteOrganizationRequest schema","properties":{"id":{"type":"string"}},"type":"object"},"DeleteSMTPConfigRequest":{"description":"DeleteSMTPConfigRequest schema","properties":{"id":{}},"type":"object"},"DeleteWebhookConfigRequest":{"description":"DeleteWebhookConfigRequest schema","properties":{"type":{"type":"string"}},"required":["type"],"type":"object"},"Extension":{"nullable":true,"properties":{"author":{"type":"string"},"category":{"type":"string"},"content_hash":{"type":"string"},"created_at":{"format":"date-time","type":"string"},"deleted_at":{"format":"date-time","nullable":true,"type":"string"},"description":{"type":"string"},"extension_id":{"type":"string"},"extension_type":{"type":"string"},"icon":{"type":"string"},"id":{},"is_verified":{"type":"boolean"},"name":{"type":"string"},"parent_extension_id":{"nullable":true},"parsed_content":{"type":"string"},"updated_at":{"format":"date-time","type":"string"},"validation_errors":{"type":"string"},"validation_status":{"type":"string"},"variables":{"items":{"properties":{"created_at":{"format":"date-time","type":"string"},"default_value":{},"description":{"type":"string"},"extension":{"$ref":"#/components/schemas/Extension"},"extension_id":{},"id":{},"is_required":{"type":"boolean"},"validation_pattern":{"type":"string"},"variable_name":{"type":"string"},"variable_type":{"type":"string"}},"type":"object"},"type":"array"},"version":{"type":"string"},"yaml_content":{"type":"string"}},"type":"object"},"ExtensionCategory":{"description":"ExtensionCategory schema","type":"string"},"ExtensionExecution":{"description":"ExtensionExecution schema","properties":{"completed_at":{"format":"date-time","nullable":true,"type":"string"},"created_at":{"format":"date-time","type":"string"},"error_message":{"type":"string"},"execution_log":{"type":"string"},"exit_code":{"type":"integer"},"extension":{"nullable":true,"properties":{"author":{"type":"string"},"category":{"type":"string"},"content_hash":{"type":"string"},"created_at":{"format":"date-time","type":"string"},"deleted_at":{"format":"date-time","nullable":true,"type":"string"},"description":{"type":"string"},"extension_id":{"type":"string"},"extension_type":{"type":"string"},"icon":{"type":"string"},"id":{},"is_verified":{"type":"boolean"},"name":{"type":"string"},"parent_extension_id":{"nullable":true},"parsed_content":{"type":"string"},"updated_at":{"format":"date-time","type":"string"},"validation_errors":{"type":"string"},"validation_status":{"type":"string"},"variables":{"items":{"properties":{"created_at":{"format":"date-time","type":"string"},"default_value":{},"description":{"type":"string"},"extension":{"$ref":"#/components/schemas/Extension"},"extension_id":{},"id":{},"is_required":{"type":"boolean"},"validation_pattern":{"type":"string"},"variable_name":{"type":"string"},"variable_type":{"type":"string"}},"type":"object"},"type":"array"},"version":{"type":"string"},"yaml_content":{"type":"string"}},"type":"object"},"extension_id":{},"id":{},"log_seq":{"format":"int64","type":"integer"},"server_hostname":{"type":"string"},"started_at":{"format":"date-time","type":"string"},"status":{"type":"string"},"steps":{"items":{"properties":{"completed_at":{"format":"date-time","nullable":true,"type":"string"},"created_at":{"format":"date-time","type":"string"},"execution":{"$ref":"#/components/schemas/ExtensionExecution"},"execution_id":{},"exit_code":{"type":"integer"},"id":{},"output":{"type":"string"},"phase":{"type":"string"},"started_at":{"format":"date-time","type":"string"},"status":{"type":"string"},"step_name":{"type":"string"},"step_order":{"type":"integer"}},"type":"object"},"nullable":true,"type":"array"},"variable_values":{"type":"string"}},"type":"object"},"ExtensionListResponse":{"description":"ExtensionListResponse schema","properties":{"extensions":{"items":{"properties":{"author":{"type":"string"},"category":{"type":"string"},"content_hash":{"type":"string"},"created_at":{"format":"date-time","type":"string"},"deleted_at":{"format":"date-time","nullable":true,"type":"string"},"description":{"type":"string"},"extension_id":{"type":"string"},"extension_type":{"type":"string"},"icon":{"type":"string"},"id":{},"is_verified":{"type":"boolean"},"name":{"type":"string"},"parent_extension_id":{"nullable":true},"parsed_content":{"type":"string"},"updated_at":{"format":"date-time","type":"string"},"validation_errors":{"type":"string"},"validation_status":{"type":"string"},"variables":{"items":{"properties":{"created_at":{"format":"date-time","type":"string"},"default_value":{},"description":{"type":"string"},"extension":{"$ref":"#/components/schemas/Extension"},"extension_id":{},"id":{},"is_required":{"type":"boolean"},"validation_pattern":{"type":"string"},"variable_name":{"type":"string"},"variable_type":{"type":"string"}},"type":"object"},"type":"array"},"version":{"type":"string"},"yaml_content":{"type":"string"}},"type":"object"},"type":"array"},"page":{"type":"integer"},"page_size":{"type":"integer"},"total":{"type":"integer"},"total_pages":{"type":"integer"}},"type":"object"},"ForkExtensionRequest":{"description":"ForkExtensionRequest schema","properties":{"yaml_content":{"nullable":true,"type":"string"}},"type":"object"},"GetApplicationDeploymentsRequest":{"description":"GetApplicationDeploymentsRequest schema","properties":{"id":{"type":"string"},"limit":{"type":"string"},"page":{"type":"string"}},"type":"object"},"GetApplicationsRequest":{"description":"GetApplicationsRequest schema","properties":{"page":{"type":"string"},"page_size":{"type":"string"},"repository":{"type":"string"}},"type":"object"},"GetGithubRepositoryBranchesRequest":{"description":"GetGithubRepositoryBranchesRequest schema","properties":{"repository_name":{"type":"string"}},"required":["repository_name"],"type":"object"},"GetOrganizationUsersRequest":{"description":"GetOrganizationUsersRequest schema","properties":{"id":{"type":"string"}},"type":"object"},"HTTPError":{"description":"HTTPError schema","properties":{"detail":{"description":"Human readable error message","nullable":true,"type":"string"},"errors":{"items":{"properties":{"more":{"additionalProperties":{},"type":"object"},"name":{"type":"string"},"reason":{"type":"string"}},"type":"object"},"nullable":true,"type":"array"},"instance":{"nullable":true,"type":"string"},"status":{"description":"HTTP status code","example":403,"nullable":true,"type":"integer"},"title":{"description":"Short title of the error","nullable":true,"type":"string"},"type":{"description":"URL of the error type. Can be used to lookup the error in a documentation","nullable":true,"type":"string"}},"type":"object"},"InviteResendRequest":{"description":"InviteResendRequest schema","properties":{"email":{"type":"string"},"organization_id":{"type":"string"},"role":{"type":"string"}},"type":"object"},"InviteSendRequest":{"description":"InviteSendRequest schema","properties":{"email":{"type":"string"},"organization_id":{"type":"string"},"role":{"type":"string"}},"type":"object"},"ListFilesRequest":{"description":"ListFilesRequest schema","properties":{"path":{"type":"string"}},"type":"object"},"ListImagesRequest":{"description":"ListImagesRequest schema","properties":{"all":{"nullable":true,"type":"boolean"},"container_id":{"nullable":true,"type":"string"},"image_prefix":{"nullable":true,"type":"string"}},"type":"object"},"ListLogsResponse":{"description":"ListLogsResponse schema","properties":{"logs":{"items":{"properties":{"created_at":{"format":"date-time","type":"string"},"data":{},"execution_id":{},"id":{},"level":{"type":"string"},"message":{"type":"string"},"sequence":{"format":"int64","type":"integer"},"step_id":{"nullable":true}},"type":"object"},"type":"array"},"next_after":{"format":"int64","type":"integer"}},"type":"object"},"LogoutRequest":{"description":"LogoutRequest schema","properties":{"refresh_token":{"type":"string"}},"type":"object"},"MoveDirectory":{"description":"MoveDirectory schema","properties":{"from_path":{"type":"string"},"to_path":{"type":"string"}},"type":"object"},"PruneBuildCacheRequest":{"description":"PruneBuildCacheRequest schema","properties":{"all":{"nullable":true,"type":"boolean"},"filters":{"nullable":true,"type":"string"}},"type":"object"},"PruneImagesRequest":{"description":"PruneImagesRequest schema","properties":{"dangling":{"nullable":true,"type":"boolean"},"label":{"nullable":true,"type":"string"},"until":{"nullable":true,"type":"string"}},"type":"object"},"ReDeployApplicationRequest":{"description":"ReDeployApplicationRequest schema","properties":{"force":{"type":"boolean"},"force_without_cache":{"type":"boolean"},"id":{}},"type":"object"},"RegisterRequest":{"description":"RegisterRequest schema","properties":{"email":{"type":"string"},"organization":{"type":"string"},"password":{"type":"string"},"type":{"type":"string"},"username":{"type":"string"}},"type":"object"},"RemoveUserFromOrganizationRequest":{"description":"RemoveUserFromOrganizationRequest schema","properties":{"organization_id":{"type":"string"},"user_id":{"type":"string"}},"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":{"id":{}},"type":"object"},"RollbackDeploymentRequest":{"description":"RollbackDeploymentRequest schema","properties":{"id":{}},"type":"object"},"RunExtensionRequest":{"description":"RunExtensionRequest schema","properties":{"variables":{"additionalProperties":{},"type":"object"}},"type":"object"},"TwoFactorLoginRequest":{"description":"TwoFactorLoginRequest schema","properties":{"code":{"type":"string"},"email":{"type":"string"},"password":{"type":"string"}},"type":"object"},"TwoFactorVerifyRequest":{"description":"TwoFactorVerifyRequest schema","properties":{"code":{"type":"string"}},"type":"object"},"UpdateAutoUpdateRequest":{"description":"UpdateAutoUpdateRequest schema","properties":{"auto_update":{"type":"boolean"}},"type":"object"},"UpdateAvatarRequest":{"description":"UpdateAvatarRequest schema","properties":{"avatarData":{"type":"string"}},"type":"object"},"UpdateCheckResponse":{"description":"UpdateCheckResponse schema","properties":{"current_version":{"type":"string"},"environment":{"type":"string"},"last_checked":{"format":"date-time","type":"string"},"latest_version":{"type":"string"},"update_available":{"type":"boolean"}},"type":"object"},"UpdateDeploymentRequest":{"description":"UpdateDeploymentRequest schema","properties":{"base_path":{"nullable":true,"type":"string"},"build_variables":{"additionalProperties":{"type":"string"},"nullable":true,"type":"object"},"dockerfile_path":{"nullable":true,"type":"string"},"environment_variables":{"additionalProperties":{"type":"string"},"nullable":true,"type":"object"},"force":{"nullable":true,"type":"boolean"},"id":{"nullable":true},"name":{"nullable":true,"type":"string"},"port":{"nullable":true,"type":"integer"},"post_run_command":{"nullable":true,"type":"string"},"pre_run_command":{"nullable":true,"type":"string"}},"type":"object"},"UpdateDomainRequest":{"description":"UpdateDomainRequest schema","properties":{"id":{"type":"string"},"name":{"type":"string"}},"type":"object"},"UpdateFeatureFlagRequest":{"description":"UpdateFeatureFlagRequest schema","properties":{"feature_name":{"type":"string"},"is_enabled":{"type":"boolean"}},"required":["feature_name"],"type":"object"},"UpdateFontRequest":{"description":"UpdateFontRequest schema","properties":{"font_family":{"type":"string"},"font_size":{"type":"integer"}},"type":"object"},"UpdateGithubConnectorRequest":{"description":"UpdateGithubConnectorRequest schema","properties":{"installation_id":{"type":"string"}},"type":"object"},"UpdateLanguageRequest":{"description":"UpdateLanguageRequest schema","properties":{"language":{"type":"string"}},"type":"object"},"UpdateOrganizationRequest":{"description":"UpdateOrganizationRequest schema","properties":{"description":{"type":"string"},"id":{"type":"string"},"name":{"type":"string"}},"type":"object"},"UpdatePreferenceRequest":{"description":"UpdatePreferenceRequest schema","properties":{"category":{"type":"string"},"enabled":{"type":"boolean"},"type":{"type":"string"}},"required":["category","type"],"type":"object"},"UpdateRequest":{"description":"UpdateRequest schema","properties":{"force":{"type":"boolean"}},"type":"object"},"UpdateResponse":{"description":"UpdateResponse schema","properties":{"message":{"type":"string"},"success":{"type":"boolean"}},"type":"object"},"UpdateSMTPConfigRequest":{"description":"UpdateSMTPConfigRequest schema","properties":{"from_email":{"nullable":true,"type":"string"},"from_name":{"nullable":true,"type":"string"},"host":{"nullable":true,"type":"string"},"id":{},"organization_id":{},"password":{"nullable":true,"type":"string"},"port":{"nullable":true,"type":"integer"},"username":{"nullable":true,"type":"string"}},"type":"object"},"UpdateThemeRequest":{"description":"UpdateThemeRequest schema","properties":{"theme":{"type":"string"}},"type":"object"},"UpdateUserNameRequest":{"description":"UpdateUserNameRequest schema","properties":{"name":{"type":"string"}},"type":"object"},"UpdateUserRoleRequest":{"description":"UpdateUserRoleRequest schema","properties":{"organization_id":{"type":"string"},"role":{"type":"string"},"user_id":{"type":"string"}},"type":"object"},"UpdateWebhookConfigRequest":{"description":"UpdateWebhookConfigRequest schema","properties":{"is_active":{"nullable":true,"type":"boolean"},"type":{"type":"string"},"webhook_url":{"nullable":true,"type":"string"}},"required":["type"],"type":"object"},"unknown-interface":{"description":"unknown-interface schema"}},"securitySchemes":{"bearerAuth":{"bearerFormat":"JWT","description":"Enter your JWT token in the format: Bearer \u003ctoken\u003e","scheme":"bearer","type":"http"}}},"info":{"description":"\nThis is the autogenerated OpenAPI documentation for your [Fuego](https://github.com/go-fuego/fuego) API.\n\nBelow is a Fuego Cheatsheet to help you get started. Don't hesitate to check the [Fuego documentation](https://go-fuego.dev) for more details.\n\nHappy coding! 🔥\n\n## Usage\n\n### Route registration\n\n```go\nfunc main() {\n\t// Create a new server\n\ts := fuego.NewServer()\n\n\t// Register some routes\n\tfuego.Post(s, \"/hello\", myController)\n\tfuego.Get(s, \"/myPath\", otherController)\n\tfuego.Put(s, \"/hello\", thirdController)\n\n\tadminRoutes := fuego.Group(s, \"/admin\")\n\tfuego.Use(adminRoutes, myMiddleware) // This middleware (for authentication, etc...) will be available for routes starting by /admin/*, \n\tfuego.Get(adminRoutes, \"/hello\", groupController) // This route will be available at /admin/hello\n\n\t// Start the server\n\ts.Start()\n}\n```\n\n### Basic controller\n\n```go\ntype MyBody struct {\n\tName string `json:\"name\" validate:\"required,max=30\"`\n}\n\ntype MyResponse struct {\n\tAnswer string `json:\"answer\"`\n}\n\nfunc hello(ctx fuego.ContextWithBody[MyBody]) (*MyResponse, error) {\n\tbody, err := ctx.Body()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn \u0026MyResponse{Answer: \"Hello \" + body.Name}, nil\n}\n```\n\n### Add openAPI information to the route\n\n```go\nimport (\n\t\"github.com/go-fuego/fuego\"\n\t\"github.com/go-fuego/fuego/option\"\n\t\"github.com/go-fuego/fuego/param\"\n)\n\nfunc main() {\n\ts := fuego.NewServer()\n\n\t// Custom OpenAPI options\n\tfuego.Post(s, \"/\", myController\n\t\toption.Description(\"This route does something...\"),\n\t\toption.Summary(\"This is my summary\"),\n\t\toption.Tags(\"MyTag\"), // A tag is set by default according to the return type (can be deactivated)\n\t\toption.Deprecated(), // Marks the route as deprecated in the OpenAPI spec\n\n\t\toption.Query(\"name\", \"Declares a query parameter with default value\", param.Default(\"Carmack\")),\n\t\toption.Header(\"Authorization\", \"Bearer token\", param.Required()),\n\t\toptionPagination,\n\t\toptionCustomBehavior,\n\t)\n\n\ts.Run()\n}\n\nvar optionPagination = option.Group(\n\toption.QueryInt(\"page\", \"Page number\", param.Default(1), param.Example(\"1st page\", 1), param.Example(\"42nd page\", 42)),\n\toption.QueryInt(\"perPage\", \"Number of items per page\"),\n)\n\nvar optionCustomBehavior = func(r *fuego.BaseRoute) {\n\tr.XXX = \"YYY\"\n}\n```\n\nThen, in the controller\n\n```go\ntype MyResponse struct {\n\tAnswer string `json:\"answer\"`\n}\n\nfunc getAllPets(ctx fuego.ContextNoBody) (*MyResponse, error) {\n\tname := ctx.QueryParam(\"name\")\n\tperPage, _ := ctx.QueryParamIntErr(\"per_page\")\n\n\treturn \u0026MyResponse{Answer: \"Hello \" + name}, nil\n}\n```\n","title":"OpenAPI","version":"0.0.1"},"openapi":"3.1.0","paths":{"/api/v1/audit/logs":{"get":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/audit/controller.(*AuditController).GetRecentAuditLogs`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func23`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func24`\n\n---\n\n","operationId":"GET_/api/v1/audit/logs","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 recent audit logs","tags":["api/v1/audit"]}},"/api/v1/auth/2fa-login":{"post":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/auth/controller.(*AuthController).TwoFactorLogin`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n\n---\n\n","operationId":"POST_/api/v1/auth/2fa-login","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/TwoFactorLoginRequest"}}},"description":"Request body for types.TwoFactorLoginRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"two factor login","tags":["api/v1/auth"]}},"/api/v1/auth/create-user":{"post":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/auth/controller.(*AuthController).CreateUser`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n\n---\n\n","operationId":"POST_/api/v1/auth/create-user","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/RegisterRequest"}}},"description":"Request body for types.RegisterRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"create user","tags":["api/v1/auth"]}},"/api/v1/auth/disable-2fa":{"post":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/auth/controller.(*AuthController).DisableTwoFactor`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n\n---\n\n","operationId":"POST_/api/v1/auth/disable-2fa","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"disable two factor","tags":["api/v1/auth"]}},"/api/v1/auth/is-admin-registered":{"get":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/auth/controller.(*AuthController).IsAdminRegistered`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n\n---\n\n","operationId":"GET_/api/v1/auth/is-admin-registered","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"is admin registered","tags":["api/v1/auth"]}},"/api/v1/auth/logout":{"post":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/auth/controller.(*AuthController).Logout`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n\n---\n\n","operationId":"POST_/api/v1/auth/logout","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/LogoutRequest"}}},"description":"Request body for types.LogoutRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"logout","tags":["api/v1/auth"]}},"/api/v1/auth/send-verification-email":{"post":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/auth/controller.(*AuthController).SendVerificationEmail`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n\n---\n\n","operationId":"POST_/api/v1/auth/send-verification-email","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"send verification email","tags":["api/v1/auth"]}},"/api/v1/auth/setup-2fa":{"post":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/auth/controller.(*AuthController).SetupTwoFactor`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n\n---\n\n","operationId":"POST_/api/v1/auth/setup-2fa","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"setup two factor","tags":["api/v1/auth"]}},"/api/v1/auth/verify-2fa":{"post":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/auth/controller.(*AuthController).VerifyTwoFactor`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n\n---\n\n","operationId":"POST_/api/v1/auth/verify-2fa","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/TwoFactorVerifyRequest"}}},"description":"Request body for types.TwoFactorVerifyRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"verify two factor","tags":["api/v1/auth"]}},"/api/v1/auth/verify-email":{"get":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/auth/controller.(*AuthController).VerifyEmail`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n\n---\n\n","operationId":"GET_/api/v1/auth/verify-email","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"verify email","tags":["api/v1/auth"]}},"/api/v1/container":{"get":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/container/controller.(*ContainerController).ListContainers`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func26`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func27`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func28`\n\n---\n\n","operationId":"GET_/api/v1/container","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"list containers","tags":["api/v1/container"]}},"/api/v1/container/images":{"post":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/container/controller.(*ContainerController).ListImages`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func26`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func27`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func28`\n\n---\n\n","operationId":"POST_/api/v1/container/images","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/ListImagesRequest"}}},"description":"Request body for controller.ListImagesRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"list images","tags":["api/v1/container"]}},"/api/v1/container/prune/build-cache":{"post":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/container/controller.(*ContainerController).PruneBuildCache`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func26`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func27`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func28`\n\n---\n\n","operationId":"POST_/api/v1/container/prune/build-cache","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/PruneBuildCacheRequest"}}},"description":"Request body for controller.PruneBuildCacheRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"prune build cache","tags":["api/v1/container"]}},"/api/v1/container/prune/images":{"post":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/container/controller.(*ContainerController).PruneImages`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func26`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func27`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func28`\n\n---\n\n","operationId":"POST_/api/v1/container/prune/images","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/PruneImagesRequest"}}},"description":"Request body for controller.PruneImagesRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"prune images","tags":["api/v1/container"]}},"/api/v1/container/{container_id}":{"delete":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/container/controller.(*ContainerController).RemoveContainer`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func26`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func27`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func28`\n\n---\n\n","operationId":"DELETE_/api/v1/container/:container_id","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}},{"in":"path","name":"container_id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"remove container","tags":["api/v1/container"]},"get":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/container/controller.(*ContainerController).GetContainer`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func26`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func27`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func28`\n\n---\n\n","operationId":"GET_/api/v1/container/:container_id","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}},{"in":"path","name":"container_id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 container","tags":["api/v1/container"]}},"/api/v1/container/{container_id}/logs":{"post":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/container/controller.(*ContainerController).GetContainerLogs`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func26`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func27`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func28`\n\n---\n\n","operationId":"POST_/api/v1/container/:container_id/logs","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}},{"in":"path","name":"container_id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/ContainerLogsRequest"}}},"description":"Request body for types.ContainerLogsRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 container logs","tags":["api/v1/container"]}},"/api/v1/container/{container_id}/restart":{"post":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/container/controller.(*ContainerController).RestartContainer`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func26`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func27`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func28`\n\n---\n\n","operationId":"POST_/api/v1/container/:container_id/restart","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}},{"in":"path","name":"container_id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"restart container","tags":["api/v1/container"]}},"/api/v1/container/{container_id}/start":{"post":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/container/controller.(*ContainerController).StartContainer`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func26`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func27`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func28`\n\n---\n\n","operationId":"POST_/api/v1/container/:container_id/start","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}},{"in":"path","name":"container_id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"start container","tags":["api/v1/container"]}},"/api/v1/container/{container_id}/stop":{"post":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/container/controller.(*ContainerController).StopContainer`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func26`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func27`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func28`\n\n---\n\n","operationId":"POST_/api/v1/container/:container_id/stop","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}},{"in":"path","name":"container_id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"stop container","tags":["api/v1/container"]}},"/api/v1/deploy/application":{"delete":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/deploy/controller.(*DeployController).DeleteApplication`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func20`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func21`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func22`\n\n---\n\n","operationId":"DELETE_/api/v1/deploy/application","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/DeleteDeploymentRequest"}}},"description":"Request body for types.DeleteDeploymentRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 application","tags":["api/v1/deploy","application"]},"get":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/deploy/controller.(*DeployController).GetApplicationById`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func20`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func21`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func22`\n\n---\n\n","operationId":"GET_/api/v1/deploy/application","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 application by id","tags":["api/v1/deploy","application"]},"post":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/deploy/controller.(*DeployController).HandleDeploy`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func20`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func21`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func22`\n\n---\n\n","operationId":"POST_/api/v1/deploy/application","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/CreateDeploymentRequest"}}},"description":"Request body for types.CreateDeploymentRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"handle deploy","tags":["api/v1/deploy","application"]},"put":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/deploy/controller.(*DeployController).UpdateApplication`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func20`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func21`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func22`\n\n---\n\n","operationId":"PUT_/api/v1/deploy/application","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/UpdateDeploymentRequest"}}},"description":"Request body for types.UpdateDeploymentRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 application","tags":["api/v1/deploy","application"]}},"/api/v1/deploy/application/deployments":{"get":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/deploy/controller.(*DeployController).GetApplicationDeployments`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func20`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func21`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func22`\n\n---\n\n","operationId":"GET_/api/v1/deploy/application/deployments","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/GetApplicationDeploymentsRequest"}}},"description":"Request body for controller.GetApplicationDeploymentsRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 application deployments","tags":["api/v1/deploy","application"]}},"/api/v1/deploy/application/deployments/{deployment_id}":{"get":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/deploy/controller.(*DeployController).GetDeploymentById`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func20`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func21`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func22`\n\n---\n\n","operationId":"GET_/api/v1/deploy/application/deployments/:deployment_id","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}},{"in":"path","name":"deployment_id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 deployment by id","tags":["api/v1/deploy","application"]}},"/api/v1/deploy/application/deployments/{deployment_id}/logs":{"get":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/deploy/controller.(*DeployController).GetDeploymentLogs`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func20`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func21`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func22`\n\n---\n\n","operationId":"GET_/api/v1/deploy/application/deployments/:deployment_id/logs","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}},{"in":"path","name":"deployment_id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 deployment logs","tags":["api/v1/deploy","application"]}},"/api/v1/deploy/application/logs/{application_id}":{"get":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/deploy/controller.(*DeployController).GetLogs`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func20`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func21`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func22`\n\n---\n\n","operationId":"GET_/api/v1/deploy/application/logs/:application_id","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}},{"in":"path","name":"application_id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 logs","tags":["api/v1/deploy","application"]}},"/api/v1/deploy/application/redeploy":{"post":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/deploy/controller.(*DeployController).ReDeployApplication`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func20`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func21`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func22`\n\n---\n\n","operationId":"POST_/api/v1/deploy/application/redeploy","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/ReDeployApplicationRequest"}}},"description":"Request body for types.ReDeployApplicationRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"re deploy application","tags":["api/v1/deploy","application"]}},"/api/v1/deploy/application/restart":{"post":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/deploy/controller.(*DeployController).HandleRestart`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func20`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func21`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func22`\n\n---\n\n","operationId":"POST_/api/v1/deploy/application/restart","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/RestartDeploymentRequest"}}},"description":"Request body for types.RestartDeploymentRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"handle restart","tags":["api/v1/deploy","application"]}},"/api/v1/deploy/application/rollback":{"post":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/deploy/controller.(*DeployController).HandleRollback`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func20`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func21`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func22`\n\n---\n\n","operationId":"POST_/api/v1/deploy/application/rollback","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/RollbackDeploymentRequest"}}},"description":"Request body for types.RollbackDeploymentRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"handle rollback","tags":["api/v1/deploy","application"]}},"/api/v1/deploy/applications":{"get":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/deploy/controller.(*DeployController).GetApplications`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func20`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func21`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func22`\n\n---\n\n","operationId":"GET_/api/v1/deploy/applications","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/GetApplicationsRequest"}}},"description":"Request body for controller.GetApplicationsRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 applications","tags":["api/v1/deploy"]}},"/api/v1/domain":{"delete":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/domain/controller.(*DomainsController).DeleteDomain`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func3`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func5`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func7`\n\n---\n\n","operationId":"DELETE_/api/v1/domain","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/DeleteDomainRequest"}}},"description":"Request body for types.DeleteDomainRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 domain","tags":["api/v1/domain"]},"post":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/domain/controller.(*DomainsController).CreateDomain`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func3`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func5`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func7`\n\n---\n\n","operationId":"POST_/api/v1/domain","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/CreateDomainRequest"}}},"description":"Request body for types.CreateDomainRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"create domain","tags":["api/v1/domain"]},"put":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/domain/controller.(*DomainsController).UpdateDomain`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func3`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func5`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func7`\n\n---\n\n","operationId":"PUT_/api/v1/domain","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/UpdateDomainRequest"}}},"description":"Request body for types.UpdateDomainRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 domain","tags":["api/v1/domain"]}},"/api/v1/domain/generate":{"get":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/domain/controller.(*DomainsController).GenerateRandomSubDomain`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func3`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func5`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func7`\n\n---\n\n","operationId":"GET_/api/v1/domain/generate","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"generate random sub domain","tags":["api/v1/domain"]}},"/api/v1/domains":{"get":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/domain/controller.(*DomainsController).GetDomains`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func4`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func6`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func8`\n\n---\n\n","operationId":"GET_/api/v1/domains","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 domains","tags":["api/v1/domains"]}},"/api/v1/extensions":{"get":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/extension/controller.(*ExtensionsController).GetExtensions`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func29`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func30`\n\n---\n\n","operationId":"GET_/api/v1/extensions","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExtensionListResponse"}},"application/xml":{"schema":{"$ref":"#/components/schemas/ExtensionListResponse"}}},"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 extensions","tags":["api/v1/extensions"]}},"/api/v1/extensions/by-extension-id/{extension_id}":{"get":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/extension/controller.(*ExtensionsController).GetExtensionByExtensionID`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func29`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func30`\n\n---\n\n","operationId":"GET_/api/v1/extensions/by-extension-id/:extension_id","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}},{"in":"path","name":"extension_id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Extension"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Extension"}}},"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 extension by extension i d","tags":["api/v1/extensions"]}},"/api/v1/extensions/by-extension-id/{extension_id}/executions":{"get":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/extension/controller.(*ExtensionsController).ListExecutionsByExtensionID`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func29`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func30`\n\n---\n\n","operationId":"GET_/api/v1/extensions/by-extension-id/:extension_id/executions","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}},{"in":"path","name":"extension_id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/ExtensionExecution"},"type":"array"}},"application/xml":{"schema":{"items":{"$ref":"#/components/schemas/ExtensionExecution"},"type":"array"}}},"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":"list executions by extension i d","tags":["api/v1/extensions"]}},"/api/v1/extensions/categories":{"get":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/extension/controller.(*ExtensionsController).GetCategories`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func29`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func30`\n\n---\n\n","operationId":"GET_/api/v1/extensions/categories","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/ExtensionCategory"},"type":"array"}},"application/xml":{"schema":{"items":{"$ref":"#/components/schemas/ExtensionCategory"},"type":"array"}}},"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 categories","tags":["api/v1/extensions"]}},"/api/v1/extensions/execution/{execution_id}":{"get":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/extension/controller.(*ExtensionsController).GetExecution`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func29`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func30`\n\n---\n\n","operationId":"GET_/api/v1/extensions/execution/:execution_id","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}},{"in":"path","name":"execution_id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExtensionExecution"}},"application/xml":{"schema":{"$ref":"#/components/schemas/ExtensionExecution"}}},"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 execution","tags":["api/v1/extensions"]}},"/api/v1/extensions/execution/{execution_id}/cancel":{"post":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/extension/controller.(*ExtensionsController).CancelExecution`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func29`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func30`\n\n---\n\n","operationId":"POST_/api/v1/extensions/execution/:execution_id/cancel","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}},{"in":"path","name":"execution_id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"cancel execution","tags":["api/v1/extensions"]}},"/api/v1/extensions/execution/{execution_id}/logs":{"get":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/extension/controller.(*ExtensionsController).ListExecutionLogs`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func29`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func30`\n\n---\n\n","operationId":"GET_/api/v1/extensions/execution/:execution_id/logs","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}},{"in":"path","name":"execution_id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListLogsResponse"}},"application/xml":{"schema":{"$ref":"#/components/schemas/ListLogsResponse"}}},"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":"list execution logs","tags":["api/v1/extensions"]}},"/api/v1/extensions/{extension_id}/fork":{"post":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/extension/controller.(*ExtensionsController).ForkExtension`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func29`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func30`\n\n---\n\n","operationId":"POST_/api/v1/extensions/:extension_id/fork","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}},{"in":"path","name":"extension_id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/ForkExtensionRequest"}}},"description":"Request body for controller.ForkExtensionRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Extension"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Extension"}}},"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":"fork extension","tags":["api/v1/extensions"]}},"/api/v1/extensions/{extension_id}/run":{"post":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/extension/controller.(*ExtensionsController).RunExtension`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func29`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func30`\n\n---\n\n","operationId":"POST_/api/v1/extensions/:extension_id/run","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}},{"in":"path","name":"extension_id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/RunExtensionRequest"}}},"description":"Request body for controller.RunExtensionRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExtensionExecution"}},"application/xml":{"schema":{"$ref":"#/components/schemas/ExtensionExecution"}}},"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":"run extension","tags":["api/v1/extensions"]}},"/api/v1/extensions/{id}":{"delete":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/extension/controller.(*ExtensionsController).DeleteFork`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func29`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func30`\n\n---\n\n","operationId":"DELETE_/api/v1/extensions/:id","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}},{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/"}},"application/xml":{"schema":{"$ref":"#/components/schemas/"}}},"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 fork","tags":["api/v1/extensions"]},"get":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/extension/controller.(*ExtensionsController).GetExtension`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func29`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func30`\n\n---\n\n","operationId":"GET_/api/v1/extensions/:id","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}},{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Extension"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Extension"}}},"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 extension","tags":["api/v1/extensions"]}},"/api/v1/feature-flags":{"get":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/feature-flags/controller.(*FeatureFlagController).GetFeatureFlags`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n\n---\n\n","operationId":"GET_/api/v1/feature-flags","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 feature flags","tags":["api/v1/feature-flags"]},"put":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/feature-flags/controller.(*FeatureFlagController).UpdateFeatureFlag`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func25`\n\n---\n\n","operationId":"PUT_/api/v1/feature-flags","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/UpdateFeatureFlagRequest"}}},"description":"Request body for types.UpdateFeatureFlagRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 feature flag","tags":["api/v1/feature-flags"]}},"/api/v1/feature-flags/check":{"get":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/feature-flags/controller.(*FeatureFlagController).IsFeatureEnabled`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n\n---\n\n","operationId":"GET_/api/v1/feature-flags/check","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"is feature enabled","tags":["api/v1/feature-flags"]}},"/api/v1/file-manager":{"get":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/file-manager/controller.(*FileManagerController).ListFiles`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func17`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func18`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func19`\n\n---\n\n","operationId":"GET_/api/v1/file-manager","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/ListFilesRequest"}}},"description":"Request body for controller.ListFilesRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"list files","tags":["api/v1/file-manager"]}},"/api/v1/file-manager/copy-directory":{"post":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/file-manager/controller.(*FileManagerController).CopyDirectory`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func17`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func18`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func19`\n\n---\n\n","operationId":"POST_/api/v1/file-manager/copy-directory","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/CopyDirectory"}}},"description":"Request body for controller.CopyDirectory","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"copy directory","tags":["api/v1/file-manager"]}},"/api/v1/file-manager/create-directory":{"post":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/file-manager/controller.(*FileManagerController).CreateDirectory`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func17`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func18`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func19`\n\n---\n\n","operationId":"POST_/api/v1/file-manager/create-directory","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/CreateDirectoryRequest"}}},"description":"Request body for controller.CreateDirectoryRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"create directory","tags":["api/v1/file-manager"]}},"/api/v1/file-manager/delete-directory":{"delete":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/file-manager/controller.(*FileManagerController).DeleteDirectory`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func17`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func18`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func19`\n\n---\n\n","operationId":"DELETE_/api/v1/file-manager/delete-directory","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/DeleteDirectoryRequest"}}},"description":"Request body for controller.DeleteDirectoryRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 directory","tags":["api/v1/file-manager"]}},"/api/v1/file-manager/move-directory":{"post":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/file-manager/controller.(*FileManagerController).MoveDirectory`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func17`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func18`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func19`\n\n---\n\n","operationId":"POST_/api/v1/file-manager/move-directory","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/MoveDirectory"}}},"description":"Request body for controller.MoveDirectory","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"move directory","tags":["api/v1/file-manager"]}},"/api/v1/file-manager/upload":{"post":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/file-manager/controller.(*FileManagerController).UploadFile`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func17`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func18`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func19`\n\n---\n\n","operationId":"POST_/api/v1/file-manager/upload","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"upload file","tags":["api/v1/file-manager"]}},"/api/v1/github-connector":{"post":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/github-connector/controller.(*GithubConnectorController).CreateGithubConnector`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func9`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func10`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func11`\n\n---\n\n","operationId":"POST_/api/v1/github-connector","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/CreateGithubConnectorRequest"}}},"description":"Request body for types.CreateGithubConnectorRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"create github connector","tags":["api/v1/github-connector"]},"put":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/github-connector/controller.(*GithubConnectorController).UpdateGithubConnectorRequest`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func9`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func10`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func11`\n\n---\n\n","operationId":"PUT_/api/v1/github-connector","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/UpdateGithubConnectorRequest"}}},"description":"Request body for types.UpdateGithubConnectorRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 github connector request","tags":["api/v1/github-connector"]}},"/api/v1/github-connector/all":{"get":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/github-connector/controller.(*GithubConnectorController).GetGithubConnectors`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func9`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func10`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func11`\n\n---\n\n","operationId":"GET_/api/v1/github-connector/all","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 github connectors","tags":["api/v1/github-connector"]}},"/api/v1/github-connector/repositories":{"get":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/github-connector/controller.(*GithubConnectorController).GetGithubRepositories`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func9`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func10`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func11`\n\n---\n\n","operationId":"GET_/api/v1/github-connector/repositories","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 github repositories","tags":["api/v1/github-connector"]}},"/api/v1/github-connector/repository/branches":{"post":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/github-connector/controller.(*GithubConnectorController).GetGithubRepositoryBranches`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func9`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func10`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func11`\n\n---\n\n","operationId":"POST_/api/v1/github-connector/repository/branches","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/GetGithubRepositoryBranchesRequest"}}},"description":"Request body for controller.GetGithubRepositoryBranchesRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 github repository branches","tags":["api/v1/github-connector"]}},"/api/v1/health":{"get":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/health.HealthCheck`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n\n---\n\n","operationId":"GET_/api/v1/health","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"health check","tags":["api/v1/health"]}},"/api/v1/health/versions":{"get":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal.(*Router).BasicRoutes.func1`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n\n---\n\n","operationId":"GET_/api/v1/health/versions","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/unknown-interface"}},"application/xml":{"schema":{"$ref":"#/components/schemas/unknown-interface"}}},"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":"func1","tags":["api/v1/health","versions"]}},"/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.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func12`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func13`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func14`\n\n---\n\n","operationId":"GET_/api/v1/notification/preferences","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func12`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func13`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func14`\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/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func12`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func13`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func14`\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/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func12`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func13`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func14`\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/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func12`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func13`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func14`\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/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func12`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func13`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func14`\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/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func12`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func13`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func14`\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/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func12`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func13`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func14`\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/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"create webhook config","tags":["api/v1/notification","webhook"]},"put":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/notification/controller.(*NotificationController).UpdateWebhookConfig`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func12`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func13`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func14`\n\n---\n\n","operationId":"PUT_/api/v1/notification/webhook","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/UpdateWebhookConfigRequest"}}},"description":"Request body for notification.UpdateWebhookConfigRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 webhook config","tags":["api/v1/notification","webhook"]}},"/api/v1/notification/webhook/{type}":{"get":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/notification/controller.(*NotificationController).GetWebhookConfig`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func12`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func13`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func14`\n\n---\n\n","operationId":"GET_/api/v1/notification/webhook/:type","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}},{"in":"path","name":"type","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 webhook config","tags":["api/v1/notification","webhook"]}},"/api/v1/organizations":{"delete":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/organization/controller.(*OrganizationsController).DeleteOrganization`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func15`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func16`\n\n---\n\n","operationId":"DELETE_/api/v1/organizations","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/DeleteOrganizationRequest"}}},"description":"Request body for types.DeleteOrganizationRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 organization","tags":["api/v1/organizations"]},"get":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/organization/controller.(*OrganizationsController).GetOrganization`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func15`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func16`\n\n---\n\n","operationId":"GET_/api/v1/organizations","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 organization","tags":["api/v1/organizations"]},"post":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/organization/controller.(*OrganizationsController).CreateOrganization`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func15`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func16`\n\n---\n\n","operationId":"POST_/api/v1/organizations","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/CreateOrganizationRequest"}}},"description":"Request body for types.CreateOrganizationRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"create organization","tags":["api/v1/organizations"]},"put":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/organization/controller.(*OrganizationsController).UpdateOrganization`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func15`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func16`\n\n---\n\n","operationId":"PUT_/api/v1/organizations","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/UpdateOrganizationRequest"}}},"description":"Request body for types.UpdateOrganizationRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 organization","tags":["api/v1/organizations"]}},"/api/v1/organizations/all":{"get":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/organization/controller.(*OrganizationsController).GetOrganizations`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func15`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func16`\n\n---\n\n","operationId":"GET_/api/v1/organizations/all","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 organizations","tags":["api/v1/organizations"]}},"/api/v1/organizations/invite/resend":{"post":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/organization/controller.(*OrganizationsController).ResendInvite`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func15`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func16`\n\n---\n\n","operationId":"POST_/api/v1/organizations/invite/resend","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/InviteResendRequest"}}},"description":"Request body for types.InviteResendRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"resend invite","tags":["api/v1/organizations"]}},"/api/v1/organizations/invite/send":{"post":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/organization/controller.(*OrganizationsController).SendInvite`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func15`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func16`\n\n---\n\n","operationId":"POST_/api/v1/organizations/invite/send","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/InviteSendRequest"}}},"description":"Request body for types.InviteSendRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"send invite","tags":["api/v1/organizations"]}},"/api/v1/organizations/remove-user":{"post":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/organization/controller.(*OrganizationsController).RemoveUserFromOrganization`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func15`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func16`\n\n---\n\n","operationId":"POST_/api/v1/organizations/remove-user","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/RemoveUserFromOrganizationRequest"}}},"description":"Request body for types.RemoveUserFromOrganizationRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"remove user from organization","tags":["api/v1/organizations"]}},"/api/v1/organizations/update-user-role":{"post":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/organization/controller.(*OrganizationsController).UpdateUserRole`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func15`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func16`\n\n---\n\n","operationId":"POST_/api/v1/organizations/update-user-role","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/UpdateUserRoleRequest"}}},"description":"Request body for types.UpdateUserRoleRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 user role","tags":["api/v1/organizations"]}},"/api/v1/organizations/users":{"get":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/organization/controller.(*OrganizationsController).GetOrganizationUsers`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func15`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func16`\n\n---\n\n","operationId":"GET_/api/v1/organizations/users","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/GetOrganizationUsersRequest"}}},"description":"Request body for controller.GetOrganizationUsersRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 organization users","tags":["api/v1/organizations"]}},"/api/v1/update":{"post":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/update/controller.(*UpdateController).PerformUpdate`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n\n---\n\n","operationId":"POST_/api/v1/update","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/UpdateRequest"}}},"description":"Request body for types.UpdateRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateResponse"}},"application/xml":{"schema":{"$ref":"#/components/schemas/UpdateResponse"}}},"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":"perform update","tags":["api/v1/update"]}},"/api/v1/update/check":{"get":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/update/controller.(*UpdateController).CheckForUpdates`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n\n---\n\n","operationId":"GET_/api/v1/update/check","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCheckResponse"}},"application/xml":{"schema":{"$ref":"#/components/schemas/UpdateCheckResponse"}}},"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":"check for updates","tags":["api/v1/update"]}},"/api/v1/user":{"get":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/user/controller.(*UserController).GetUserDetails`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func2`\n\n---\n\n","operationId":"GET_/api/v1/user","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 user details","tags":["api/v1/user"]}},"/api/v1/user/avatar":{"patch":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/user/controller.(*UserController).UpdateAvatar`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func2`\n\n---\n\n","operationId":"PATCH_/api/v1/user/avatar","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/UpdateAvatarRequest"}}},"description":"Request body for types.UpdateAvatarRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 avatar","tags":["api/v1/user"]}},"/api/v1/user/name":{"patch":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/user/controller.(*UserController).UpdateUserName`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func2`\n\n---\n\n","operationId":"PATCH_/api/v1/user/name","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/UpdateUserNameRequest"}}},"description":"Request body for types.UpdateUserNameRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 user name","tags":["api/v1/user"]}},"/api/v1/user/organizations":{"get":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/user/controller.(*UserController).GetUserOrganizations`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func2`\n\n---\n\n","operationId":"GET_/api/v1/user/organizations","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 user organizations","tags":["api/v1/user"]}},"/api/v1/user/settings":{"get":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/user/controller.(*UserController).GetSettings`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func2`\n\n---\n\n","operationId":"GET_/api/v1/user/settings","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 settings","tags":["api/v1/user"]}},"/api/v1/user/settings/auto-update":{"patch":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/user/controller.(*UserController).UpdateAutoUpdate`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func2`\n\n---\n\n","operationId":"PATCH_/api/v1/user/settings/auto-update","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/UpdateAutoUpdateRequest"}}},"description":"Request body for controller.UpdateAutoUpdateRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 auto update","tags":["api/v1/user"]}},"/api/v1/user/settings/font":{"patch":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/user/controller.(*UserController).UpdateFont`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func2`\n\n---\n\n","operationId":"PATCH_/api/v1/user/settings/font","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/UpdateFontRequest"}}},"description":"Request body for controller.UpdateFontRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 font","tags":["api/v1/user"]}},"/api/v1/user/settings/language":{"patch":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/user/controller.(*UserController).UpdateLanguage`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func2`\n\n---\n\n","operationId":"PATCH_/api/v1/user/settings/language","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/UpdateLanguageRequest"}}},"description":"Request body for controller.UpdateLanguageRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 language","tags":["api/v1/user"]}},"/api/v1/user/settings/theme":{"patch":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/user/controller.(*UserController).UpdateTheme`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func1`\n- `github.com/raghavyuva/nixopus-api/internal.(*Router).Routes.func2`\n\n---\n\n","operationId":"PATCH_/api/v1/user/settings/theme","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"requestBody":{"content":{"*/*":{"schema":{"$ref":"#/components/schemas/UpdateThemeRequest"}}},"description":"Request body for controller.UpdateThemeRequest","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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 theme","tags":["api/v1/user"]}},"/api/v1/webhook":{"post":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal/features/deploy/controller.(*DeployController).HandleGithubWebhook`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n\n---\n\n","operationId":"POST_/api/v1/webhook","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Response"}}},"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":"handle github webhook","tags":["api/v1/webhook"]}},"/ws":{"get":{"description":"#### Controller: \n\n`github.com/raghavyuva/nixopus-api/internal.(*Router).WebSocketServer.func1`\n\n#### Middlewares:\n\n- `github.com/go-fuego/fuego.defaultLogger.middleware`\n\n---\n\n","operationId":"GET_/ws","parameters":[{"in":"header","name":"Accept","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/unknown-interface"}},"application/xml":{"schema":{"$ref":"#/components/schemas/unknown-interface"}}},"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":"func1"}}},"servers":[{"description":"local server","url":"http://:8080"}],"tags":[{"name":"api/v1/audit"},{"name":"api/v1/auth"},{"name":"api/v1/container"},{"name":"api/v1/deploy"},{"name":"api/v1/domain"},{"name":"api/v1/domains"},{"name":"api/v1/extensions"},{"name":"api/v1/feature-flags"},{"name":"api/v1/file-manager"},{"name":"api/v1/github-connector"},{"name":"api/v1/health"},{"name":"api/v1/notification"},{"name":"api/v1/organizations"},{"name":"api/v1/update"},{"name":"api/v1/user"},{"name":"api/v1/webhook"},{"name":"application"},{"name":"preferences"},{"name":"smtp"},{"name":"versions"},{"name":"webhook"}]} \ No newline at end of file diff --git a/api/internal/features/extension/controller/delete_extension.go b/api/internal/features/extension/controller/delete_extension.go new file mode 100644 index 000000000..2a20166e8 --- /dev/null +++ b/api/internal/features/extension/controller/delete_extension.go @@ -0,0 +1,24 @@ +package controller + +import ( + "net/http" + + "github.com/go-fuego/fuego" + "github.com/raghavyuva/nixopus-api/internal/features/logger" +) + +func (c *ExtensionsController) DeleteFork(ctx fuego.ContextNoBody) (*struct { + Status string `json:"status"` +}, error) { + id := ctx.PathParam("id") + if id == "" { + return nil, fuego.HTTPError{Err: nil, Status: http.StatusBadRequest} + } + if err := c.service.DeleteFork(id); err != nil { + c.logger.Log(logger.Error, err.Error(), "") + return nil, fuego.HTTPError{Err: err, Status: http.StatusBadRequest} + } + return &struct { + Status string `json:"status"` + }{Status: "ok"}, nil +} diff --git a/api/internal/features/extension/controller/fork_extension.go b/api/internal/features/extension/controller/fork_extension.go new file mode 100644 index 000000000..c90c97f16 --- /dev/null +++ b/api/internal/features/extension/controller/fork_extension.go @@ -0,0 +1,40 @@ +package controller + +import ( + "net/http" + + "github.com/go-fuego/fuego" + "github.com/raghavyuva/nixopus-api/internal/features/logger" + "github.com/raghavyuva/nixopus-api/internal/types" +) + +type ForkExtensionRequest struct { + YAMLContent *string `json:"yaml_content"` +} + +func (c *ExtensionsController) ForkExtension(ctx fuego.ContextWithBody[ForkExtensionRequest]) (types.Extension, error) { + extensionID := ctx.PathParam("extension_id") + if extensionID == "" { + return types.Extension{}, fuego.HTTPError{Err: nil, Status: http.StatusBadRequest} + } + req, err := ctx.Body() + if err != nil { + return types.Extension{}, fuego.HTTPError{Err: err, Status: http.StatusBadRequest} + } + var yamlOverride string + if req.YAMLContent != nil { + yamlOverride = *req.YAMLContent + } + authorName := "" + if userAny := ctx.Request().Context().Value(types.UserContextKey); userAny != nil { + if u, ok := userAny.(*types.User); ok && u != nil { + authorName = u.Username + } + } + newExt, err := c.service.ForkExtension(extensionID, yamlOverride, authorName) + if err != nil { + c.logger.Log(logger.Error, err.Error(), "") + return types.Extension{}, fuego.HTTPError{Err: err, Status: http.StatusInternalServerError} + } + return *newExt, nil +} diff --git a/api/internal/features/extension/controller/get_extensions.go b/api/internal/features/extension/controller/get_extensions.go new file mode 100644 index 000000000..84d2a5c3c --- /dev/null +++ b/api/internal/features/extension/controller/get_extensions.go @@ -0,0 +1,167 @@ +package controller + +import ( + "net/http" + "strconv" + + "github.com/go-fuego/fuego" + "github.com/raghavyuva/nixopus-api/internal/features/logger" + "github.com/raghavyuva/nixopus-api/internal/types" +) + +func (c *ExtensionsController) GetExtensions(ctx fuego.ContextNoBody) (*types.ExtensionListResponse, error) { + params := types.ExtensionListParams{} + + categoryParam := ctx.QueryParam("category") + if categoryParam != "" { + cat := types.ExtensionCategory(categoryParam) + params.Category = &cat + } + + searchParam := ctx.QueryParam("search") + if searchParam != "" { + params.Search = searchParam + } + + if typeParam := ctx.QueryParam("type"); typeParam != "" { + et := types.ExtensionType(typeParam) + params.Type = &et + } + + sortByParam := ctx.QueryParam("sort_by") + if sortByParam != "" { + params.SortBy = types.ExtensionSortField(sortByParam) + } + + sortDirParam := ctx.QueryParam("sort_dir") + if sortDirParam != "" { + params.SortDir = types.SortDirection(sortDirParam) + } + + pageParam := ctx.QueryParam("page") + if pageParam != "" { + if page, err := strconv.Atoi(pageParam); err == nil && page > 0 { + params.Page = page + } + } + + pageSizeParam := ctx.QueryParam("page_size") + if pageSizeParam != "" { + if pageSize, err := strconv.Atoi(pageSizeParam); err == nil && pageSize > 0 { + params.PageSize = pageSize + } + } + + response, err := c.service.ListExtensions(params) + if err != nil { + c.logger.Log(logger.Error, err.Error(), "") + return nil, fuego.HTTPError{ + Err: err, + Status: http.StatusInternalServerError, + } + } + + return response, nil +} + +func (c *ExtensionsController) GetCategories(ctx fuego.ContextNoBody) ([]types.ExtensionCategory, error) { + cats, err := c.service.ListCategories() + if err != nil { + c.logger.Log(logger.Error, err.Error(), "") + return nil, fuego.HTTPError{Err: err, Status: http.StatusInternalServerError} + } + return cats, nil +} + +func (c *ExtensionsController) GetExtension(ctx fuego.ContextNoBody) (types.Extension, error) { + id := ctx.PathParam("id") + if id == "" { + return types.Extension{}, fuego.HTTPError{ + Err: nil, + Status: http.StatusBadRequest, + } + } + + extension, err := c.service.GetExtension(id) + if err != nil { + if err.Error() == "extension not found" { + return types.Extension{}, fuego.HTTPError{ + Err: err, + Status: http.StatusNotFound, + } + } + c.logger.Log(logger.Error, err.Error(), "") + return types.Extension{}, fuego.HTTPError{ + Err: err, + Status: http.StatusInternalServerError, + } + } + + return *extension, nil +} + +func (c *ExtensionsController) GetExtensionByExtensionID(ctx fuego.ContextNoBody) (types.Extension, error) { + extensionID := ctx.PathParam("extension_id") + if extensionID == "" { + return types.Extension{}, fuego.HTTPError{ + Err: nil, + Status: http.StatusBadRequest, + } + } + + extension, err := c.service.GetExtensionByID(extensionID) + if err != nil { + if err.Error() == "extension not found" { + return types.Extension{}, fuego.HTTPError{ + Err: err, + Status: http.StatusNotFound, + } + } + c.logger.Log(logger.Error, err.Error(), "") + return types.Extension{}, fuego.HTTPError{ + Err: err, + Status: http.StatusInternalServerError, + } + } + + return *extension, nil +} + +func (c *ExtensionsController) GetExecution(ctx fuego.ContextNoBody) (*types.ExtensionExecution, error) { + id := ctx.PathParam("execution_id") + if id == "" { + return nil, fuego.HTTPError{ + Err: nil, + Status: http.StatusBadRequest, + } + } + + exec, err := c.service.GetExecutionByID(id) + if err != nil { + c.logger.Log(logger.Error, err.Error(), "") + return nil, fuego.HTTPError{ + Err: err, + Status: http.StatusInternalServerError, + } + } + return exec, nil +} + +func (c *ExtensionsController) ListExecutionsByExtensionID(ctx fuego.ContextNoBody) ([]types.ExtensionExecution, error) { + extensionID := ctx.PathParam("extension_id") + if extensionID == "" { + return nil, fuego.HTTPError{ + Err: nil, + Status: http.StatusBadRequest, + } + } + execs, err := c.service.ListExecutionsByExtensionID(extensionID) + if err != nil { + c.logger.Log(logger.Error, err.Error(), "") + return nil, fuego.HTTPError{ + Err: err, + Status: http.StatusInternalServerError, + } + } + return execs, nil +} diff --git a/api/internal/features/extension/controller/init.go b/api/internal/features/extension/controller/init.go new file mode 100644 index 000000000..78d45459d --- /dev/null +++ b/api/internal/features/extension/controller/init.go @@ -0,0 +1,72 @@ +package controller + +import ( + "context" + "net/http" + + "github.com/raghavyuva/nixopus-api/internal/features/extension/service" + "github.com/raghavyuva/nixopus-api/internal/features/extension/storage" + "github.com/raghavyuva/nixopus-api/internal/features/extension/validation" + "github.com/raghavyuva/nixopus-api/internal/features/logger" + shared_storage "github.com/raghavyuva/nixopus-api/internal/storage" + "github.com/raghavyuva/nixopus-api/internal/utils" +) + +type ExtensionsController struct { + store *shared_storage.Store + service *service.ExtensionService + validator *validation.Validator + ctx context.Context + logger logger.Logger +} + +func NewExtensionsController( + store *shared_storage.Store, + ctx context.Context, + l logger.Logger, +) *ExtensionsController { + storage := storage.ExtensionStorage{DB: store.DB, Ctx: ctx} + return &ExtensionsController{ + store: store, + service: service.NewExtensionService(store, ctx, l, &storage), + validator: validation.NewValidator(&storage), + ctx: ctx, + logger: l, + } +} + +// parseAndValidate parses and validates the request body. +// +// This method attempts to parse the request body into the provided 'req' interface +// using the controller's validator. If parsing fails, an error response is sent +// and the method returns false. It also validates the parsed request object and +// returns false if validation fails. If both operations are successful, it returns true. +// +// Parameters: +// +// w - the HTTP response writer to send error responses. +// r - the HTTP request containing the body to parse. +// req - the interface to populate with the parsed request body. +// +// Returns: +// +// bool - true if parsing and validation succeed, false otherwise. +func (c *ExtensionsController) parseAndValidate(w http.ResponseWriter, r *http.Request, req interface{}) bool { + if err := c.validator.ParseRequestBody(r, r.Body, req); err != nil { + c.logger.Log(logger.Error, "Failed to decode request", err.Error()) + utils.SendErrorResponse(w, "Failed to decode request", http.StatusBadRequest) + return false + } + + if err := c.validator.ValidateRequest(req); err != nil { + c.logger.Log(logger.Error, err.Error(), err.Error()) + utils.SendErrorResponse(w, err.Error(), http.StatusBadRequest) + return false + } + + return true +} + +type RunExtensionRequest struct { + Variables map[string]interface{} `json:"variables"` +} diff --git a/api/internal/features/extension/controller/run_extension.go b/api/internal/features/extension/controller/run_extension.go new file mode 100644 index 000000000..1980459d8 --- /dev/null +++ b/api/internal/features/extension/controller/run_extension.go @@ -0,0 +1,86 @@ +package controller + +import ( + "net/http" + "strconv" + + "github.com/go-fuego/fuego" + "github.com/raghavyuva/nixopus-api/internal/features/logger" + "github.com/raghavyuva/nixopus-api/internal/types" +) + +func (c *ExtensionsController) RunExtension(ctx fuego.ContextWithBody[RunExtensionRequest]) (*types.ExtensionExecution, error) { + extensionID := ctx.PathParam("extension_id") + if extensionID == "" { + return nil, fuego.HTTPError{Err: nil, Status: http.StatusBadRequest} + } + contentType := ctx.Request().Header.Get("Content-Type") + if contentType != "" && (len(contentType) >= 19 && contentType[:19] == "multipart/form-data") { + vars, err := c.service.ParseMultipartRunRequest(ctx.Request()) + if err != nil { + return nil, fuego.HTTPError{Err: err, Status: http.StatusBadRequest} + } + exec, err := c.service.StartRun(extensionID, vars) + if err != nil { + c.logger.Log(logger.Error, err.Error(), "") + return nil, fuego.HTTPError{Err: err, Status: http.StatusInternalServerError} + } + return exec, nil + } + + req, err := ctx.Body() + if err != nil { + return nil, fuego.HTTPError{Err: err, Status: http.StatusBadRequest} + } + exec, err := c.service.StartRun(extensionID, req.Variables) + if err != nil { + c.logger.Log(logger.Error, err.Error(), "") + return nil, fuego.HTTPError{Err: err, Status: http.StatusInternalServerError} + } + return exec, nil +} + +func (c *ExtensionsController) CancelExecution(ctx fuego.ContextNoBody) (*types.Response, error) { + execID := ctx.PathParam("execution_id") + if execID == "" { + return nil, fuego.HTTPError{Err: nil, Status: http.StatusBadRequest} + } + if err := c.service.CancelExecution(execID); err != nil { + return nil, fuego.HTTPError{Err: err, Status: http.StatusInternalServerError} + } + return &types.Response{Status: "success", Message: "Execution cancelled"}, nil +} + +type ListLogsResponse struct { + Logs []types.ExtensionLog `json:"logs"` + NextAfter int64 `json:"next_after"` +} + +func (c *ExtensionsController) ListExecutionLogs(ctx fuego.ContextNoBody) (*ListLogsResponse, error) { + execID := ctx.PathParam("execution_id") + if execID == "" { + return nil, fuego.HTTPError{Err: nil, Status: http.StatusBadRequest} + } + afterSeq := int64(0) + if v := ctx.QueryParam("afterSeq"); v != "" { + if parsed, err := strconv.ParseInt(v, 10, 64); err == nil { + afterSeq = parsed + } + } + limit := 200 + if v := ctx.QueryParam("limit"); v != "" { + if parsed, err := strconv.Atoi(v); err == nil { + limit = parsed + } + } + logs, err := c.service.ListExecutionLogs(execID, afterSeq, limit) + if err != nil { + c.logger.Log(logger.Error, err.Error(), "") + return nil, fuego.HTTPError{Err: err, Status: http.StatusInternalServerError} + } + var next int64 = afterSeq + if len(logs) > 0 { + next = logs[len(logs)-1].Sequence + } + return &ListLogsResponse{Logs: logs, NextAfter: next}, nil +} diff --git a/api/internal/features/extension/engine/command.go b/api/internal/features/extension/engine/command.go new file mode 100644 index 000000000..291a29e1d --- /dev/null +++ b/api/internal/features/extension/engine/command.go @@ -0,0 +1,50 @@ +package engine + +import ( + "fmt" + + "github.com/raghavyuva/nixopus-api/internal/features/ssh" + "github.com/raghavyuva/nixopus-api/internal/types" +) + +type commandModule struct{} + +func (commandModule) Type() string { return "command" } + +func (commandModule) Execute(sshClient *ssh.SSH, step types.SpecStep, vars map[string]interface{}) (string, func(), error) { + raw, _ := step.Properties["cmd"].(string) + if raw == "" { + return "", nil, fmt.Errorf("command: 'cmd' is required") + } + revertRaw, _ := step.Properties["revert_cmd"].(string) + user, _ := step.Properties["user"].(string) + + cmd := replaceVars(raw, vars) + if user != "" { + cmd = fmt.Sprintf("sudo -u %s %s", user, cmd) + } + if step.Timeout > 0 && hasCommand(sshClient, "timeout") { + cmd = fmt.Sprintf("timeout %ds %s", step.Timeout, cmd) + } + output, err := sshClient.RunCommand(cmd) + if err != nil { + return "", nil, fmt.Errorf("command: execution failed cmd=%q user=%q timeout=%ds: %w (output: %s)", cmd, user, step.Timeout, err, output) + } + + var compensate func() + if revertRaw != "" { + rev := replaceVars(revertRaw, vars) + if user != "" { + rev = fmt.Sprintf("sudo -u %s %s", user, rev) + } + if step.Timeout > 0 && hasCommand(sshClient, "timeout") { + rev = fmt.Sprintf("timeout %ds %s", step.Timeout, rev) + } + compensate = func() { _, _ = sshClient.RunCommand(rev) } + } + return output, compensate, nil +} + +func init() { + RegisterModule(commandModule{}) +} diff --git a/api/internal/features/extension/engine/common.go b/api/internal/features/extension/engine/common.go new file mode 100644 index 000000000..b1ffe647b --- /dev/null +++ b/api/internal/features/extension/engine/common.go @@ -0,0 +1,17 @@ +package engine + +import ( + "fmt" + "strings" +) + +// replaceVars substitutes tokens like {{ varName }} in the input string +// using the provided vars map. Missing vars are left unchanged. +func replaceVars(in string, vars map[string]interface{}) string { + out := in + for k, v := range vars { + token := fmt.Sprintf("{{ %s }}", k) + out = strings.ReplaceAll(out, token, fmt.Sprint(v)) + } + return out +} diff --git a/api/internal/features/extension/engine/docker.go b/api/internal/features/extension/engine/docker.go new file mode 100644 index 000000000..307198adf --- /dev/null +++ b/api/internal/features/extension/engine/docker.go @@ -0,0 +1,333 @@ +package engine + +import ( + "context" + "fmt" + "io" + "strings" + + "github.com/docker/docker/api/types/container" + imagetypes "github.com/docker/docker/api/types/image" + "github.com/docker/docker/api/types/network" + "github.com/docker/go-connections/nat" + deploydocker "github.com/raghavyuva/nixopus-api/internal/features/deploy/docker" + "github.com/raghavyuva/nixopus-api/internal/features/ssh" + "github.com/raghavyuva/nixopus-api/internal/types" +) + +type dockerModule struct{} + +func (dockerModule) Type() string { return "docker" } + +func (dockerModule) Execute(_ *ssh.SSH, step types.SpecStep, vars map[string]interface{}) (string, func(), error) { + action, _ := step.Properties["action"].(string) + name, _ := step.Properties["name"].(string) + image, _ := step.Properties["image"].(string) + tag, _ := step.Properties["tag"].(string) + ports, _ := step.Properties["ports"].(string) + restart, _ := step.Properties["restart"].(string) + cmdStr, _ := step.Properties["cmd"].(string) + envAny, _ := step.Properties["env"] + volumesAny, _ := step.Properties["volumes"] + networksAny, _ := step.Properties["networks"] + + if image != "" { + image = replaceVars(image, vars) + } + if tag != "" { + tag = replaceVars(tag, vars) + } + if name != "" { + name = replaceVars(name, vars) + } + if ports != "" { + ports = replaceVars(ports, vars) + } + if cmdStr != "" { + cmdStr = replaceVars(cmdStr, vars) + } + + if action == "" { + return "", nil, fmt.Errorf("docker: action is required (name=%q image=%q tag=%q)", name, image, tag) + } + + svc := deploydocker.NewDockerService() + + type handler func() (string, func(), error) + handlers := map[string]handler{ + "pull": func() (string, func(), error) { return dockerPull(svc, image, tag) }, + "run": func() (string, func(), error) { + return dockerRun(svc, name, image, tag, ports, restart, cmdStr, envAny, volumesAny, networksAny, vars) + }, + "stop": func() (string, func(), error) { return dockerStop(svc, name) }, + "start": func() (string, func(), error) { return dockerStart(svc, name) }, + "rm": func() (string, func(), error) { return dockerRm(svc, name) }, + } + + h, ok := handlers[action] + if !ok { + return "", nil, fmt.Errorf("docker: unsupported action %q (name=%q image=%q tag=%q)", action, name, image, tag) + } + out, comp, err := h() + if err != nil { + return "", nil, err + } + return strings.TrimSpace(out), comp, nil +} + +func parsePortMappings(mappings string) (nat.PortMap, error) { + pm := nat.PortMap{} + for _, m := range strings.Split(mappings, ",") { + m = strings.TrimSpace(m) + if m == "" { + continue + } + parts := strings.Split(m, ":") + if len(parts) != 2 { + return nil, fmt.Errorf("invalid port mapping: %s", m) + } + host := parts[0] + p, err := nat.NewPort("tcp", parts[1]) + if err != nil { + return nil, err + } + pm[p] = []nat.PortBinding{{HostIP: "0.0.0.0", HostPort: host}} + } + return pm, nil +} + +func exposedFromBindings(pm nat.PortMap) nat.PortSet { + es := nat.PortSet{} + for p := range pm { + es[p] = struct{}{} + } + return es +} + +func normalizeStringList(v interface{}, vars map[string]interface{}) ([]string, error) { + if v == nil { + return nil, nil + } + switch t := v.(type) { + case string: + s := strings.TrimSpace(replaceVars(t, vars)) + if s == "" { + return nil, nil + } + parts := strings.Split(s, ",") + out := make([]string, 0, len(parts)) + for _, p := range parts { + p = strings.TrimSpace(p) + if p != "" { + out = append(out, p) + } + } + return out, nil + case []interface{}: + out := make([]string, 0, len(t)) + for _, it := range t { + s, ok := it.(string) + if !ok { + return nil, fmt.Errorf("invalid list item type, expected string") + } + s = strings.TrimSpace(replaceVars(s, vars)) + if s != "" { + out = append(out, s) + } + } + return out, nil + default: + return nil, fmt.Errorf("unsupported list type") + } +} + +func normalizeEnv(v interface{}, vars map[string]interface{}) ([]string, error) { + if v == nil { + return nil, nil + } + switch t := v.(type) { + case map[string]interface{}: + out := make([]string, 0, len(t)) + for k, val := range t { + vs := fmt.Sprint(val) + out = append(out, fmt.Sprintf("%s=%s", k, replaceVars(vs, vars))) + } + return out, nil + case []interface{}: + out := make([]string, 0, len(t)) + for _, it := range t { + s, ok := it.(string) + if !ok { + return nil, fmt.Errorf("invalid env item type, expected string") + } + out = append(out, replaceVars(strings.TrimSpace(s), vars)) + } + return out, nil + case string: + s := strings.TrimSpace(replaceVars(t, vars)) + if s == "" { + return nil, nil + } + parts := strings.Split(s, ",") + out := make([]string, 0, len(parts)) + for _, p := range parts { + p = strings.TrimSpace(p) + if p != "" { + out = append(out, p) + } + } + return out, nil + default: + return nil, fmt.Errorf("unsupported env type") + } +} + +func findContainerIDByName(svc *deploydocker.DockerService, name string) (string, error) { + if name == "" { + return "", fmt.Errorf("name is required") + } + list, err := svc.ListContainers(container.ListOptions{All: true}) + if err != nil { + return "", err + } + for _, c := range list { + for _, n := range c.Names { + if strings.TrimPrefix(n, "/") == name { + return c.ID, nil + } + } + } + return "", fmt.Errorf("container not found: %s", name) +} + +func init() { + RegisterModule(dockerModule{}) +} + +func dockerPull(svc *deploydocker.DockerService, image string, tag string) (string, func(), error) { + if image == "" { + return "", nil, fmt.Errorf("docker: image is required for pull") + } + ref := image + if tag != "" { + ref = fmt.Sprintf("%s:%s", image, tag) + } + r, err := svc.Cli.ImagePull(context.Background(), ref, imagetypes.PullOptions{}) + if err != nil { + return "", nil, fmt.Errorf("docker: image pull failed ref=%q: %w", ref, err) + } + defer r.Close() + b, _ := io.ReadAll(r) + return string(b), nil, nil +} + +func dockerRun( + svc *deploydocker.DockerService, + name string, + image string, + tag string, + ports string, + restart string, + cmdStr string, + envAny interface{}, + volumesAny interface{}, + networksAny interface{}, + vars map[string]interface{}, +) (string, func(), error) { + if image == "" || name == "" { + return "", nil, fmt.Errorf("docker: run requires image and name (name=%q image=%q tag=%q)", name, image, tag) + } + ref := image + if tag != "" { + ref = fmt.Sprintf("%s:%s", image, tag) + } + containerCfg := container.Config{Image: ref} + hostCfg := container.HostConfig{} + networkingCfg := network.NetworkingConfig{} + if ports != "" { + pm, err := parsePortMappings(ports) + if err != nil { + return "", nil, err + } + hostCfg.PortBindings = pm + containerCfg.ExposedPorts = exposedFromBindings(pm) + } + if restart != "" { + hostCfg.RestartPolicy = container.RestartPolicy{Name: container.RestartPolicyMode(restart)} + } + // env + if envList, err := normalizeEnv(envAny, vars); err != nil { + return "", nil, err + } else if len(envList) > 0 { + containerCfg.Env = envList + } + + // volumes (binds) + if binds, err := normalizeStringList(volumesAny, vars); err != nil { + return "", nil, err + } else if len(binds) > 0 { + hostCfg.Binds = binds + } + + // networks + if nets, err := normalizeStringList(networksAny, vars); err != nil { + return "", nil, err + } else if len(nets) > 0 { + if networkingCfg.EndpointsConfig == nil { + networkingCfg.EndpointsConfig = map[string]*network.EndpointSettings{} + } + for _, n := range nets { + networkingCfg.EndpointsConfig[n] = &network.EndpointSettings{} + } + } + + // command + if strings.TrimSpace(cmdStr) != "" { + containerCfg.Cmd = strings.Fields(cmdStr) + } + + resp, err := svc.CreateContainer(containerCfg, hostCfg, networkingCfg, name) + if err != nil { + return "", nil, fmt.Errorf("docker: create container failed name=%q image=%q: %w", name, ref, err) + } + if err := svc.StartContainer(resp.ID, container.StartOptions{}); err != nil { + return "", nil, fmt.Errorf("docker: start container failed name=%q id=%q: %w", name, resp.ID, err) + } + compensate := func() { _ = svc.RemoveContainer(resp.ID, container.RemoveOptions{Force: true}) } + return resp.ID, compensate, nil +} + +func dockerStop(svc *deploydocker.DockerService, name string) (string, func(), error) { + id, err := findContainerIDByName(svc, name) + if err != nil { + return "", nil, fmt.Errorf("docker: stop failed - %w", err) + } + if err := svc.StopContainer(id, container.StopOptions{}); err != nil { + return "", nil, fmt.Errorf("docker: stop container failed name=%q id=%q: %w", name, id, err) + } + compensate := func() { _ = svc.StartContainer(id, container.StartOptions{}) } + return id, compensate, nil +} + +func dockerStart(svc *deploydocker.DockerService, name string) (string, func(), error) { + id, err := findContainerIDByName(svc, name) + if err != nil { + return "", nil, fmt.Errorf("docker: start failed - %w", err) + } + if err := svc.StartContainer(id, container.StartOptions{}); err != nil { + return "", nil, fmt.Errorf("docker: start container failed name=%q id=%q: %w", name, id, err) + } + compensate := func() { _ = svc.StopContainer(id, container.StopOptions{}) } + return id, compensate, nil +} + +func dockerRm(svc *deploydocker.DockerService, name string) (string, func(), error) { + id, err := findContainerIDByName(svc, name) + if err != nil { + return "", nil, fmt.Errorf("docker: rm failed - %w", err) + } + if err := svc.RemoveContainer(id, container.RemoveOptions{Force: true}); err != nil { + return "", nil, fmt.Errorf("docker: remove container failed name=%q id=%q: %w", name, id, err) + } + return id, nil, nil +} diff --git a/api/internal/features/extension/engine/docker_compose.go b/api/internal/features/extension/engine/docker_compose.go new file mode 100644 index 000000000..256fe7571 --- /dev/null +++ b/api/internal/features/extension/engine/docker_compose.go @@ -0,0 +1,77 @@ +package engine + +import ( + "fmt" + "strings" + + deploydocker "github.com/raghavyuva/nixopus-api/internal/features/deploy/docker" + "github.com/raghavyuva/nixopus-api/internal/features/ssh" + "github.com/raghavyuva/nixopus-api/internal/types" +) + +type dockerComposeModule struct{} + +func (dockerComposeModule) Type() string { return "docker_compose" } + +func (dockerComposeModule) Execute(_ *ssh.SSH, step types.SpecStep, vars map[string]interface{}) (string, func(), error) { + file, _ := step.Properties["file"].(string) + action, _ := step.Properties["action"].(string) // up, down, pull, build, restart + _, _ = step.Properties["project"].(string) + _, _ = step.Properties["args"].(string) + revertCmdRaw, _ := step.Properties["revert_cmd"].(string) + _, _ = step.Properties["user"].(string) + + if action == "" { + return "", nil, fmt.Errorf("docker_compose action is required") + } + + svc := deploydocker.NewDockerService() + + type handler func() (string, func(), error) + handlers := map[string]handler{ + "up": func() (string, func(), error) { return composeUp(svc, file) }, + "down": func() (string, func(), error) { return composeDown(svc, file) }, + "build": func() (string, func(), error) { return composeBuild(svc, file) }, + } + + h, ok := handlers[action] + if !ok { + return "", nil, fmt.Errorf("unsupported docker_compose action: %s", action) + } + out, comp, err := h() + if err != nil { + return "", nil, err + } + + if revertCmdRaw != "" { + // ignored by design in service backed module + } + return strings.TrimSpace(out), comp, nil +} + +func composeUp(svc *deploydocker.DockerService, file string) (string, func(), error) { + if err := svc.ComposeUp(file, map[string]string{}); err != nil { + return "", nil, err + } + compensate := func() { _ = svc.ComposeDown(file) } + return "compose up", compensate, nil +} + +func composeDown(svc *deploydocker.DockerService, file string) (string, func(), error) { + if err := svc.ComposeDown(file); err != nil { + return "", nil, err + } + compensate := func() { _ = svc.ComposeUp(file, map[string]string{}) } + return "compose down", compensate, nil +} + +func composeBuild(svc *deploydocker.DockerService, file string) (string, func(), error) { + if err := svc.ComposeBuild(file, map[string]string{}); err != nil { + return "", nil, err + } + return "compose build", nil, nil +} + +func init() { + RegisterModule(dockerComposeModule{}) +} diff --git a/api/internal/features/extension/engine/file.go b/api/internal/features/extension/engine/file.go new file mode 100644 index 000000000..d77f1bdfb --- /dev/null +++ b/api/internal/features/extension/engine/file.go @@ -0,0 +1,148 @@ +package engine + +import ( + "fmt" + "io" + "os" + "path/filepath" + + "github.com/pkg/sftp" + "github.com/raghavyuva/nixopus-api/internal/features/ssh" + "github.com/raghavyuva/nixopus-api/internal/types" +) + +type fileModule struct{} + +func (fileModule) Type() string { return "file" } + +type sftpClient interface { + Close() error + MkdirAll(path string) error + Remove(path string) error + Rename(fromPath string, toPath string) error + Create(path string) (*sftp.File, error) +} + +var ( + runCommandFn = func(c *ssh.SSH, cmd string) (string, error) { return c.RunCommand(cmd) } + withSFTPFn = func(c *ssh.SSH, f func(s sftpClient) (string, error)) (string, error) { + client, err := c.Connect() + if err != nil { + return "", err + } + s, err := client.NewSftp() + if err != nil { + return "", err + } + defer s.Close() + return f(s) + } +) + +type fileAction func(*ssh.SSH, string, string) (string, error) + +func handleMove(c *ssh.SSH, src, dest string) (string, error) { + return withSFTPFn(c, func(s sftpClient) (string, error) { + if err := s.Rename(src, dest); err != nil { + return "", fmt.Errorf("failed to move %s to %s: %w", src, dest, err) + } + return fmt.Sprintf("moved %s to %s", src, dest), nil + }) +} + +func handleCopy(c *ssh.SSH, src, dest string) (string, error) { + cmd := fmt.Sprintf("cp -r %s %s", src, dest) + output, err := runCommandFn(c, cmd) + if err != nil { + return "", fmt.Errorf("failed to copy %s to %s: %w (output: %s)", src, dest, err, output) + } + return fmt.Sprintf("copied %s to %s", src, dest), nil +} + +func handleUpload(c *ssh.SSH, src, dest string) (string, error) { + f, err := os.Open(src) + if err != nil { + return "", fmt.Errorf("failed to open source file %s: %w", src, err) + } + defer f.Close() + + return withSFTPFn(c, func(s sftpClient) (string, error) { + if err := s.MkdirAll(filepath.Dir(dest)); err != nil { + return "", fmt.Errorf("failed to create destination directory %s: %w", filepath.Dir(dest), err) + } + out, err := s.Create(dest) + if err != nil { + return "", fmt.Errorf("failed to create destination file %s: %w", dest, err) + } + defer out.Close() + if _, err := io.Copy(out, f); err != nil { + return "", fmt.Errorf("failed to copy file content from %s to %s: %w", src, dest, err) + } + return fmt.Sprintf("uploaded to %s", dest), nil + }) +} + +func handleDelete(c *ssh.SSH, _src, dest string) (string, error) { + return withSFTPFn(c, func(s sftpClient) (string, error) { + if err := s.Remove(dest); err != nil { + return "", fmt.Errorf("failed to delete %s: %w", dest, err) + } + return fmt.Sprintf("deleted %s", dest), nil + }) +} + +func handleMkdir(c *ssh.SSH, _src, dest string) (string, error) { + return withSFTPFn(c, func(s sftpClient) (string, error) { + if err := s.MkdirAll(dest); err != nil { + return "", fmt.Errorf("failed to create directory %s: %w", dest, err) + } + return fmt.Sprintf("mkdir %s", dest), nil + }) +} + +var actionHandlers = map[string]fileAction{ + "move": handleMove, + "copy": handleCopy, + "upload": handleUpload, + "delete": handleDelete, + "mkdir": handleMkdir, +} + +func (fileModule) Execute(sshClient *ssh.SSH, step types.SpecStep, vars map[string]interface{}) (string, func(), error) { + action, _ := step.Properties["action"].(string) + src, _ := step.Properties["src"].(string) + dest, _ := step.Properties["dest"].(string) + + if action == "mkdir" && dest == "" { + return "", nil, fmt.Errorf("dest is required for mkdir action") + } + + h, ok := actionHandlers[action] + if !ok { + return "", nil, fmt.Errorf("unsupported file action: %s", action) + } + out, err := h(sshClient, src, dest) + if err != nil { + return "", nil, err + } + + var compensate func() + switch action { + case "move": + compensate = func() { _, _ = handleMove(sshClient, dest, src) } + case "copy": + compensate = func() { _, _ = handleDelete(sshClient, "", dest) } + case "upload": + compensate = func() { _, _ = handleDelete(sshClient, "", dest) } + case "delete": + // cannot reliably restore deleted content without backup + compensate = nil + case "mkdir": + compensate = func() { _, _ = handleDelete(sshClient, "", dest) } + } + return out, compensate, nil +} + +func init() { + RegisterModule(fileModule{}) +} diff --git a/api/internal/features/extension/engine/package.go b/api/internal/features/extension/engine/package.go new file mode 100644 index 000000000..f453b79d4 --- /dev/null +++ b/api/internal/features/extension/engine/package.go @@ -0,0 +1,166 @@ +package engine + +import ( + "fmt" + "strings" + + "github.com/raghavyuva/nixopus-api/internal/features/ssh" + "github.com/raghavyuva/nixopus-api/internal/types" +) + +type packageModule struct{} + +func (packageModule) Type() string { return "package" } + +func (packageModule) Execute(sshClient *ssh.SSH, step types.SpecStep, vars map[string]interface{}) (string, func(), error) { + name, _ := step.Properties["name"].(string) + state, _ := step.Properties["state"].(string) + if name == "" { + return "", nil, fmt.Errorf("package name is required") + } + if state == "" { + state = "present" + } + + pm, err := detectPackageManager(sshClient) + if err != nil { + return "", nil, err + } + + cmd, err := buildPackageCommand(pm, state, name) + if err != nil { + return "", nil, err + } + + if step.Timeout > 0 && hasCommand(sshClient, "timeout") { + cmd = fmt.Sprintf("timeout %ds %s", step.Timeout, cmd) + } + out, err := sshClient.RunCommand(cmd) + if err != nil { + return "", nil, fmt.Errorf("package operation failed: %w (output: %s)", err, out) + } + var compensate func() + switch state { + case "present", "latest": + compensate = func() { + if rollbackCmd, rbErr := buildPackageCommand(pm, "absent", name); rbErr == nil { + _, _ = sshClient.RunCommand(rollbackCmd) + } + } + case "absent": + compensate = func() { + if rollbackCmd, rbErr := buildPackageCommand(pm, "present", name); rbErr == nil { + _, _ = sshClient.RunCommand(rollbackCmd) + } + } + } + return strings.TrimSpace(out), compensate, nil +} + +type packageBuilder func(state, name string) (string, error) + +func buildPackageCommand(pm string, state string, name string) (string, error) { + builders := map[string]packageBuilder{ + "apt": buildAptCommand, + "dnf": buildDnfCommand, + "yum": buildYumCommand, + "apk": buildApkCommand, + "pacman": buildPacmanCommand, + } + b, ok := builders[pm] + if !ok { + return "", fmt.Errorf("unsupported package manager: %s", pm) + } + return b(state, name) +} + +func buildAptCommand(state, name string) (string, error) { + switch state { + case "present": + return fmt.Sprintf("sudo apt-get update -y && sudo DEBIAN_FRONTEND=noninteractive apt-get install -y %s", name), nil + case "absent": + return fmt.Sprintf("sudo DEBIAN_FRONTEND=noninteractive apt-get remove -y %s", name), nil + case "latest": + return fmt.Sprintf("sudo apt-get update -y && sudo DEBIAN_FRONTEND=noninteractive apt-get install -y --only-upgrade %s", name), nil + default: + return "", fmt.Errorf("unsupported state '%s' for apt", state) + } +} + +func buildDnfCommand(state, name string) (string, error) { + switch state { + case "present": + return fmt.Sprintf("sudo dnf install -y %s", name), nil + case "absent": + return fmt.Sprintf("sudo dnf remove -y %s", name), nil + case "latest": + return fmt.Sprintf("sudo dnf upgrade -y %s", name), nil + default: + return "", fmt.Errorf("unsupported state '%s' for dnf", state) + } +} + +func buildYumCommand(state, name string) (string, error) { + switch state { + case "present": + return fmt.Sprintf("sudo yum install -y %s", name), nil + case "absent": + return fmt.Sprintf("sudo yum remove -y %s", name), nil + case "latest": + return fmt.Sprintf("sudo yum update -y %s", name), nil + default: + return "", fmt.Errorf("unsupported state '%s' for yum", state) + } +} + +func buildApkCommand(state, name string) (string, error) { + switch state { + case "present": + return fmt.Sprintf("sudo apk add --no-cache %s", name), nil + case "absent": + return fmt.Sprintf("sudo apk del %s", name), nil + case "latest": + return fmt.Sprintf("sudo apk add --upgrade %s", name), nil + default: + return "", fmt.Errorf("unsupported state '%s' for apk", state) + } +} + +func buildPacmanCommand(state, name string) (string, error) { + switch state { + case "present": + return fmt.Sprintf("sudo pacman -S --noconfirm %s", name), nil + case "absent": + return fmt.Sprintf("sudo pacman -R --noconfirm %s", name), nil + case "latest": + return "sudo pacman -Syu --noconfirm", nil + default: + return "", fmt.Errorf("unsupported state '%s' for pacman", state) + } +} + +func detectPackageManager(sshClient *ssh.SSH) (string, error) { + switch { + case hasCommand(sshClient, "apt-get"): + return "apt", nil + case hasCommand(sshClient, "dnf"): + return "dnf", nil + case hasCommand(sshClient, "yum"): + return "yum", nil + case hasCommand(sshClient, "apk"): + return "apk", nil + case hasCommand(sshClient, "pacman"): + return "pacman", nil + default: + return "", fmt.Errorf("no supported package manager found") + } +} + +func hasCommand(sshClient *ssh.SSH, name string) bool { + out, _ := sshClient.RunCommand("command -v " + name + " >/dev/null 2>&1 && echo yes || echo no") + return strings.Contains(out, "yes") +} + +func init() { + RegisterModule(packageModule{}) +} diff --git a/api/internal/features/extension/engine/registry.go b/api/internal/features/extension/engine/registry.go new file mode 100644 index 000000000..1e054a239 --- /dev/null +++ b/api/internal/features/extension/engine/registry.go @@ -0,0 +1,39 @@ +package engine + +import ( + "sync" + + "github.com/raghavyuva/nixopus-api/internal/features/ssh" + "github.com/raghavyuva/nixopus-api/internal/types" +) + +// Module defines a pluggable executor for a step type. +type Module interface { + // Type returns the step type this module handles + Type() string + // Execute runs the step and returns output or an error. + Execute(sshClient *ssh.SSH, step types.SpecStep, vars map[string]interface{}) (string, func(), error) +} + +var ( + regMu sync.RWMutex + modules = map[string]Module{} +) + +// RegisterModule registers a module by its Type(). +func RegisterModule(m Module) { + if m == nil { + return + } + regMu.Lock() + modules[m.Type()] = m + regMu.Unlock() +} + +// GetModule fetches a module by type. +func GetModule(stepType string) Module { + regMu.RLock() + m := modules[stepType] + regMu.RUnlock() + return m +} diff --git a/api/internal/features/extension/engine/service.go b/api/internal/features/extension/engine/service.go new file mode 100644 index 000000000..053585fe8 --- /dev/null +++ b/api/internal/features/extension/engine/service.go @@ -0,0 +1,73 @@ +package engine + +import ( + "fmt" + + "github.com/raghavyuva/nixopus-api/internal/features/ssh" + "github.com/raghavyuva/nixopus-api/internal/types" +) + +type serviceModule struct{} + +func (serviceModule) Type() string { return "service" } + +func (serviceModule) Execute(sshClient *ssh.SSH, step types.SpecStep, vars map[string]interface{}) (string, func(), error) { + name, _ := step.Properties["name"].(string) + action, _ := step.Properties["action"].(string) + revertAction, _ := step.Properties["revert_action"].(string) + runAsUser, _ := step.Properties["user"].(string) + if name == "" { + return "", nil, fmt.Errorf("service name is required for service step") + } + if action == "" { + return "", nil, fmt.Errorf("service action is required for service step") + } + cmd := serviceCmd(sshClient, action, name, step.Timeout) + if runAsUser != "" { + cmd = fmt.Sprintf("sudo -u %s %s", runAsUser, cmd) + } + output, err := sshClient.RunCommand(cmd) + if err != nil { + return "", nil, fmt.Errorf("failed to execute service command '%s': %w (output: %s)", cmd, err, output) + } + if revertAction == "" { + switch action { + case "start": + revertAction = "stop" + case "stop": + revertAction = "start" + case "enable": + revertAction = "disable" + case "disable": + revertAction = "enable" + case "restart": + revertAction = "restart" + } + } + var compensate func() + if revertAction != "" { + rev := serviceCmd(sshClient, revertAction, name, step.Timeout) + if runAsUser != "" { + rev = fmt.Sprintf("sudo -u %s %s", runAsUser, rev) + } + compensate = func() { _, _ = sshClient.RunCommand(rev) } + } + return output, compensate, nil +} + +func serviceCmd(sshClient *ssh.SSH, action string, name string, timeout int) string { + var base string + if hasCommand(sshClient, "systemctl") { + base = "sudo systemctl " + action + " " + name + } else { + base = "sudo service " + name + " " + action + } + if timeout > 0 && hasCommand(sshClient, "timeout") { + return fmt.Sprintf("timeout %ds %s", timeout, base) + } + return base +} + +func init() { + RegisterModule(serviceModule{}) +} diff --git a/api/internal/features/extension/engine/user.go b/api/internal/features/extension/engine/user.go new file mode 100644 index 000000000..6e9d49e5b --- /dev/null +++ b/api/internal/features/extension/engine/user.go @@ -0,0 +1,238 @@ +package engine + +import ( + "fmt" + "strings" + + "github.com/raghavyuva/nixopus-api/internal/features/ssh" + "github.com/raghavyuva/nixopus-api/internal/types" +) + +type userModule struct{} + +func (userModule) Type() string { return "user" } + +func (userModule) Execute(sshClient *ssh.SSH, step types.SpecStep, vars map[string]interface{}) (string, func(), error) { + username, _ := step.Properties["username"].(string) + action, _ := step.Properties["action"].(string) + shell, _ := step.Properties["shell"].(string) + home, _ := step.Properties["home"].(string) + groups, _ := step.Properties["groups"].(string) + revertAction, _ := step.Properties["revert_action"].(string) + + if username == "" { + return "", nil, fmt.Errorf("username is required for user operations") + } + + tools := userTools(sshClient) + var cmd string + switch action { + case "ensure": + var err error + cmd, err = buildEnsureUserCmd(tools, username, shell, home, groups) + if err != nil { + return "", nil, fmt.Errorf("failed to build ensure user command: %w", err) + } + case "delete": + var err error + cmd, err = buildDeleteUserCmd(tools, username) + if err != nil { + return "", nil, fmt.Errorf("failed to build delete user command: %w", err) + } + case "modify": + var err error + cmd, err = buildModifyUserCmd(username, shell, home, groups) + if err != nil { + return "", nil, fmt.Errorf("failed to build modify user command: %w", err) + } + case "add_groups": + var err error + cmd, err = buildAddGroupsCmd(username, groups) + if err != nil { + return "", nil, fmt.Errorf("failed to build add groups command: %w", err) + } + case "remove_groups": + var err error + cmd, err = buildRemoveGroupsCmd(tools, username, groups) + if err != nil { + return "", nil, fmt.Errorf("failed to build remove groups command: %w", err) + } + case "check": + cmd = buildCheckUserCmd(username) + default: + return "", nil, fmt.Errorf("unsupported user action: %s", action) + } + + if step.Timeout > 0 && hasCommand(sshClient, "timeout") { + cmd = fmt.Sprintf("timeout %ds %s", step.Timeout, cmd) + } + + output, err := sshClient.RunCommand(cmd) + if err != nil { + return "", nil, fmt.Errorf("failed to execute user command '%s': %w (output: %s)", cmd, err, output) + } + if revertAction == "" { + switch action { + case "ensure": + revertAction = "delete" + case "delete": + revertAction = "ensure" + case "add_groups": + revertAction = "remove_groups" + case "remove_groups": + revertAction = "add_groups" + case "modify": + revertAction = "" // cannot reliably revert without pre-state + case "check": + revertAction = "" // no-op + } + } + var compensate func() + if revertAction != "" { + tools := userTools(sshClient) + var rev string + var rbErr error + switch revertAction { + case "ensure": + rev, rbErr = buildEnsureUserCmd(tools, username, shell, home, groups) + case "delete": + rev, rbErr = buildDeleteUserCmd(tools, username) + case "add_groups": + rev, rbErr = buildAddGroupsCmd(username, groups) + case "remove_groups": + rev, rbErr = buildRemoveGroupsCmd(tools, username, groups) + } + if rbErr == nil && rev != "" { + if step.Timeout > 0 && hasCommand(sshClient, "timeout") { + rev = fmt.Sprintf("timeout %ds %s", step.Timeout, rev) + } + compensate = func() { _, _ = sshClient.RunCommand(rev) } + } + } + return output, compensate, nil +} + +type userToolset struct { + add string + del string + mod string + groupDel string +} + +func userTools(sshClient *ssh.SSH) userToolset { + return userToolset{ + add: firstAvailable(sshClient, "/usr/sbin/useradd", "/usr/bin/useradd", "useradd", "/usr/sbin/adduser", "/usr/bin/adduser", "adduser"), + del: firstAvailable(sshClient, "/usr/sbin/userdel", "/usr/bin/userdel", "userdel", "/usr/sbin/deluser", "/usr/bin/deluser", "deluser"), + mod: firstAvailable(sshClient, "/usr/sbin/usermod", "/usr/bin/usermod", "usermod", "/usr/bin/chsh", "chsh"), + groupDel: firstAvailable(sshClient, "/usr/bin/gpasswd", "gpasswd", "/usr/sbin/deluser", "/usr/bin/deluser", "deluser"), + } +} + +func firstAvailable(sshClient *ssh.SSH, names ...string) string { + for _, n := range names { + if hasCommand(sshClient, n) { + return n + } + } + return "" +} + +func buildEnsureUserCmd(tools userToolset, username string, shell string, home string, groups string) (string, error) { + var cmd string + if strings.Contains(tools.add, "useradd") { + cmd = fmt.Sprintf("id -u %s >/dev/null 2>&1 || sudo useradd %s", username, username) + } else if strings.Contains(tools.add, "adduser") { + cmd = fmt.Sprintf("id -u %s >/dev/null 2>&1 || sudo adduser -D %s", username, username) + } else { + return "", fmt.Errorf("user management tool not found") + } + if shell != "" { + if strings.Contains(tools.mod, "usermod") { + cmd += fmt.Sprintf(" && sudo usermod -s %s %s", shell, username) + } else if strings.Contains(tools.mod, "chsh") { + cmd += fmt.Sprintf(" && sudo chsh -s %s %s", shell, username) + } + } + if home != "" { + cmd += fmt.Sprintf(" && sudo usermod -d %s %s", home, username) + } + if groups != "" { + cmd += fmt.Sprintf(" && sudo usermod -aG %s %s", groups, username) + } + return cmd, nil +} + +func buildDeleteUserCmd(tools userToolset, username string) (string, error) { + if strings.Contains(tools.del, "userdel") { + return fmt.Sprintf("id -u %s >/dev/null 2>&1 && sudo userdel -r %s || true", username, username), nil + } + if strings.Contains(tools.del, "deluser") { + return fmt.Sprintf("id -u %s >/dev/null 2>&1 && sudo deluser --remove-home %s || true", username, username), nil + } + return "", fmt.Errorf("user management tool not found") +} + +func buildModifyUserCmd(username string, shell string, home string, groups string) (string, error) { + parts := []string{} + if shell != "" { + parts = append(parts, fmt.Sprintf("-s %s", shell)) + } + if home != "" { + parts = append(parts, fmt.Sprintf("-d %s", home)) + } + if groups != "" { + parts = append(parts, fmt.Sprintf("-aG %s", groups)) + } + if len(parts) == 0 { + return "", fmt.Errorf("no changes") + } + return fmt.Sprintf("sudo usermod %s %s", strings.Join(parts, " "), username), nil +} + +func buildAddGroupsCmd(username string, groups string) (string, error) { + if groups == "" { + return "", fmt.Errorf("no groups provided") + } + cmds := []string{} + for _, g := range strings.Split(groups, ",") { + g = strings.TrimSpace(g) + if g == "" { + continue + } + cmds = append(cmds, fmt.Sprintf("sudo usermod -aG %s %s", g, username)) + } + if len(cmds) == 0 { + return "", fmt.Errorf("no groups provided") + } + return strings.Join(cmds, " && "), nil +} + +func buildRemoveGroupsCmd(tools userToolset, username string, groups string) (string, error) { + if groups == "" { + return "", fmt.Errorf("no groups provided") + } + cmds := []string{} + for _, g := range strings.Split(groups, ",") { + g = strings.TrimSpace(g) + if g == "" { + continue + } + if strings.Contains(tools.groupDel, "gpasswd") { + cmds = append(cmds, fmt.Sprintf("sudo gpasswd -d %s %s || true", username, g)) + } else if strings.Contains(tools.groupDel, "deluser") { + cmds = append(cmds, fmt.Sprintf("sudo deluser %s %s || true", username, g)) + } + } + if len(cmds) == 0 { + return "", fmt.Errorf("no groups provided") + } + return strings.Join(cmds, " && "), nil +} + +func buildCheckUserCmd(username string) string { + return fmt.Sprintf("id -u %s >/dev/null 2>&1 && echo exists || echo missing", username) +} + +func init() { + RegisterModule(userModule{}) +} diff --git a/api/internal/features/extension/loader/loader.go b/api/internal/features/extension/loader/loader.go new file mode 100644 index 000000000..abfe0e84c --- /dev/null +++ b/api/internal/features/extension/loader/loader.go @@ -0,0 +1,158 @@ +package loader + +import ( + "context" + "fmt" + "log" + "path/filepath" + + "github.com/google/uuid" + "github.com/raghavyuva/nixopus-api/internal/features/extension/parser" + "github.com/raghavyuva/nixopus-api/internal/types" + "github.com/uptrace/bun" +) + +type ExtensionLoader struct { + db *bun.DB +} + +func NewExtensionLoader(db *bun.DB) *ExtensionLoader { + return &ExtensionLoader{ + db: db, + } +} + +func (l *ExtensionLoader) LoadExtensionsFromDirectory(ctx context.Context, dirPath string) error { + parser := parser.NewParser() + + extensions, allVariables, err := parser.LoadExtensionsFromDirectory(dirPath) + if err != nil { + return fmt.Errorf("failed to load extensions from directory: %w", err) + } + + log.Printf("Found %d extension files in %s", len(extensions), dirPath) + + for i, extension := range extensions { + variables := allVariables[i] + + if err := l.upsertExtension(ctx, extension, variables); err != nil { + log.Printf("Failed to upsert extension %s: %v", extension.ExtensionID, err) + continue + } + + // log.Printf("Successfully loaded extension: %s (%s)", extension.Name, extension.ExtensionID) + } + + return nil +} + +func (l *ExtensionLoader) upsertExtension(ctx context.Context, extension *types.Extension, variables []types.ExtensionVariable) error { + tx, err := l.db.BeginTx(ctx, nil) + if err != nil { + return fmt.Errorf("failed to begin transaction: %w", err) + } + defer tx.Rollback() + + var existingExtension types.Extension + err = tx.NewSelect(). + Model(&existingExtension). + Where("extension_id = ?", extension.ExtensionID). + Scan(ctx) + + if err != nil && err.Error() != "sql: no rows in result set" { + return fmt.Errorf("failed to check existing extension: %w", err) + } + + if err == nil { + if existingExtension.ContentHash == extension.ContentHash { + // log.Printf("Extension %s unchanged, skipping", extension.ExtensionID) + return tx.Commit() + } + + extension.ID = existingExtension.ID + extension.CreatedAt = existingExtension.CreatedAt + + if _, err := tx.NewUpdate(). + Model(extension). + Where("id = ?", extension.ID). + Exec(ctx); err != nil { + return fmt.Errorf("failed to update extension: %w", err) + } + + if err := l.deleteExtensionVariables(ctx, tx, extension.ID); err != nil { + return fmt.Errorf("failed to delete old variables: %w", err) + } + } else { + extension.ID = uuid.New() + + if _, err := tx.NewInsert(). + Model(extension). + Exec(ctx); err != nil { + return fmt.Errorf("failed to insert extension: %w", err) + } + } + + for i := range variables { + variables[i].ID = uuid.New() + variables[i].ExtensionID = extension.ID + } + + if len(variables) > 0 { + if _, err := tx.NewInsert(). + Model(&variables). + Exec(ctx); err != nil { + return fmt.Errorf("failed to insert variables: %w", err) + } + } + + return tx.Commit() +} + +func (l *ExtensionLoader) deleteExtensionVariables(ctx context.Context, tx bun.Tx, extensionID uuid.UUID) error { + _, err := tx.NewDelete(). + Model((*types.ExtensionVariable)(nil)). + Where("extension_id = ?", extensionID). + Exec(ctx) + return err +} + +func (l *ExtensionLoader) LoadExtensionsFromTemplates(ctx context.Context) error { + templatesPath := filepath.Join(".", "templates") + return l.LoadExtensionsFromDirectory(ctx, templatesPath) +} + +func (l *ExtensionLoader) GetExtensionByID(ctx context.Context, extensionID string) (*types.Extension, error) { + var extension types.Extension + + err := l.db.NewSelect(). + Model(&extension). + Relation("Variables"). + Where("extension_id = ? AND deleted_at IS NULL", extensionID). + Scan(ctx) + + if err != nil { + return nil, fmt.Errorf("failed to get extension: %w", err) + } + + return &extension, nil +} + +func (l *ExtensionLoader) ListExtensions(ctx context.Context, category *types.ExtensionCategory) ([]types.Extension, error) { + var extensions []types.Extension + + query := l.db.NewSelect(). + Model(&extensions). + Relation("Variables"). + Where("deleted_at IS NULL") + + if category != nil { + query = query.Where("category = ?", *category) + } + + err := query.Scan(ctx) + if err != nil { + return nil, fmt.Errorf("failed to list extensions: %w", err) + } + + return extensions, nil +} diff --git a/api/internal/features/extension/parser/parse.go b/api/internal/features/extension/parser/parse.go new file mode 100644 index 000000000..b2eb7b267 --- /dev/null +++ b/api/internal/features/extension/parser/parse.go @@ -0,0 +1,124 @@ +package parser + +import ( + "crypto/sha256" + "encoding/hex" + "encoding/json" + "fmt" + "io/ioutil" + "path/filepath" + + "github.com/raghavyuva/nixopus-api/internal/types" + "gopkg.in/yaml.v3" +) + +func NewParser() *Parser { + return &Parser{} +} + +func (p *Parser) ParseExtensionFile(filePath string) (*types.Extension, []types.ExtensionVariable, error) { + data, err := ioutil.ReadFile(filePath) + if err != nil { + return nil, nil, fmt.Errorf("failed to read file %s: %w", filePath, err) + } + + var extYAML ExtensionYAML + if err := yaml.Unmarshal(data, &extYAML); err != nil { + return nil, nil, fmt.Errorf("failed to parse YAML: %w", err) + } + + if err := p.validateExtension(&extYAML); err != nil { + return nil, nil, fmt.Errorf("validation failed: %w", err) + } + + extension := p.convertToExtension(&extYAML, string(data)) + variables := p.convertToVariables(&extYAML, extension.ExtensionID) + + return extension, variables, nil +} + +func (p *Parser) ParseExtensionContent(content string) (*types.Extension, []types.ExtensionVariable, error) { + var extYAML ExtensionYAML + if err := yaml.Unmarshal([]byte(content), &extYAML); err != nil { + return nil, nil, fmt.Errorf("failed to parse YAML: %w", err) + } + if err := p.validateExtension(&extYAML); err != nil { + return nil, nil, fmt.Errorf("validation failed: %w", err) + } + extension := p.convertToExtension(&extYAML, content) + variables := p.convertToVariables(&extYAML, extension.ExtensionID) + return extension, variables, nil +} + +func (p *Parser) convertToExtension(extYAML *ExtensionYAML, yamlContent string) *types.Extension { + parsedContent, err := json.Marshal(extYAML) + if err != nil { + parsedContent = []byte("{}") + } + hash := sha256.Sum256([]byte(yamlContent)) + + return &types.Extension{ + ExtensionID: extYAML.Metadata.ID, + Name: extYAML.Metadata.Name, + Description: extYAML.Metadata.Description, + Author: extYAML.Metadata.Author, + Icon: extYAML.Metadata.Icon, + Category: types.ExtensionCategory(extYAML.Metadata.Category), + ExtensionType: types.ExtensionType(extYAML.Metadata.Type), + Version: extYAML.Metadata.Version, + IsVerified: extYAML.Metadata.IsVerified, + YAMLContent: yamlContent, + ParsedContent: string(parsedContent), + ContentHash: hex.EncodeToString(hash[:]), + ValidationStatus: types.ValidationStatusValid, + } +} + +func (p *Parser) convertToVariables(extYAML *ExtensionYAML, extensionID string) []types.ExtensionVariable { + var variables []types.ExtensionVariable + + for varName, variable := range extYAML.Variables { + defaultValueJSON, err := json.Marshal(variable.Default) + if err != nil { + defaultValueJSON = []byte("null") + } + + variables = append(variables, types.ExtensionVariable{ + VariableName: varName, + VariableType: variable.Type, + Description: variable.Description, + DefaultValue: json.RawMessage(defaultValueJSON), + IsRequired: variable.IsRequired, + ValidationPattern: variable.ValidationPattern, + }) + } + + return variables +} + +func (p *Parser) LoadExtensionsFromDirectory(dirPath string) ([]*types.Extension, [][]types.ExtensionVariable, error) { + var extensions []*types.Extension + var allVariables [][]types.ExtensionVariable + + files, err := filepath.Glob(filepath.Join(dirPath, "*.yaml")) + if err != nil { + return nil, nil, fmt.Errorf("failed to read directory: %w", err) + } + + for _, file := range files { + // Skip rfc.yaml file + if filepath.Base(file) == "rfc.yaml" { + continue + } + + extension, variables, err := p.ParseExtensionFile(file) + if err != nil { + return nil, nil, fmt.Errorf("failed to parse %s: %w", file, err) + } + + extensions = append(extensions, extension) + allVariables = append(allVariables, variables) + } + + return extensions, allVariables, nil +} diff --git a/api/internal/features/extension/parser/types.go b/api/internal/features/extension/parser/types.go new file mode 100644 index 000000000..a6b571048 --- /dev/null +++ b/api/internal/features/extension/parser/types.go @@ -0,0 +1,43 @@ +package parser + +type ExtensionMetadata struct { + ID string `yaml:"id"` + Name string `yaml:"name"` + Description string `yaml:"description"` + Author string `yaml:"author"` + Icon string `yaml:"icon"` + Category string `yaml:"category"` + Type string `yaml:"type"` + Version string `yaml:"version"` + IsVerified bool `yaml:"isVerified"` +} + +type ExtensionVariable struct { + Type string `yaml:"type"` + Description string `yaml:"description"` + Default interface{} `yaml:"default"` + IsRequired bool `yaml:"is_required"` + ValidationPattern string `yaml:"validation_pattern"` +} + +type ExecutionStep struct { + Name string `yaml:"name"` + Type string `yaml:"type"` + Properties map[string]interface{} `yaml:"properties"` + Conditions []string `yaml:"conditions,omitempty"` + IgnoreErrors bool `yaml:"ignore_errors,omitempty"` + Timeout int `yaml:"timeout,omitempty"` +} + +type ExtensionExecution struct { + Run []ExecutionStep `yaml:"run"` + Validate []ExecutionStep `yaml:"validate"` +} + +type ExtensionYAML struct { + Metadata ExtensionMetadata `yaml:"metadata"` + Variables map[string]ExtensionVariable `yaml:"variables"` + Execution ExtensionExecution `yaml:"execution"` +} + +type Parser struct{} diff --git a/api/internal/features/extension/parser/validate.go b/api/internal/features/extension/parser/validate.go new file mode 100644 index 000000000..55bfb8394 --- /dev/null +++ b/api/internal/features/extension/parser/validate.go @@ -0,0 +1,229 @@ +package parser + +import ( + "fmt" + "strings" +) + +func (p *Parser) validateExtension(ext *ExtensionYAML) error { + if ext.Metadata.ID == "" { + return fmt.Errorf("metadata.id is required") + } + if ext.Metadata.Name == "" { + return fmt.Errorf("metadata.name is required") + } + if ext.Metadata.Description == "" { + return fmt.Errorf("metadata.description is required") + } + if ext.Metadata.Author == "" { + return fmt.Errorf("metadata.author is required") + } + if ext.Metadata.Icon == "" { + return fmt.Errorf("metadata.icon is required") + } + if ext.Metadata.Category == "" { + return fmt.Errorf("metadata.category is required") + } + + if ext.Metadata.Type == "" { + return fmt.Errorf("metadata.type is required (install or run)") + } + if ext.Metadata.Type != "install" && ext.Metadata.Type != "run" { + return fmt.Errorf("invalid metadata.type: %s", ext.Metadata.Type) + } + + if !p.isValidCategory(ext.Metadata.Category) { + return fmt.Errorf("invalid category: %s", ext.Metadata.Category) + } + + if !p.isValidExtensionID(ext.Metadata.ID) { + return fmt.Errorf("invalid extension_id format: %s", ext.Metadata.ID) + } + + if ext.Metadata.Version != "" && !p.isValidVersion(ext.Metadata.Version) { + return fmt.Errorf("invalid version format: %s", ext.Metadata.Version) + } + + for varName, variable := range ext.Variables { + if !p.isValidVariableName(varName) { + return fmt.Errorf("invalid variable name: %s", varName) + } + if !p.isValidVariableType(variable.Type) { + return fmt.Errorf("invalid variable type for %s: %s", varName, variable.Type) + } + } + + if len(ext.Execution.Run) == 0 && len(ext.Execution.Validate) == 0 { + return fmt.Errorf("execution must have at least one step") + } + for _, step := range append([]ExecutionStep{}, ext.Execution.Run...) { + if err := p.validateStep(step); err != nil { + return err + } + } + for _, step := range append([]ExecutionStep{}, ext.Execution.Validate...) { + if err := p.validateStep(step); err != nil { + return err + } + } + + return nil +} + +func (p *Parser) validateStep(step ExecutionStep) error { + if strings.TrimSpace(step.Name) == "" { + return fmt.Errorf("execution step name is required") + } + validators := map[string]func(ExecutionStep) error{ + "command": p.validateCommandStep, + "file": p.validateFileStep, + "service": p.validateServiceStep, + "user": p.validateUserStep, + "docker": p.validateDockerStep, + "docker_compose": p.validateDockerComposeStep, + } + v, ok := validators[step.Type] + if !ok { + return fmt.Errorf("invalid execution step type: %s", step.Type) + } + if err := v(step); err != nil { + return err + } + if step.Timeout < 0 { + return fmt.Errorf("timeout cannot be negative") + } + return nil +} + +func (p *Parser) validateCommandStep(step ExecutionStep) error { + return p.requireProps(step, map[string]bool{"cmd": true}) +} + +func (p *Parser) validateFileStep(step ExecutionStep) error { + if err := p.requireProps(step, map[string]bool{"action": true}); err != nil { + return err + } + action, _ := step.Properties["action"].(string) + if action == "mkdir" { + return p.requireProps(step, map[string]bool{"dest": true}) + } + return p.requireProps(step, map[string]bool{"src": true, "dest": true}) +} + +func (p *Parser) validateServiceStep(step ExecutionStep) error { + return p.requireProps(step, map[string]bool{"name": true, "action": true}) +} + +func (p *Parser) validateUserStep(step ExecutionStep) error { + return p.requireProps(step, map[string]bool{"username": true, "action": true}) +} + +func (p *Parser) validateDockerStep(step ExecutionStep) error { + if err := p.requireProps(step, map[string]bool{"action": true}); err != nil { + return err + } + action, _ := step.Properties["action"].(string) + switch action { + case "pull": + return p.requireProps(step, map[string]bool{"image": true}) + case "run": + // name and image are required; optional: tag, ports, restart, env, volumes, networks, cmd + return p.requireProps(step, map[string]bool{"name": true, "image": true}) + case "stop", "start", "rm": + return p.requireProps(step, map[string]bool{"name": true}) + default: + return fmt.Errorf("unsupported docker action: %s", action) + } +} + +func (p *Parser) validateDockerComposeStep(step ExecutionStep) error { + if err := p.requireProps(step, map[string]bool{"action": true, "file": true}); err != nil { + return err + } + action, _ := step.Properties["action"].(string) + switch action { + case "up", "down", "build": + return nil + default: + return fmt.Errorf("unsupported docker_compose action: %s", action) + } +} + +func (p *Parser) requireProps(step ExecutionStep, required map[string]bool) error { + for key := range required { + if _, ok := step.Properties[key]; !ok { + return fmt.Errorf("%s step requires '%s' property", step.Type, key) + } + } + return nil +} + +func (p *Parser) isValidCategory(category string) bool { + validCategories := []string{ + "Security", "Containers", "Database", "Web Server", + "Maintenance", "Monitoring", "Storage", "Network", + "Development", "Other", + } + for _, valid := range validCategories { + if category == valid { + return true + } + } + return false +} + +func (p *Parser) isValidExtensionID(id string) bool { + if len(id) < 3 || len(id) > 50 { + return false + } + for _, char := range id { + if !((char >= 'a' && char <= 'z') || (char >= '0' && char <= '9') || char == '-') { + return false + } + } + return !strings.HasPrefix(id, "-") && !strings.HasSuffix(id, "-") +} + +func (p *Parser) isValidVersion(version string) bool { + parts := strings.Split(version, ".") + if len(parts) != 3 { + return false + } + for _, part := range parts { + if len(part) == 0 { + return false + } + for _, char := range part { + if char < '0' || char > '9' { + return false + } + } + } + return true +} + +func (p *Parser) isValidVariableName(name string) bool { + if len(name) == 0 || len(name) > 100 { + return false + } + if !((name[0] >= 'a' && name[0] <= 'z') || (name[0] >= 'A' && name[0] <= 'Z') || name[0] == '_') { + return false + } + for _, char := range name { + if !((char >= 'a' && char <= 'z') || (char >= 'A' && char <= 'Z') || + (char >= '0' && char <= '9') || char == '_') { + return false + } + } + return true +} + +func (p *Parser) isValidVariableType(varType string) bool { + validTypes := []string{"string", "integer", "boolean", "array"} + for _, valid := range validTypes { + if varType == valid { + return true + } + } + return false +} diff --git a/api/internal/features/extension/service/context.go b/api/internal/features/extension/service/context.go new file mode 100644 index 000000000..6db8f824c --- /dev/null +++ b/api/internal/features/extension/service/context.go @@ -0,0 +1,48 @@ +package service + +import ( + "github.com/raghavyuva/nixopus-api/internal/features/ssh" + "github.com/raghavyuva/nixopus-api/internal/types" +) + +type RunContext struct { + Exec *types.ExtensionExecution + Spec types.ExtensionSpec + Vars map[string]interface{} + SSH *ssh.SSH + Steps []types.ExecutionStep + // Compensations is a stack of revert functions for previously completed steps + compensations []func() + rolledBack bool +} + +func NewRunContext(exec *types.ExtensionExecution, spec types.ExtensionSpec, vars map[string]interface{}, sshClient *ssh.SSH, steps []types.ExecutionStep) *RunContext { + return &RunContext{ + Exec: exec, + Spec: spec, + Vars: vars, + SSH: sshClient, + Steps: steps, + compensations: make([]func(), 0, 8), + } +} + +// pushCompensation adds a revert function to the stack +func (c *RunContext) pushCompensation(fn func()) { + if fn == nil { + return + } + c.compensations = append(c.compensations, fn) +} + +// rollback executes compensations in reverse order, ignoring errors +func (c *RunContext) rollback() { + if c.rolledBack { + return + } + for i := len(c.compensations) - 1; i >= 0; i-- { + c.compensations[i]() + } + c.compensations = nil + c.rolledBack = true +} diff --git a/api/internal/features/extension/service/executor.go b/api/internal/features/extension/service/executor.go new file mode 100644 index 000000000..518f305fc --- /dev/null +++ b/api/internal/features/extension/service/executor.go @@ -0,0 +1,173 @@ +package service + +import ( + "fmt" + "time" + + "github.com/raghavyuva/nixopus-api/internal/features/extension/engine" + "github.com/raghavyuva/nixopus-api/internal/features/logger" + "github.com/raghavyuva/nixopus-api/internal/types" +) + +type StepOutcome struct { + Step *types.ExecutionStep + Output string + Err error + Ignore bool +} + +func (s *ExtensionService) executeRun(ctx *RunContext) { + ctx.Exec.StartedAt = time.Now() + _ = s.storage.UpdateExecution(ctx.Exec) + + s.logger.Log(logger.Info, fmt.Sprintf("Starting extension execution: %s", ctx.Exec.ID.String()), "") + s.appendLog(ctx.Exec.ID, nil, "info", "execution_started", map[string]interface{}{}) + + steps := ctx.Steps + + if stop := s.processPhase(ctx, &steps, "run", ctx.Spec.Execution.Run, 0); stop { + s.logger.Log(logger.Error, fmt.Sprintf("Extension execution failed during run phase: %s", ctx.Exec.ID.String()), "") + return + } + if stop := s.processPhase(ctx, &steps, "validate", ctx.Spec.Execution.Validate, len(ctx.Spec.Execution.Run)); stop { + s.logger.Log(logger.Error, fmt.Sprintf("Extension execution failed during validate phase: %s", ctx.Exec.ID.String()), "") + return + } + + ctx.Exec.Status = types.ExecutionStatusCompleted + finished := time.Now() + ctx.Exec.CompletedAt = &finished + _ = s.storage.UpdateExecution(ctx.Exec) + + s.logger.Log(logger.Info, fmt.Sprintf("Extension execution completed successfully: %s", ctx.Exec.ID.String()), "") + s.appendLog(ctx.Exec.ID, nil, "info", "execution_completed", map[string]interface{}{"status": ctx.Exec.Status}) +} + +func (s *ExtensionService) processPhase( + ctx *RunContext, + steps *[]types.ExecutionStep, + phase string, + specSteps []types.SpecStep, + offset int, +) bool { + s.logger.Log(logger.Info, fmt.Sprintf("Processing phase: %s with %d spec steps, offset: %d", phase, len(specSteps), offset), "") + s.logger.Log(logger.Info, fmt.Sprintf("Available steps: %d", len(*steps)), "") + + for idx := range specSteps { + if s.shouldCancel(ctx) { + s.markCancelled(ctx) + return true + } + + stepOrder := offset + idx + 1 + step := s.getStepByPhaseAndOrder(steps, phase, stepOrder) + if step == nil { + s.logger.Log(logger.Error, fmt.Sprintf("Step not found for phase: %s, order: %d", phase, stepOrder), "") + continue + } + s.beginStep(step) + + out, runErr := s.executeSpecStep(ctx, specSteps[idx]) + step.Output = out + outcome := StepOutcome{Step: step, Output: out, Err: runErr, Ignore: specSteps[idx].IgnoreErrors} + if s.finalizeStep(ctx, outcome) { + return true + } + } + return false +} + +func (s *ExtensionService) shouldCancel(ctx *RunContext) bool { + cur, err := s.storage.GetExecutionByID(ctx.Exec.ID.String()) + return err == nil && cur.Status == types.ExecutionStatusCancelled +} + +func (s *ExtensionService) markCancelled(ctx *RunContext) { + ctx.Exec.Status = types.ExecutionStatusCancelled + finished := time.Now() + ctx.Exec.CompletedAt = &finished + _ = s.storage.UpdateExecution(ctx.Exec) +} + +func (s *ExtensionService) beginStep(step *types.ExecutionStep) { + step.Status = types.ExecutionStatusRunning + step.StartedAt = time.Now() + + // Log to console + s.logger.Log(logger.Info, fmt.Sprintf("STEP STARTED: %s", step.StepName), "") + sid := step.ID + s.appendLog(step.ExecutionID, &sid, "info", "step_started", map[string]interface{}{"step_name": step.StepName, "phase": step.Phase, "order": step.StepOrder}) + + _ = s.storage.UpdateExecutionStep(step) +} + +func (s *ExtensionService) finalizeStep(ctx *RunContext, outcome StepOutcome) bool { + if outcome.Err == nil { + outcome.Step.Status = types.ExecutionStatusCompleted + completed := time.Now() + outcome.Step.CompletedAt = &completed + successLog := fmt.Sprintf("[%s] STEP COMPLETED: %s\nOutput: %s", + completed.Format("2006-01-02 15:04:05"), + outcome.Step.StepName, + outcome.Output) + s.logger.Log(logger.Info, fmt.Sprintf("STEP COMPLETED: %s - %s", outcome.Step.StepName, outcome.Output), "") + sid := outcome.Step.ID + s.appendLog(ctx.Exec.ID, &sid, "info", "step_completed", map[string]interface{}{"step_name": outcome.Step.StepName, "output": outcome.Output}) + ctx.Exec.ExecutionLog = ctx.Exec.ExecutionLog + "\n" + successLog + _ = s.storage.UpdateExecutionStep(outcome.Step) + _ = s.storage.UpdateExecution(ctx.Exec) + return false + } + + outcome.Step.Status = types.ExecutionStatusFailed + completed := time.Now() + outcome.Step.CompletedAt = &completed + errorLog := fmt.Sprintf("[%s] STEP FAILED: %s\nError: %v\nOutput: %s", + completed.Format("2006-01-02 15:04:05"), + outcome.Step.StepName, + outcome.Err, + outcome.Output) + s.logger.Log(logger.Error, fmt.Sprintf("STEP FAILED: %s - Error: %v, Output: %s", outcome.Step.StepName, outcome.Err, outcome.Output), "") + sid := outcome.Step.ID + s.appendLog(ctx.Exec.ID, &sid, "error", "step_failed", map[string]interface{}{"step_name": outcome.Step.StepName, "error": outcome.Err.Error(), "output": outcome.Output}) + ctx.Exec.ExecutionLog = ctx.Exec.ExecutionLog + "\n" + errorLog + _ = s.storage.UpdateExecutionStep(outcome.Step) + _ = s.storage.UpdateExecution(ctx.Exec) + + if outcome.Ignore { + return false + } + // perform rollback for prior successful steps + ctx.rollback() + ctx.Exec.Status = types.ExecutionStatusFailed + ctx.Exec.ErrorMessage = outcome.Err.Error() + finished := time.Now() + ctx.Exec.CompletedAt = &finished + _ = s.storage.UpdateExecution(ctx.Exec) + return true +} + +func (s *ExtensionService) getStepByPhaseAndOrder(steps *[]types.ExecutionStep, phase string, order int) *types.ExecutionStep { + s.logger.Log(logger.Info, fmt.Sprintf("Looking for step: phase=%s, order=%d", phase, order), "") + for i := range *steps { + st := &(*steps)[i] + s.logger.Log(logger.Info, fmt.Sprintf("Checking step: phase=%s, order=%d, name=%s", st.Phase, st.StepOrder, st.StepName), "") + if st.Phase == phase && st.StepOrder == order { + s.logger.Log(logger.Info, fmt.Sprintf("Found step: %s", st.StepName), "") + return st + } + } + s.logger.Log(logger.Error, fmt.Sprintf("Step not found: phase=%s, order=%d", phase, order), "") + return nil +} + +func (s *ExtensionService) executeSpecStep(ctx *RunContext, spec types.SpecStep) (string, error) { + if m := engine.GetModule(spec.Type); m != nil { + out, comp, err := m.Execute(ctx.SSH, spec, ctx.Vars) + if err == nil && comp != nil { + ctx.pushCompensation(comp) + } + return out, err + } + return "", fmt.Errorf("unsupported step type: %s", spec.Type) +} diff --git a/api/internal/features/extension/service/run_extension.go b/api/internal/features/extension/service/run_extension.go new file mode 100644 index 000000000..f2e914a23 --- /dev/null +++ b/api/internal/features/extension/service/run_extension.go @@ -0,0 +1,75 @@ +package service + +import ( + "encoding/json" + "fmt" + + "github.com/raghavyuva/nixopus-api/internal/features/logger" + "github.com/raghavyuva/nixopus-api/internal/features/ssh" + "github.com/raghavyuva/nixopus-api/internal/types" +) + +func (s *ExtensionService) StartRun(extensionID string, variableValues map[string]interface{}) (*types.ExtensionExecution, error) { + ext, err := s.storage.GetExtensionByID(extensionID) + if err != nil { + return nil, err + } + + s.logger.Log(logger.Info, fmt.Sprintf("Extension ParsedContent: %s", ext.ParsedContent), "") + + var spec types.ExtensionSpec + var jsonString string + if err := json.Unmarshal([]byte(ext.ParsedContent), &jsonString); err != nil { + s.logger.Log(logger.Error, fmt.Sprintf("Failed to unmarshal JSON string: %v", err), "") + return nil, err + } + + if err := json.Unmarshal([]byte(jsonString), &spec); err != nil { + s.logger.Log(logger.Error, fmt.Sprintf("Failed to unmarshal extension spec: %v", err), "") + return nil, err + } + + s.logger.Log(logger.Info, fmt.Sprintf("Parsed spec - Run steps: %d, Validate steps: %d", len(spec.Execution.Run), len(spec.Execution.Validate)), "") + + varsJSON, _ := json.Marshal(variableValues) + exec := &types.ExtensionExecution{ + ExtensionID: ext.ID, + VariableValues: string(varsJSON), + Status: types.ExecutionStatusRunning, + } + if err := s.storage.CreateExecution(exec); err != nil { + s.logger.Log(logger.Error, err.Error(), "") + return nil, err + } + + var steps []types.ExecutionStep + order := 1 + for _, st := range spec.Execution.Run { + steps = append(steps, types.ExecutionStep{ + ExecutionID: exec.ID, + StepName: st.Name, + Phase: "run", + StepOrder: order, + Status: types.ExecutionStatusPending, + }) + order++ + } + for _, st := range spec.Execution.Validate { + steps = append(steps, types.ExecutionStep{ + ExecutionID: exec.ID, + StepName: st.Name, + Phase: "validate", + StepOrder: order, + Status: types.ExecutionStatusPending, + }) + order++ + } + if err := s.storage.CreateExecutionSteps(steps); err != nil { + s.logger.Log(logger.Error, err.Error(), "") + return nil, err + } + + ctx := NewRunContext(exec, spec, variableValues, ssh.NewSSH(), steps) + go s.executeRun(ctx) + return exec, nil +} diff --git a/api/internal/features/extension/service/service.go b/api/internal/features/extension/service/service.go new file mode 100644 index 000000000..cc872b079 --- /dev/null +++ b/api/internal/features/extension/service/service.go @@ -0,0 +1,285 @@ +package service + +import ( + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "os" + "path/filepath" + "time" + + "github.com/google/uuid" + "github.com/raghavyuva/nixopus-api/internal/features/extension/parser" + "github.com/raghavyuva/nixopus-api/internal/features/extension/storage" + "github.com/raghavyuva/nixopus-api/internal/features/logger" + shared_storage "github.com/raghavyuva/nixopus-api/internal/storage" + "github.com/raghavyuva/nixopus-api/internal/types" +) + +type ExtensionService struct { + store *shared_storage.Store + storage storage.ExtensionStorageInterface + ctx context.Context + logger logger.Logger +} + +func NewExtensionService( + store *shared_storage.Store, + ctx context.Context, + l logger.Logger, + storage storage.ExtensionStorageInterface, +) *ExtensionService { + return &ExtensionService{ + store: store, + storage: storage, + ctx: ctx, + logger: l, + } +} + +func (s *ExtensionService) CreateExtension(extension *types.Extension) error { + if err := s.storage.CreateExtension(extension); err != nil { + s.logger.Log(logger.Error, err.Error(), "") + return err + } + return nil +} + +func (s *ExtensionService) GetExtension(id string) (*types.Extension, error) { + extension, err := s.storage.GetExtension(id) + if err != nil { + s.logger.Log(logger.Error, err.Error(), "") + return nil, err + } + return extension, nil +} + +func (s *ExtensionService) GetExtensionByID(extensionID string) (*types.Extension, error) { + extension, err := s.storage.GetExtensionByID(extensionID) + if err != nil { + s.logger.Log(logger.Error, err.Error(), "") + return nil, err + } + return extension, nil +} + +func (s *ExtensionService) UpdateExtension(extension *types.Extension) error { + if err := s.storage.UpdateExtension(extension); err != nil { + s.logger.Log(logger.Error, err.Error(), "") + return err + } + return nil +} + +func (s *ExtensionService) DeleteExtension(id string) error { + if err := s.storage.DeleteExtension(id); err != nil { + s.logger.Log(logger.Error, err.Error(), "") + return err + } + return nil +} + +func (s *ExtensionService) DeleteFork(id string) error { + ext, err := s.storage.GetExtension(id) + if err != nil { + return err + } + if ext.ParentExtensionID == nil { + return fmt.Errorf("only forked extensions can be removed") + } + return s.storage.DeleteExtension(id) +} + +func (s *ExtensionService) ForkExtension(extensionID string, yamlOverride string, authorName string) (*types.Extension, error) { + src, err := s.storage.GetExtensionByID(extensionID) + if err != nil { + return nil, err + } + // Disallow forking a forked extension + if src.ParentExtensionID != nil { + return nil, fmt.Errorf("forking a forked extension is not allowed") + } + fork := *src + fork.ID = uuid.UUID{} + fork.ParentExtensionID = &src.ID + fork.Name = src.Name + " (Fork)" + // ensure unique extension_id for fork + fork.ExtensionID = src.ExtensionID + "-fork-" + time.Now().Format("20060102150405") + fork.CreatedAt = time.Now() + fork.UpdatedAt = time.Now() + fork.DeletedAt = nil + + if yamlOverride != "" { + p := parser.NewParser() + ext, variables, err := p.ParseExtensionContent(yamlOverride) + if err != nil { + return nil, err + } + fork.Name = ext.Name + fork.Description = ext.Description + if authorName != "" { + fork.Author = authorName + } else { + fork.Author = ext.Author + } + fork.Icon = ext.Icon + fork.Category = ext.Category + fork.ExtensionType = ext.ExtensionType + fork.Version = ext.Version + fork.IsVerified = false + fork.YAMLContent = yamlOverride + fork.ParsedContent = ext.ParsedContent + fork.ContentHash = ext.ContentHash + + if authorName != "" { + fork.Author = authorName + } + if err := s.storage.CreateExtension(&fork); err != nil { + return nil, err + } + if len(variables) > 0 { + for i := range variables { + variables[i].ExtensionID = fork.ID + } + if err := s.storage.CreateExtensionVariables(variables); err != nil { + return nil, err + } + } + return &fork, nil + } + + if err := s.storage.CreateExtension(&fork); err != nil { + return nil, err + } + + // copy variables + if len(src.Variables) > 0 { + vars := make([]types.ExtensionVariable, 0, len(src.Variables)) + for _, v := range src.Variables { + vars = append(vars, types.ExtensionVariable{ + ExtensionID: fork.ID, + VariableName: v.VariableName, + VariableType: v.VariableType, + Description: v.Description, + DefaultValue: v.DefaultValue, + IsRequired: v.IsRequired, + ValidationPattern: v.ValidationPattern, + CreatedAt: time.Now(), + }) + } + if err := s.storage.CreateExtensionVariables(vars); err != nil { + return nil, err + } + } + return &fork, nil +} + +func (s *ExtensionService) ListExtensions(params types.ExtensionListParams) (*types.ExtensionListResponse, error) { + response, err := s.storage.ListExtensions(params) + if err != nil { + s.logger.Log(logger.Error, err.Error(), "") + return nil, err + } + return response, nil +} + +func (s *ExtensionService) ListCategories() ([]types.ExtensionCategory, error) { + cats, err := s.storage.ListCategories() + if err != nil { + s.logger.Log(logger.Error, err.Error(), "") + return nil, err + } + return cats, nil +} + +func (s *ExtensionService) ParseMultipartRunRequest(r *http.Request) (map[string]interface{}, error) { + if err := r.ParseMultipartForm(32 << 20); err != nil { + return nil, err + } + vars := map[string]interface{}{} + if raw := r.FormValue("variables"); raw != "" { + _ = json.Unmarshal([]byte(raw), &vars) + } + file, header, err := r.FormFile("file") + if err == nil && file != nil { + defer file.Close() + tmpDir := os.TempDir() + tmpPath := filepath.Join(tmpDir, header.Filename) + out, err := os.Create(tmpPath) + if err != nil { + return nil, err + } + defer out.Close() + if _, err := io.Copy(out, file); err != nil { + return nil, err + } + vars["uploaded_file_path"] = tmpPath + } + return vars, err +} + +func (s *ExtensionService) CancelExecution(id string) error { + exec, err := s.storage.GetExecutionByID(id) + if err != nil { + return err + } + if exec.Status == types.ExecutionStatusCompleted || exec.Status == types.ExecutionStatusFailed { + return nil + } + now := time.Now() + exec.Status = types.ExecutionStatusCancelled + exec.CompletedAt = &now + return s.storage.UpdateExecution(exec) +} + +func (s *ExtensionService) GetExecutionByID(id string) (*types.ExtensionExecution, error) { + exec, err := s.storage.GetExecutionByID(id) + if err != nil { + s.logger.Log(logger.Error, err.Error(), "") + return nil, err + } + return exec, nil +} + +func (s *ExtensionService) ListExecutionsByExtensionID(extensionID string) ([]types.ExtensionExecution, error) { + execs, err := s.storage.ListExecutionsByExtensionID(extensionID) + if err != nil { + s.logger.Log(logger.Error, err.Error(), "") + return nil, err + } + return execs, nil +} + +func (s *ExtensionService) appendLog(executionID uuid.UUID, stepID *uuid.UUID, level string, message string, data map[string]interface{}) { + seq, err := s.storage.NextLogSequence(executionID.String()) + if err != nil { + s.logger.Log(logger.Error, fmt.Sprintf("failed to get next log sequence: %v", err), "") + return + } + var payload []byte + if data != nil { + payload, _ = json.Marshal(data) + } else { + payload = []byte("{}") + } + log := &types.ExtensionLog{ + ExecutionID: executionID, + StepID: stepID, + Level: level, + Message: message, + Data: payload, + Sequence: seq, + } + _ = s.storage.CreateExtensionLog(log) +} + +func (s *ExtensionService) ListExecutionLogs(executionID string, afterSeq int64, limit int) ([]types.ExtensionLog, error) { + logs, err := s.storage.ListExtensionLogs(executionID, afterSeq, limit) + if err != nil { + s.logger.Log(logger.Error, err.Error(), "") + return nil, err + } + return logs, nil +} diff --git a/api/internal/features/extension/storage/storage.go b/api/internal/features/extension/storage/storage.go new file mode 100644 index 000000000..f37de60ef --- /dev/null +++ b/api/internal/features/extension/storage/storage.go @@ -0,0 +1,360 @@ +package storage + +import ( + "context" + "database/sql" + "errors" + + "github.com/raghavyuva/nixopus-api/internal/types" + "github.com/uptrace/bun" +) + +type ExtensionStorage struct { + DB *bun.DB + Ctx context.Context + tx *bun.Tx +} + +type ExtensionStorageInterface interface { + CreateExtension(extension *types.Extension) error + CreateExtensionVariables(vars []types.ExtensionVariable) error + GetExtension(id string) (*types.Extension, error) + GetExtensionByID(extensionID string) (*types.Extension, error) + UpdateExtension(extension *types.Extension) error + DeleteExtension(id string) error + ListExtensions(params types.ExtensionListParams) (*types.ExtensionListResponse, error) + ListCategories() ([]types.ExtensionCategory, error) + CreateExecution(exec *types.ExtensionExecution) error + CreateExecutionSteps(steps []types.ExecutionStep) error + ListExecutionSteps(executionID string) ([]types.ExecutionStep, error) + UpdateExecutionStep(step *types.ExecutionStep) error + UpdateExecution(exec *types.ExtensionExecution) error + GetExecutionByID(id string) (*types.ExtensionExecution, error) + ListExecutionsByExtensionID(extensionID string) ([]types.ExtensionExecution, error) + CreateExtensionLog(log *types.ExtensionLog) error + CreateExtensionLogs(logs []types.ExtensionLog) error + ListExtensionLogs(executionID string, afterSeq int64, limit int) ([]types.ExtensionLog, error) + NextLogSequence(executionID string) (int64, error) + BeginTx() (bun.Tx, error) + WithTx(tx bun.Tx) ExtensionStorageInterface +} + +func (s *ExtensionStorage) BeginTx() (bun.Tx, error) { + return s.DB.BeginTx(s.Ctx, nil) +} + +func (s *ExtensionStorage) WithTx(tx bun.Tx) ExtensionStorageInterface { + return &ExtensionStorage{ + DB: s.DB, + Ctx: s.Ctx, + tx: &tx, + } +} + +func (s *ExtensionStorage) getDB() bun.IDB { + if s.tx != nil { + return *s.tx + } + return s.DB +} + +func (s *ExtensionStorage) CreateExtension(extension *types.Extension) error { + _, err := s.getDB().NewInsert().Model(extension).Exec(s.Ctx) + if err != nil { + return err + } + return nil +} + +func (s *ExtensionStorage) CreateExtensionVariables(vars []types.ExtensionVariable) error { + if len(vars) == 0 { + return nil + } + _, err := s.getDB().NewInsert().Model(&vars).Exec(s.Ctx) + if err != nil { + return err + } + return nil +} + +func (s *ExtensionStorage) GetExtension(id string) (*types.Extension, error) { + var extension types.Extension + err := s.getDB().NewSelect(). + Model(&extension). + Relation("Variables"). + Where("id = ? AND deleted_at IS NULL", id). + Scan(s.Ctx) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + return nil, errors.New("extension not found") + } + return nil, err + } + return &extension, nil +} + +func (s *ExtensionStorage) GetExtensionByID(extensionID string) (*types.Extension, error) { + var extension types.Extension + err := s.getDB().NewSelect(). + Model(&extension). + Relation("Variables"). + Where("extension_id = ? AND deleted_at IS NULL", extensionID). + Scan(s.Ctx) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + return nil, errors.New("extension not found") + } + return nil, err + } + return &extension, nil +} + +func (s *ExtensionStorage) UpdateExtension(extension *types.Extension) error { + _, err := s.getDB().NewUpdate(). + Model(extension). + Where("id = ?", extension.ID). + Exec(s.Ctx) + if err != nil { + return err + } + return nil +} + +func (s *ExtensionStorage) DeleteExtension(id string) error { + _, err := s.getDB().NewUpdate(). + Model((*types.Extension)(nil)). + Set("deleted_at = NOW()"). + Where("id = ?", id). + Exec(s.Ctx) + if err != nil { + return err + } + return nil +} + +func (s *ExtensionStorage) ListExtensions(params types.ExtensionListParams) (*types.ExtensionListResponse, error) { + var extensions []types.Extension + + if params.Page <= 0 { + params.Page = 1 + } + if params.PageSize <= 0 { + params.PageSize = 12 + } + if params.SortBy == "" { + params.SortBy = types.ExtensionSortFieldName + } + if params.SortDir == "" { + params.SortDir = types.SortDirectionAsc + } + + query := s.getDB().NewSelect(). + Model(&extensions). + Relation("Variables"). + Where("deleted_at IS NULL") + + if params.Category != nil { + query = query.Where("category = ?", *params.Category) + } + + if params.Type != nil { + query = query.Where("extension_type = ?", *params.Type) + } + + if params.Search != "" { + searchPattern := "%" + params.Search + "%" + query = query.WhereGroup(" AND ", func(q *bun.SelectQuery) *bun.SelectQuery { + return q.Where("name ILIKE ?", searchPattern). + WhereOr("description ILIKE ?", searchPattern). + WhereOr("author ILIKE ?", searchPattern). + WhereOr("category::text ILIKE ?", searchPattern) + }) + } + + sortColumn := string(params.SortBy) + if params.SortDir == types.SortDirectionDesc { + query = query.Order(sortColumn + " DESC") + } else { + query = query.Order(sortColumn + " ASC") + } + + var total int + countQuery := s.getDB().NewSelect(). + Model((*types.Extension)(nil)). + Where("deleted_at IS NULL") + + if params.Category != nil { + countQuery = countQuery.Where("category = ?", *params.Category) + } + + if params.Type != nil { + countQuery = countQuery.Where("extension_type = ?", *params.Type) + } + + if params.Search != "" { + searchPattern := "%" + params.Search + "%" + countQuery = countQuery.WhereGroup(" AND ", func(q *bun.SelectQuery) *bun.SelectQuery { + return q.Where("name ILIKE ?", searchPattern). + WhereOr("description ILIKE ?", searchPattern). + WhereOr("author ILIKE ?", searchPattern). + WhereOr("category::text ILIKE ?", searchPattern) + }) + } + + total, err := countQuery.Count(s.Ctx) + if err != nil { + return nil, err + } + + offset := (params.Page - 1) * params.PageSize + query = query.Limit(params.PageSize).Offset(offset) + + err = query.Scan(s.Ctx) + if err != nil { + return nil, err + } + + totalPages := (total + params.PageSize - 1) / params.PageSize + + return &types.ExtensionListResponse{ + Extensions: extensions, + Total: total, + Page: params.Page, + PageSize: params.PageSize, + TotalPages: totalPages, + }, nil +} + +func (s *ExtensionStorage) ListCategories() ([]types.ExtensionCategory, error) { + var categories []types.ExtensionCategory + err := s.getDB().NewSelect(). + TableExpr("extensions"). + ColumnExpr("DISTINCT category"). + Where("deleted_at IS NULL"). + Scan(s.Ctx, &categories) + if err != nil { + return nil, err + } + return categories, nil +} + +func (s *ExtensionStorage) CreateExecution(exec *types.ExtensionExecution) error { + _, err := s.getDB().NewInsert().Model(exec).Exec(s.Ctx) + if err != nil { + return err + } + return nil +} + +func (s *ExtensionStorage) CreateExecutionSteps(steps []types.ExecutionStep) error { + if len(steps) == 0 { + return nil + } + _, err := s.getDB().NewInsert().Model(&steps).Exec(s.Ctx) + if err != nil { + return err + } + return nil +} + +func (s *ExtensionStorage) ListExecutionSteps(executionID string) ([]types.ExecutionStep, error) { + var steps []types.ExecutionStep + err := s.getDB().NewSelect(). + Model(&steps). + Where("execution_id = ?", executionID). + Order("step_order ASC"). + Scan(s.Ctx) + if err != nil { + return nil, err + } + return steps, nil +} + +func (s *ExtensionStorage) UpdateExecutionStep(step *types.ExecutionStep) error { + _, err := s.getDB().NewUpdate(). + Model(step). + Where("id = ?", step.ID). + Exec(s.Ctx) + if err != nil { + return err + } + return nil +} + +func (s *ExtensionStorage) UpdateExecution(exec *types.ExtensionExecution) error { + _, err := s.getDB().NewUpdate(). + Model(exec). + Where("id = ?", exec.ID). + Exec(s.Ctx) + if err != nil { + return err + } + return nil +} + +func (s *ExtensionStorage) GetExecutionByID(id string) (*types.ExtensionExecution, error) { + var exec types.ExtensionExecution + err := s.getDB().NewSelect(). + Model(&exec). + Where("id = ?", id). + Scan(s.Ctx) + if err != nil { + return nil, err + } + return &exec, nil +} + +func (s *ExtensionStorage) ListExecutionsByExtensionID(extensionID string) ([]types.ExtensionExecution, error) { + var execs []types.ExtensionExecution + err := s.getDB().NewSelect(). + Model(&execs). + Where("extension_id = ?", extensionID). + Order("created_at DESC"). + Scan(s.Ctx) + if err != nil { + return nil, err + } + return execs, nil +} + +func (s *ExtensionStorage) NextLogSequence(executionID string) (int64, error) { + var seq int64 + _, err := s.getDB().NewUpdate().Table("extension_executions"). + Set("log_seq = log_seq + 1"). + Where("id = ?", executionID). + Returning("log_seq"). + Exec(s.Ctx, &seq) + if err != nil { + return 0, err + } + return seq, nil +} + +func (s *ExtensionStorage) CreateExtensionLog(log *types.ExtensionLog) error { + _, err := s.getDB().NewInsert().Model(log).Exec(s.Ctx) + return err +} + +func (s *ExtensionStorage) CreateExtensionLogs(logs []types.ExtensionLog) error { + if len(logs) == 0 { + return nil + } + _, err := s.getDB().NewInsert().Model(&logs).Exec(s.Ctx) + return err +} + +func (s *ExtensionStorage) ListExtensionLogs(executionID string, afterSeq int64, limit int) ([]types.ExtensionLog, error) { + if limit <= 0 || limit > 1000 { + limit = 200 + } + var logs []types.ExtensionLog + q := s.getDB().NewSelect().Model(&logs).Where("execution_id = ?", executionID) + if afterSeq > 0 { + q = q.Where("sequence > ?", afterSeq) + } + err := q.Order("sequence ASC").Limit(limit).Scan(s.Ctx) + if err != nil { + return nil, err + } + return logs, nil +} diff --git a/api/internal/features/extension/validation/validator.go b/api/internal/features/extension/validation/validator.go new file mode 100644 index 000000000..180a43510 --- /dev/null +++ b/api/internal/features/extension/validation/validator.go @@ -0,0 +1,29 @@ +package validation + +import ( + "io" + "net/http" + + "github.com/go-playground/validator/v10" + "github.com/raghavyuva/nixopus-api/internal/features/extension/storage" +) + +type Validator struct { + validator *validator.Validate + storage storage.ExtensionStorageInterface +} + +func NewValidator(storage storage.ExtensionStorageInterface) *Validator { + return &Validator{ + validator: validator.New(), + storage: storage, + } +} + +func (v *Validator) ParseRequestBody(r *http.Request, body io.ReadCloser, req interface{}) error { + return nil +} + +func (v *Validator) ValidateRequest(req interface{}) error { + return v.validator.Struct(req) +} diff --git a/api/internal/features/supertokens/auth.go b/api/internal/features/supertokens/auth.go index e15b5c981..ca81e5784 100644 --- a/api/internal/features/supertokens/auth.go +++ b/api/internal/features/supertokens/auth.go @@ -43,7 +43,7 @@ var ( "audit:create", "audit:read", "audit:update", "audit:delete", "terminal:create", "terminal:read", "terminal:update", "terminal:delete", "feature_flags:read", "feature_flags:update", - "dashboard:read", + "dashboard:read", "extension:read", "extension:create", "extension:update", "extension:delete", } memberPermissions = []string{ @@ -57,10 +57,12 @@ var ( "deploy:read", "feature_flags:read", "dashboard:read", + "extension:read", } viewerPermissions = []string{ "user:read", "organization:read", "container:read", "audit:read", "domain:read", "notification:read", "file-manager:read", "deploy:read", "feature_flags:read", "dashboard:read", + "extension:read", } ) diff --git a/api/internal/routes.go b/api/internal/routes.go index e5b410f53..3d8ca5b72 100644 --- a/api/internal/routes.go +++ b/api/internal/routes.go @@ -17,6 +17,7 @@ import ( container "github.com/raghavyuva/nixopus-api/internal/features/container/controller" deploy "github.com/raghavyuva/nixopus-api/internal/features/deploy/controller" domain "github.com/raghavyuva/nixopus-api/internal/features/domain/controller" + extension "github.com/raghavyuva/nixopus-api/internal/features/extension/controller" feature_flags_controller "github.com/raghavyuva/nixopus-api/internal/features/feature-flags/controller" feature_flags_service "github.com/raghavyuva/nixopus-api/internal/features/feature-flags/service" feature_flags_storage "github.com/raghavyuva/nixopus-api/internal/features/feature-flags/storage" @@ -266,6 +267,16 @@ func (router *Router) Routes() { }) router.ContainerRoutes(containerGroup, containerController) + extensionController := extension.NewExtensionsController(router.app.Store, router.app.Ctx, l) + extensionGroup := fuego.Group(server, apiV1.Path+"/extensions") + fuego.Use(extensionGroup, func(next http.Handler) http.Handler { + return middleware.RBACMiddleware(next, router.app, "extension") + }) + fuego.Use(extensionGroup, func(next http.Handler) http.Handler { + return middleware.AuditMiddleware(next, router.app, l, "extension") + }) + router.ExtensionRoutes(extensionGroup, extensionController) + log.Printf("Server starting on port %s", PORT) log.Printf("Swagger UI available at: http://localhost:%s/swagger/", PORT) server.Run() @@ -416,6 +427,20 @@ func (router *Router) ContainerRoutes(s *fuego.Server, containerController *cont fuego.Post(s, "/images", containerController.ListImages) } +func (router *Router) ExtensionRoutes(s *fuego.Server, extensionController *extension.ExtensionsController) { + fuego.Get(s, "", extensionController.GetExtensions) + fuego.Get(s, "/categories", extensionController.GetCategories) + fuego.Get(s, "/{id}", extensionController.GetExtension) + fuego.Get(s, "/by-extension-id/{extension_id}", extensionController.GetExtensionByExtensionID) + fuego.Get(s, "/by-extension-id/{extension_id}/executions", extensionController.ListExecutionsByExtensionID) + fuego.Post(s, "/{extension_id}/run", extensionController.RunExtension) + fuego.Post(s, "/execution/{execution_id}/cancel", extensionController.CancelExecution) + fuego.Get(s, "/execution/{execution_id}", extensionController.GetExecution) + fuego.Get(s, "/execution/{execution_id}/logs", extensionController.ListExecutionLogs) + fuego.Post(s, "/{extension_id}/fork", extensionController.ForkExtension) + fuego.Delete(s, "/{id}", extensionController.DeleteFork) +} + func (router *Router) OrganizationRoutes(f *fuego.Server, organizationController *organization.OrganizationsController) { fuego.Get(f, "/users", organizationController.GetOrganizationUsers) fuego.Post(f, "/remove-user", organizationController.RemoveUserFromOrganization) diff --git a/api/internal/storage/store.go b/api/internal/storage/store.go index f3ffc3904..cb815b86e 100644 --- a/api/internal/storage/store.go +++ b/api/internal/storage/store.go @@ -3,7 +3,9 @@ package storage import ( "context" "fmt" + "log" + "github.com/raghavyuva/nixopus-api/internal/features/extension/loader" "github.com/raghavyuva/nixopus-api/internal/features/organization/storage" "github.com/raghavyuva/nixopus-api/internal/types" "github.com/uptrace/bun" @@ -43,31 +45,18 @@ func (s *Store) DropTable(ctx context.Context, model interface{}) error { func (s *Store) Init(ctx context.Context) error { s.DB.RegisterModel((*types.OrganizationUsers)(nil)) - - // tables := []interface{}{ - // (*types.User)(nil), - // (*types.Role)(nil), - // (*types.RefreshToken)(nil), - // (*types.Permission)(nil), - // (*types.RolePermissions)(nil), - // (*types.Organization)(nil), - // (*types.OrganizationUsers)(nil), - // (*types.SMTPConfigs)(nil), - // (*types.NotificationPreferences)(nil), - // (*types.PreferenceItem)(nil), - // (*types.Domain)(nil), - // (*types.Server)(nil), - // (*types.GithubConnector)(nil), - // (*types.Application)(nil), - // (*types.ApplicationStatus)(nil), - // (*types.ApplicationLogs)(nil), - // } - - // for _, model := range tables { - // if err := s.CreateTable(ctx, model); err != nil { - // return fmt.Errorf("failed to create table for %T: %w", model, err) - // } - // } + s.DB.RegisterModel((*types.Extension)(nil)) + s.DB.RegisterModel((*types.ExtensionVariable)(nil)) + s.DB.RegisterModel((*types.ExtensionExecution)(nil)) + s.DB.RegisterModel((*types.ExecutionStep)(nil)) + + // Load extensions from templates directory + extensionLoader := loader.NewExtensionLoader(s.DB) + if err := extensionLoader.LoadExtensionsFromTemplates(ctx); err != nil { + log.Printf("Warning: Failed to load extensions from templates: %v", err) + } else { + log.Println("Extensions loaded successfully from templates") + } return nil } diff --git a/api/internal/types/extension.go b/api/internal/types/extension.go new file mode 100644 index 000000000..bc0345b82 --- /dev/null +++ b/api/internal/types/extension.go @@ -0,0 +1,208 @@ +package types + +import ( + "encoding/json" + "time" + + "github.com/google/uuid" + "github.com/uptrace/bun" +) + +type ExtensionCategory string + +const ( + ExtensionCategorySecurity ExtensionCategory = "Security" + ExtensionCategoryContainers ExtensionCategory = "Containers" + ExtensionCategoryDatabase ExtensionCategory = "Database" + ExtensionCategoryWebServer ExtensionCategory = "Web Server" + ExtensionCategoryMaintenance ExtensionCategory = "Maintenance" + ExtensionCategoryMonitoring ExtensionCategory = "Monitoring" + ExtensionCategoryStorage ExtensionCategory = "Storage" + ExtensionCategoryNetwork ExtensionCategory = "Network" + ExtensionCategoryDevelopment ExtensionCategory = "Development" + ExtensionCategoryOther ExtensionCategory = "Other" +) + +type ValidationStatus string + +const ( + ValidationStatusNotValidated ValidationStatus = "not_validated" + ValidationStatusValid ValidationStatus = "valid" + ValidationStatusInvalid ValidationStatus = "invalid" +) + +type ExecutionStatus string + +const ( + ExecutionStatusPending ExecutionStatus = "pending" + ExecutionStatusRunning ExecutionStatus = "running" + ExecutionStatusCompleted ExecutionStatus = "completed" + ExecutionStatusFailed ExecutionStatus = "failed" + ExecutionStatusCancelled ExecutionStatus = "cancelled" +) + +type ExtensionType string + +const ( + ExtensionTypeInstall ExtensionType = "install" + ExtensionTypeRun ExtensionType = "run" +) + +type Extension struct { + bun.BaseModel `bun:"table:extensions,alias:e" swaggerignore:"true"` + ID uuid.UUID `json:"id" bun:"id,pk,type:uuid,default:uuid_generate_v4()"` + ExtensionID string `json:"extension_id" bun:"extension_id,unique,notnull"` + ParentExtensionID *uuid.UUID `json:"parent_extension_id,omitempty" bun:"parent_extension_id,nullzero"` + Name string `json:"name" bun:"name,notnull"` + Description string `json:"description" bun:"description,notnull"` + Author string `json:"author" bun:"author,notnull"` + Icon string `json:"icon" bun:"icon,notnull"` + Category ExtensionCategory `json:"category" bun:"category,notnull"` + ExtensionType ExtensionType `json:"extension_type" bun:"extension_type,notnull"` + Version string `json:"version" bun:"version"` + IsVerified bool `json:"is_verified" bun:"is_verified,notnull,default:false"` + YAMLContent string `json:"yaml_content" bun:"yaml_content,notnull"` + ParsedContent string `json:"parsed_content" bun:"parsed_content,notnull,type:jsonb"` + ContentHash string `json:"content_hash" bun:"content_hash,notnull"` + ValidationStatus ValidationStatus `json:"validation_status" bun:"validation_status,default:'not_validated'"` + ValidationErrors string `json:"validation_errors" bun:"validation_errors,type:jsonb"` + CreatedAt time.Time `json:"created_at" bun:"created_at,notnull,default:now()"` + UpdatedAt time.Time `json:"updated_at" bun:"updated_at,notnull,default:now()"` + DeletedAt *time.Time `json:"deleted_at,omitempty" bun:"deleted_at"` + + Variables []ExtensionVariable `json:"variables,omitempty" bun:"rel:has-many,join:id=extension_id"` +} + +type ExtensionVariable struct { + bun.BaseModel `bun:"table:extension_variables,alias:ev" swaggerignore:"true"` + ID uuid.UUID `json:"id" bun:"id,pk,type:uuid,default:uuid_generate_v4()"` + ExtensionID uuid.UUID `json:"extension_id" bun:"extension_id,notnull,type:uuid"` + VariableName string `json:"variable_name" bun:"variable_name,notnull"` + VariableType string `json:"variable_type" bun:"variable_type,notnull"` + Description string `json:"description" bun:"description"` + DefaultValue json.RawMessage `json:"default_value" bun:"default_value,type:jsonb"` + IsRequired bool `json:"is_required" bun:"is_required,default:false"` + ValidationPattern string `json:"validation_pattern" bun:"validation_pattern"` + CreatedAt time.Time `json:"created_at" bun:"created_at,notnull,default:now()"` + + Extension *Extension `json:"extension,omitempty" bun:"rel:belongs-to,join:extension_id=id"` +} + +type ExtensionExecution struct { + bun.BaseModel `bun:"table:extension_executions,alias:ee" swaggerignore:"true"` + ID uuid.UUID `json:"id" bun:"id,pk,type:uuid,default:uuid_generate_v4()"` + ExtensionID uuid.UUID `json:"extension_id" bun:"extension_id,notnull,type:uuid"` + ServerHostname string `json:"server_hostname" bun:"server_hostname"` + VariableValues string `json:"variable_values" bun:"variable_values,type:jsonb"` + Status ExecutionStatus `json:"status" bun:"status,default:'pending'"` + StartedAt time.Time `json:"started_at" bun:"started_at,notnull,default:now()"` + CompletedAt *time.Time `json:"completed_at,omitempty" bun:"completed_at"` + ExitCode int `json:"exit_code" bun:"exit_code"` + ErrorMessage string `json:"error_message" bun:"error_message"` + ExecutionLog string `json:"execution_log" bun:"execution_log"` + LogSeq int64 `json:"log_seq" bun:"log_seq"` + CreatedAt time.Time `json:"created_at" bun:"created_at,notnull,default:now()"` + + Extension *Extension `json:"extension,omitempty" bun:"rel:belongs-to,join:extension_id=id"` + Steps []ExecutionStep `json:"steps,omitempty" bun:"rel:has-many,join:id=execution_id"` +} + +type ExecutionStep struct { + bun.BaseModel `bun:"table:execution_steps,alias:es" swaggerignore:"true"` + ID uuid.UUID `json:"id" bun:"id,pk,type:uuid,default:uuid_generate_v4()"` + ExecutionID uuid.UUID `json:"execution_id" bun:"execution_id,notnull,type:uuid"` + StepName string `json:"step_name" bun:"step_name,notnull"` + Phase string `json:"phase" bun:"phase,notnull"` + StepOrder int `json:"step_order" bun:"step_order,notnull"` + StartedAt time.Time `json:"started_at" bun:"started_at,notnull,default:now()"` + CompletedAt *time.Time `json:"completed_at,omitempty" bun:"completed_at"` + Status ExecutionStatus `json:"status" bun:"status,default:'pending'"` + ExitCode int `json:"exit_code" bun:"exit_code"` + Output string `json:"output" bun:"output"` + CreatedAt time.Time `json:"created_at" bun:"created_at,notnull,default:now()"` + + Execution *ExtensionExecution `json:"execution,omitempty" bun:"rel:belongs-to,join:execution_id=id"` +} + +type ExtensionLog struct { + bun.BaseModel `bun:"table:extension_logs,alias:el" swaggerignore:"true"` + ID uuid.UUID `json:"id" bun:"id,pk,type:uuid,default:uuid_generate_v4()"` + ExecutionID uuid.UUID `json:"execution_id" bun:"execution_id,notnull,type:uuid"` + StepID *uuid.UUID `json:"step_id,omitempty" bun:"step_id,nullzero,type:uuid"` + Level string `json:"level" bun:"level,notnull"` + Message string `json:"message" bun:"message,notnull"` + Data json.RawMessage `json:"data" bun:"data,notnull,type:jsonb"` + Sequence int64 `json:"sequence" bun:"sequence,notnull"` + CreatedAt time.Time `json:"created_at" bun:"created_at,notnull,default:now()"` +} + +// SpecStep defines a single step in the extension spec (parsed from YAML/JSON) +type SpecStep struct { + Name string `json:"Name"` + Type string `json:"Type"` + Properties map[string]interface{} `json:"Properties"` + IgnoreErrors bool `json:"IgnoreErrors"` + Timeout int `json:"Timeout"` +} + +// ExtensionSpec is the parsed extension content used for execution +type ExtensionSpec struct { + Metadata struct { + ID string `json:"ID"` + Name string `json:"Name"` + Description string `json:"Description"` + Author string `json:"Author"` + Icon string `json:"Icon"` + Category string `json:"Category"` + Type string `json:"Type"` + Version string `json:"Version"` + IsVerified bool `json:"IsVerified"` + } `json:"Metadata"` + Variables map[string]struct { + Type string `json:"Type"` + Description string `json:"Description"` + Default interface{} `json:"Default"` + IsRequired bool `json:"IsRequired"` + ValidationPattern string `json:"ValidationPattern"` + } `json:"Variables"` + Execution struct { + Run []SpecStep `json:"Run"` + Validate []SpecStep `json:"Validate"` + } `json:"Execution"` +} + +type SortDirection string + +const ( + SortDirectionAsc SortDirection = "asc" + SortDirectionDesc SortDirection = "desc" +) + +type ExtensionSortField string + +const ( + ExtensionSortFieldName ExtensionSortField = "name" + ExtensionSortFieldAuthor ExtensionSortField = "author" + ExtensionSortFieldCategory ExtensionSortField = "category" + ExtensionSortFieldIsVerified ExtensionSortField = "is_verified" + ExtensionSortFieldCreatedAt ExtensionSortField = "created_at" + ExtensionSortFieldUpdatedAt ExtensionSortField = "updated_at" +) + +type ExtensionListParams struct { + Category *ExtensionCategory `json:"category,omitempty"` + Type *ExtensionType `json:"type,omitempty"` + Search string `json:"search,omitempty"` + SortBy ExtensionSortField `json:"sort_by,omitempty"` + SortDir SortDirection `json:"sort_dir,omitempty"` + Page int `json:"page,omitempty"` + PageSize int `json:"page_size,omitempty"` +} + +type ExtensionListResponse struct { + Extensions []Extension `json:"extensions"` + Total int `json:"total"` + Page int `json:"page"` + PageSize int `json:"page_size"` + TotalPages int `json:"total_pages"` +} diff --git a/api/migrations/audit/035_add_extensions_to_audit_up.sql b/api/migrations/audit/035_add_extensions_to_audit_up.sql new file mode 100644 index 000000000..36193423e --- /dev/null +++ b/api/migrations/audit/035_add_extensions_to_audit_up.sql @@ -0,0 +1 @@ +ALTER TYPE audit_resource_type ADD VALUE IF NOT EXISTS 'extension'; diff --git a/api/migrations/extensions/036_create_extensions_down.sql b/api/migrations/extensions/036_create_extensions_down.sql new file mode 100644 index 000000000..cb17318c1 --- /dev/null +++ b/api/migrations/extensions/036_create_extensions_down.sql @@ -0,0 +1,23 @@ +DROP TRIGGER IF EXISTS trigger_extensions_updated_at ON extensions; + +DROP FUNCTION IF EXISTS update_updated_at_column(); + +DROP INDEX IF EXISTS idx_execution_steps_execution; +DROP INDEX IF EXISTS idx_extension_executions_status; +DROP INDEX IF EXISTS idx_extension_executions_extension; +DROP INDEX IF EXISTS idx_extension_variables_extension; +DROP INDEX IF EXISTS idx_extensions_deleted_at; +DROP INDEX IF EXISTS idx_extensions_extension_id; +DROP INDEX IF EXISTS idx_extensions_created; +DROP INDEX IF EXISTS idx_extensions_validation_status; +DROP INDEX IF EXISTS idx_extensions_verified; +DROP INDEX IF EXISTS idx_extensions_category; + +DROP TABLE IF EXISTS execution_steps; +DROP TABLE IF EXISTS extension_executions; +DROP TABLE IF EXISTS extension_variables; +DROP TABLE IF EXISTS extensions; + +DROP TYPE IF EXISTS execution_status; +DROP TYPE IF EXISTS validation_status; +DROP TYPE IF EXISTS extension_category; diff --git a/api/migrations/extensions/036_create_extensions_up.sql b/api/migrations/extensions/036_create_extensions_up.sql new file mode 100644 index 000000000..b80033434 --- /dev/null +++ b/api/migrations/extensions/036_create_extensions_up.sql @@ -0,0 +1,135 @@ +CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; + +DO $$ BEGIN + CREATE TYPE extension_category AS ENUM ( + 'Security', 'Containers', 'Database', 'Web Server', + 'Maintenance', 'Monitoring', 'Storage', 'Network', + 'Development', 'Other' + ); +EXCEPTION + WHEN duplicate_object THEN null; +END $$; + +DO $$ BEGIN + CREATE TYPE validation_status AS ENUM ( + 'not_validated', 'valid', 'invalid' + ); +EXCEPTION + WHEN duplicate_object THEN null; +END $$; + +DO $$ BEGIN + CREATE TYPE execution_status AS ENUM ( + 'pending', 'running', 'completed', 'failed' + ); +EXCEPTION + WHEN duplicate_object THEN null; +END $$; + +CREATE TABLE IF NOT EXISTS extensions ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + extension_id VARCHAR(50) UNIQUE NOT NULL, + + name VARCHAR(100) NOT NULL, + description TEXT NOT NULL CHECK (LENGTH(description) BETWEEN 10 AND 500), + author VARCHAR(50) NOT NULL, + icon VARCHAR(10) NOT NULL, + category extension_category NOT NULL, + + version VARCHAR(20), + is_verified BOOLEAN NOT NULL DEFAULT false, + + yaml_content TEXT NOT NULL, + parsed_content JSONB NOT NULL, + content_hash VARCHAR(64) NOT NULL, + + validation_status validation_status DEFAULT 'not_validated', + validation_errors JSONB, + + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + deleted_at TIMESTAMP WITH TIME ZONE, + + CONSTRAINT valid_extension_id CHECK (extension_id ~ '^[a-z0-9][a-z0-9-]*[a-z0-9]$'), + CONSTRAINT valid_version CHECK (version IS NULL OR version ~ '^\d+\.\d+\.\d+(-[a-zA-Z0-9\-]+)?$') +); + +CREATE TABLE IF NOT EXISTS extension_variables ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + extension_id UUID REFERENCES extensions(id) ON DELETE CASCADE, + + variable_name VARCHAR(100) NOT NULL, + variable_type VARCHAR(20) NOT NULL, + description TEXT, + default_value JSONB, + is_required BOOLEAN DEFAULT false, + validation_pattern VARCHAR(500), + + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + + UNIQUE(extension_id, variable_name), + CONSTRAINT valid_variable_name CHECK (variable_name ~ '^[a-zA-Z_][a-zA-Z0-9_]*$'), + CONSTRAINT valid_variable_type CHECK (variable_type IN ('string', 'integer', 'boolean', 'array')) +); + +CREATE TABLE IF NOT EXISTS extension_executions ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + extension_id UUID REFERENCES extensions(id) ON DELETE CASCADE, + + server_hostname VARCHAR(255), + variable_values JSONB, + + status execution_status DEFAULT 'pending', + started_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + completed_at TIMESTAMP WITH TIME ZONE, + + exit_code INTEGER, + error_message TEXT, + execution_log TEXT, + + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +CREATE TABLE IF NOT EXISTS execution_steps ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + execution_id UUID REFERENCES extension_executions(id) ON DELETE CASCADE, + + step_name VARCHAR(200) NOT NULL, + phase VARCHAR(20) NOT NULL, + step_order INTEGER NOT NULL, + + started_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + completed_at TIMESTAMP WITH TIME ZONE, + status execution_status DEFAULT 'pending', + + exit_code INTEGER, + output TEXT, + + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + + CONSTRAINT valid_phase CHECK (phase IN ('pre_install', 'install', 'post_install', 'run', 'validate')) +); + +CREATE INDEX IF NOT EXISTS idx_extensions_category ON extensions(category); +CREATE INDEX IF NOT EXISTS idx_extensions_verified ON extensions(is_verified); +CREATE INDEX IF NOT EXISTS idx_extensions_validation_status ON extensions(validation_status); +CREATE INDEX IF NOT EXISTS idx_extensions_created ON extensions(created_at DESC); +CREATE INDEX IF NOT EXISTS idx_extensions_extension_id ON extensions(extension_id); +CREATE INDEX IF NOT EXISTS idx_extensions_deleted_at ON extensions(deleted_at); + +CREATE INDEX IF NOT EXISTS idx_extension_variables_extension ON extension_variables(extension_id); +CREATE INDEX IF NOT EXISTS idx_extension_executions_extension ON extension_executions(extension_id); +CREATE INDEX IF NOT EXISTS idx_extension_executions_status ON extension_executions(status); +CREATE INDEX IF NOT EXISTS idx_execution_steps_execution ON execution_steps(execution_id); + +CREATE OR REPLACE FUNCTION update_updated_at_column() +RETURNS TRIGGER AS $$ +BEGIN + NEW.updated_at = NOW(); + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER trigger_extensions_updated_at + BEFORE UPDATE ON extensions + FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); diff --git a/api/migrations/extensions/037_add_extension_type_down.sql b/api/migrations/extensions/037_add_extension_type_down.sql new file mode 100644 index 000000000..64fad158e --- /dev/null +++ b/api/migrations/extensions/037_add_extension_type_down.sql @@ -0,0 +1,7 @@ +DROP INDEX IF EXISTS idx_extensions_extension_type; + +ALTER TABLE extensions + DROP COLUMN IF EXISTS extension_type; + +DROP TYPE IF EXISTS extension_type; + diff --git a/api/migrations/extensions/037_add_extension_type_up.sql b/api/migrations/extensions/037_add_extension_type_up.sql new file mode 100644 index 000000000..57a566c9b --- /dev/null +++ b/api/migrations/extensions/037_add_extension_type_up.sql @@ -0,0 +1,11 @@ +DO $$ BEGIN + CREATE TYPE extension_type AS ENUM ('install', 'run'); +EXCEPTION + WHEN duplicate_object THEN null; +END $$; + +ALTER TABLE extensions + ADD COLUMN IF NOT EXISTS extension_type extension_type NOT NULL DEFAULT 'run'; + +CREATE INDEX IF NOT EXISTS idx_extensions_extension_type ON extensions(extension_type); + diff --git a/api/migrations/extensions/038_add_parent_extension_id_down.sql b/api/migrations/extensions/038_add_parent_extension_id_down.sql new file mode 100644 index 000000000..07aaa03c3 --- /dev/null +++ b/api/migrations/extensions/038_add_parent_extension_id_down.sql @@ -0,0 +1,3 @@ +DROP INDEX IF EXISTS idx_extensions_parent_extension_id; +ALTER TABLE extensions DROP COLUMN IF EXISTS parent_extension_id; + diff --git a/api/migrations/extensions/038_add_parent_extension_id_up.sql b/api/migrations/extensions/038_add_parent_extension_id_up.sql new file mode 100644 index 000000000..07989283e --- /dev/null +++ b/api/migrations/extensions/038_add_parent_extension_id_up.sql @@ -0,0 +1,5 @@ +ALTER TABLE extensions + ADD COLUMN IF NOT EXISTS parent_extension_id UUID NULL REFERENCES extensions(id) ON DELETE SET NULL; + +CREATE INDEX IF NOT EXISTS idx_extensions_parent_extension_id ON extensions(parent_extension_id); + diff --git a/api/migrations/extensions/039_add_extension_logs_down.sql b/api/migrations/extensions/039_add_extension_logs_down.sql new file mode 100644 index 000000000..78f95cc8b --- /dev/null +++ b/api/migrations/extensions/039_add_extension_logs_down.sql @@ -0,0 +1,5 @@ +DROP INDEX IF EXISTS idx_extension_logs_exec_created; +DROP INDEX IF EXISTS idx_extension_logs_exec_seq; +DROP TABLE IF EXISTS extension_logs; +ALTER TABLE extension_executions DROP COLUMN IF EXISTS log_seq; + diff --git a/api/migrations/extensions/039_add_extension_logs_up.sql b/api/migrations/extensions/039_add_extension_logs_up.sql new file mode 100644 index 000000000..586718088 --- /dev/null +++ b/api/migrations/extensions/039_add_extension_logs_up.sql @@ -0,0 +1,17 @@ +ALTER TABLE extension_executions +ADD COLUMN IF NOT EXISTS log_seq BIGINT NOT NULL DEFAULT 0; + +CREATE TABLE IF NOT EXISTS extension_logs ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + execution_id UUID NOT NULL REFERENCES extension_executions(id) ON DELETE CASCADE, + step_id UUID REFERENCES execution_steps(id) ON DELETE SET NULL, + level TEXT NOT NULL, + message TEXT NOT NULL, + data JSONB NOT NULL DEFAULT '{}'::jsonb, + sequence BIGINT NOT NULL, + created_at TIMESTAMPTZ NOT NULL DEFAULT now() +); + +CREATE INDEX IF NOT EXISTS idx_extension_logs_exec_seq ON extension_logs(execution_id, sequence); +CREATE INDEX IF NOT EXISTS idx_extension_logs_exec_created ON extension_logs(execution_id, created_at); + diff --git a/api/migrations/feature-flags/034_add_extensions_feature_flag_down.sql b/api/migrations/feature-flags/034_add_extensions_feature_flag_down.sql new file mode 100644 index 000000000..f12601d21 --- /dev/null +++ b/api/migrations/feature-flags/034_add_extensions_feature_flag_down.sql @@ -0,0 +1 @@ +DELETE FROM feature_flags WHERE feature_name = 'extensions'; diff --git a/api/migrations/feature-flags/034_add_extensions_feature_flag_up.sql b/api/migrations/feature-flags/034_add_extensions_feature_flag_up.sql new file mode 100644 index 000000000..024a56edd --- /dev/null +++ b/api/migrations/feature-flags/034_add_extensions_feature_flag_up.sql @@ -0,0 +1,14 @@ +INSERT INTO feature_flags (id, organization_id, feature_name, is_enabled, created_at, updated_at) +SELECT + uuid_generate_v4(), + o.id, + 'extensions', + true, + NOW(), + NOW() +FROM organizations o +WHERE NOT EXISTS ( + SELECT 1 FROM feature_flags ff + WHERE ff.organization_id = o.id + AND ff.feature_name = 'extensions' +); diff --git a/api/templates/deploy-appwrite.yaml b/api/templates/deploy-appwrite.yaml new file mode 100644 index 000000000..af4f8f499 --- /dev/null +++ b/api/templates/deploy-appwrite.yaml @@ -0,0 +1,78 @@ +metadata: + id: "deploy-appwrite" + name: "Appwrite" + description: "Appwrite is a secure open-source backend server for web, mobile, and Flutter developers. This template runs the official installer to provision the stack via Docker." + author: "Nixopus Team" + icon: "🧩" + category: "Containers" + type: "install" + version: "1.0.0" + isVerified: false + +variables: + image: + type: "string" + description: "Docker image for Appwrite installer" + default: "appwrite/appwrite" + is_required: true + + tag: + type: "string" + description: "Docker image tag" + default: "1.7.4" + is_required: true + + container_name: + type: "string" + description: "Name of the Appwrite installer container" + default: "appwrite-installer" + is_required: true + + config_volume_name: + type: "string" + description: "Named Docker volume to persist generated Appwrite configuration (/usr/src/code/appwrite)" + default: "appwrite-config" + is_required: true + + host_port: + type: "integer" + description: "Host port to expose Appwrite" + default: 5555 + is_required: true + + container_port: + type: "integer" + description: "Container port for Appwrite" + default: 80 + is_required: true + +execution: + run: + - name: "Pull Appwrite image" + type: "docker" + properties: + action: "pull" + image: "{{ image }}" + tag: "{{ tag }}" + timeout: 300 + + - name: "Run Appwrite installer" + type: "docker" + properties: + action: "run" + name: "{{ container_name }}" + image: "{{ image }}" + tag: "{{ tag }}" + ports: "{{ host_port }}:{{ container_port }}" + entrypoint: "install" + volumes: + - "/var/run/docker.sock:/var/run/docker.sock" + - "{{ config_volume_name }}:/usr/src/code/appwrite" + timeout: 600 + + validate: + - name: "Check HTTP response from Appwrite" + type: "command" + properties: + cmd: "sh -c 'for i in $(seq 1 30); do code=$(curl -fsS -o /dev/null -w \"%{http_code}\\n\" http://localhost:{{ host_port }} || true); echo \"HTTP $code\"; echo $code | grep -E \"^(200|301|302)$\" && exit 0; sleep 2; done; exit 1'" + timeout: 60 diff --git a/api/templates/deploy-beszel.yaml b/api/templates/deploy-beszel.yaml new file mode 100644 index 000000000..cb441a039 --- /dev/null +++ b/api/templates/deploy-beszel.yaml @@ -0,0 +1,83 @@ +metadata: + id: "deploy-beszel" + name: "Beszel" + description: "Simple, lightweight server monitoring with Docker stats, historical data, and alerts. Beszel provides real-time monitoring of your server resources with a clean, intuitive interface for tracking CPU, memory, disk usage, and more." + author: "Nixopus Team" + icon: "📈" + category: "Monitoring" + type: "install" + version: "1.0.0" + isVerified: false + +variables: + image: + type: "string" + description: "Docker image for Beszel" + default: "henrygd/beszel" + is_required: true + + tag: + type: "string" + description: "Docker image tag" + default: "latest" + is_required: true + + container_name: + type: "string" + description: "Name of the Beszel container" + default: "beszel" + is_required: true + + host_port: + type: "integer" + description: "Host port to expose Beszel" + default: 8090 + is_required: true + + container_port: + type: "integer" + description: "Container port for Beszel" + default: 8090 + is_required: true + + data_volume_name: + type: "string" + description: "Named Docker volume for Beszel data persistence" + default: "beszel_data" + is_required: true + +execution: + run: + - name: "Pull Beszel image" + type: "docker" + properties: + action: "pull" + image: "{{ image }}" + tag: "{{ tag }}" + timeout: 300 + + - name: "Run Beszel container" + type: "docker" + properties: + action: "run" + name: "{{ container_name }}" + image: "{{ image }}" + tag: "{{ tag }}" + ports: "{{ host_port }}:{{ container_port }}" + restart: "unless-stopped" + volumes: + - "{{ data_volume_name }}:/beszel_data" + timeout: 180 + + validate: + - name: "Check Beszel HTTP response" + type: "command" + properties: + cmd: "sh -c 'for i in $(seq 1 30); do code=$(curl -fsS -o /dev/null -w \"%{http_code}\\n\" http://localhost:{{ host_port }} || true); echo \"HTTP $code\"; echo $code | grep -E \"^(200|301|302)$\" && exit 0; sleep 2; done; exit 1'" + timeout: 60 + + - name: "Verify Beszel is accessible" + type: "command" + properties: + cmd: "sh -c 'curl -fsS http://localhost:{{ host_port }} | grep -i beszel >/dev/null && exit 0 || exit 1'" + timeout: 30 diff --git a/api/templates/deploy-blocky.yaml b/api/templates/deploy-blocky.yaml new file mode 100644 index 000000000..93994c572 --- /dev/null +++ b/api/templates/deploy-blocky.yaml @@ -0,0 +1,101 @@ +metadata: + id: "deploy-blocky" + name: "Blocky" + description: "Blocky is a DNS proxy and ad-blocker for the local network written in Go. It provides fast DNS resolution with built-in ad-blocking capabilities, customizable filtering rules, and a web interface for monitoring and configuration." + author: "Nixopus Team" + icon: "🛡️" + category: "Network" + type: "install" + version: "1.0.0" + isVerified: false + +variables: + image: + type: "string" + description: "Docker image for Blocky" + default: "spx01/blocky" + is_required: true + + tag: + type: "string" + description: "Docker image tag" + default: "latest" + is_required: true + + container_name: + type: "string" + description: "Name of the Blocky container" + default: "blocky" + is_required: true + + web_port: + type: "integer" + description: "Host port to expose Blocky web interface" + default: 4000 + is_required: true + + dns_port: + type: "integer" + description: "Host port to expose DNS service (UDP)" + default: 53 + is_required: true + + container_web_port: + type: "integer" + description: "Container port for web interface" + default: 4000 + is_required: true + + container_dns_port: + type: "integer" + description: "Container port for DNS service" + default: 53 + is_required: true + + config_volume_name: + type: "string" + description: "Named Docker volume for Blocky configuration file" + default: "blocky-config" + is_required: true + +execution: + run: + - name: "Pull Blocky image" + type: "docker" + properties: + action: "pull" + image: "{{ image }}" + tag: "{{ tag }}" + timeout: 300 + + - name: "Run Blocky container" + type: "docker" + properties: + action: "run" + name: "{{ container_name }}" + image: "{{ image }}" + tag: "{{ tag }}" + ports: "{{ web_port }}:{{ container_web_port }},{{ dns_port }}:{{ container_dns_port }}/udp" + restart: "unless-stopped" + volumes: + - "{{ config_volume_name }}:/app/config.yml" + timeout: 180 + + validate: + - name: "Check Blocky web interface HTTP response" + type: "command" + properties: + cmd: "sh -c 'for i in $(seq 1 30); do code=$(curl -fsS -o /dev/null -w \"%{http_code}\\n\" http://localhost:{{ web_port }} || true); echo \"HTTP $code\"; echo $code | grep -E \"^(200|301|302)$\" && exit 0; sleep 2; done; exit 1'" + timeout: 60 + + - name: "Verify Blocky web interface is accessible" + type: "command" + properties: + cmd: "sh -c 'curl -fsS http://localhost:{{ web_port }} | grep -i blocky >/dev/null && exit 0 || exit 1'" + timeout: 30 + + - name: "Test DNS resolution" + type: "command" + properties: + cmd: "sh -c 'nslookup google.com 127.0.0.1 -port={{ dns_port }} >/dev/null 2>&1 && exit 0 || exit 1'" + timeout: 30 diff --git a/api/templates/deploy-code-server.yaml b/api/templates/deploy-code-server.yaml new file mode 100644 index 000000000..f5c4fca35 --- /dev/null +++ b/api/templates/deploy-code-server.yaml @@ -0,0 +1,154 @@ +metadata: + id: "deploy-code-server" + name: "Code Server" + description: "Code Server is VS Code running on a remote server, accessible through the browser. It provides a full-featured VS Code experience in your browser with extensions, terminal, and all the features you expect from VS Code." + author: "Nixopus Team" + icon: "💻" + category: "Development" + type: "install" + version: "1.0.0" + isVerified: false + +variables: + image: + type: "string" + description: "Docker image for Code Server" + default: "lscr.io/linuxserver/code-server" + is_required: true + + tag: + type: "string" + description: "Docker image tag" + default: "latest" + is_required: true + + container_name: + type: "string" + description: "Name of the Code Server container" + default: "code-server" + is_required: true + + host_port: + type: "integer" + description: "Host port to expose Code Server" + default: 8443 + is_required: true + + container_port: + type: "integer" + description: "Container port for Code Server" + default: 8443 + is_required: true + + puid: + type: "integer" + description: "User ID for the container user" + default: 1000 + is_required: true + + pgid: + type: "integer" + description: "Group ID for the container user" + default: 1000 + is_required: true + + timezone: + type: "string" + description: "Timezone for the container" + default: "Etc/UTC" + is_required: true + + password: + type: "string" + description: "Password for Code Server access (optional)" + default: "" + is_required: false + + hashed_password: + type: "string" + description: "Hashed password for Code Server access (optional)" + default: "" + is_required: false + + sudo_password: + type: "string" + description: "Sudo password for the container user (optional)" + default: "" + is_required: false + + sudo_password_hash: + type: "string" + description: "Hashed sudo password for the container user (optional)" + default: "" + is_required: false + + proxy_domain: + type: "string" + description: "Proxy domain for Code Server (optional)" + default: "" + is_required: false + + default_workspace: + type: "string" + description: "Default workspace directory" + default: "/config/workspace" + is_required: true + + pwa_appname: + type: "string" + description: "PWA application name (optional)" + default: "code-server" + is_required: false + + config_volume_name: + type: "string" + description: "Named Docker volume for Code Server configuration and data" + default: "code-server-config" + is_required: true + +execution: + run: + - name: "Pull Code Server image" + type: "docker" + properties: + action: "pull" + image: "{{ image }}" + tag: "{{ tag }}" + timeout: 300 + + - name: "Run Code Server container" + type: "docker" + properties: + action: "run" + name: "{{ container_name }}" + image: "{{ image }}" + tag: "{{ tag }}" + ports: "{{ host_port }}:{{ container_port }}" + restart: "unless-stopped" + env: + PUID: "{{ puid }}" + PGID: "{{ pgid }}" + TZ: "{{ timezone }}" + PASSWORD: "{{ password }}" + HASHED_PASSWORD: "{{ hashed_password }}" + SUDO_PASSWORD: "{{ sudo_password }}" + SUDO_PASSWORD_HASH: "{{ sudo_password_hash }}" + PROXY_DOMAIN: "{{ proxy_domain }}" + DEFAULT_WORKSPACE: "{{ default_workspace }}" + PWA_APPNAME: "{{ pwa_appname }}" + volumes: + - "{{ config_volume_name }}:/config" + timeout: 300 + + validate: + - name: "Check Code Server HTTP response" + type: "command" + properties: + cmd: "sh -c 'for i in $(seq 1 60); do code=$(curl -fsS -o /dev/null -w \"%{http_code}\\n\" http://localhost:{{ host_port }} || true); echo \"HTTP $code\"; echo $code | grep -E \"^(200|301|302)$\" && exit 0; sleep 5; done; exit 1'" + timeout: 300 + + - name: "Verify Code Server is accessible" + type: "command" + properties: + cmd: "sh -c 'curl -fsS http://localhost:{{ host_port }} | grep -i \"code-server\" >/dev/null && exit 0 || exit 1'" + timeout: 30 diff --git a/api/templates/deploy-dashdot.yaml b/api/templates/deploy-dashdot.yaml new file mode 100644 index 000000000..22899fb77 --- /dev/null +++ b/api/templates/deploy-dashdot.yaml @@ -0,0 +1,77 @@ +metadata: + id: "deploy-dashdot" + name: "Dash" + description: "Aa modern server dashboard" + author: "Nixopus Team" + icon: "📊" + category: "Monitoring" + type: "install" + version: "1.0.0" + isVerified: false + +variables: + image: + type: "string" + description: "Docker image for Dashdot" + default: "mauricenino/dashdot" + is_required: true + + tag: + type: "string" + description: "Docker image tag" + default: "latest" + is_required: true + + container_name: + type: "string" + description: "Name of the Dashdot container" + default: "dashdot" + is_required: true + + host_port: + type: "integer" + description: "Host port to expose Dashdot" + default: 80 + is_required: true + + container_port: + type: "integer" + description: "Container port for Dashdot" + default: 3001 + is_required: true + + host_mount_path: + type: "string" + description: "Host path to mount for system monitoring (read-only)" + default: "/" + is_required: true + + container_mount_path: + type: "string" + description: "Container path for host mount" + default: "/mnt/host" + is_required: true + +execution: + run: + - name: "Pull Dashdot image" + type: "docker" + properties: + action: "pull" + image: "{{ image }}" + tag: "{{ tag }}" + timeout: 300 + + - name: "Run Dashdot container" + type: "docker" + properties: + action: "run" + name: "{{ container_name }}" + image: "{{ image }}" + tag: "{{ tag }}" + ports: "{{ host_port }}:{{ container_port }}" + restart: "unless-stopped" + privileged: true + volumes: + - "{{ host_mount_path }}:{{ container_mount_path }}:ro" + timeout: 180 diff --git a/api/templates/deploy-dashy.yaml b/api/templates/deploy-dashy.yaml new file mode 100644 index 000000000..c0e53144f --- /dev/null +++ b/api/templates/deploy-dashy.yaml @@ -0,0 +1,79 @@ +metadata: + id: "deploy-dashy" + name: "Dashy" + description: "Dashy is a self-hosted, highly configurable personal dashboard." + author: "Nixopus Team" + icon: "📊" + category: "Containers" + type: "install" + version: "1.0.0" + isVerified: false + +variables: + image: + type: "string" + description: "Docker image for Dashy" + default: "lissy93/dashy" + is_required: true + + tag: + type: "string" + description: "Docker image tag" + default: "latest" + is_required: true + + container_name: + type: "string" + description: "Name of the Dashy container" + default: "dashy" + is_required: true + + host_port: + type: "integer" + description: "Host port to expose Dashy" + default: 8080 + is_required: true + + container_port: + type: "integer" + description: "Container port for Dashy" + default: 8080 + is_required: true + + user_data_volume: + type: "string" + description: "Named Docker volume for Dashy user data (/app/user-data)" + default: "dashy-user-data" + is_required: true + +execution: + run: + - name: "Pull Dashy image" + type: "docker" + properties: + action: "pull" + image: "{{ image }}" + tag: "{{ tag }}" + timeout: 300 + + - name: "Run Dashy container" + type: "docker" + properties: + action: "run" + name: "{{ container_name }}" + image: "{{ image }}" + tag: "{{ tag }}" + ports: "{{ host_port }}:{{ container_port }}" + restart: "unless-stopped" + volumes: + - "{{ user_data_volume }}:/app/user-data" + timeout: 180 + + validate: + - name: "Check HTTP response from Dashy" + type: "command" + properties: + cmd: "sh -c 'for i in $(seq 1 30); do code=$(curl -fsS -o /dev/null -w \"%{http_code}\\n\" http://localhost:{{ host_port }} || true); echo \"HTTP $code\"; echo $code | grep -E \"^(200|301|302)$\" && exit 0; sleep 2; done; exit 1'" + timeout: 60 + + diff --git a/api/templates/deploy-dozzle.yaml b/api/templates/deploy-dozzle.yaml new file mode 100644 index 000000000..767b9443a --- /dev/null +++ b/api/templates/deploy-dozzle.yaml @@ -0,0 +1,101 @@ +metadata: + id: "deploy-dozzle" + name: "Dozzle" + description: "Dozzle is a real-time log viewer for Docker containers. It provides a modern web interface to monitor and view logs from all your Docker containers in real-time, with support for container actions like stopping, starting, and restarting containers." + author: "Nixopus Team" + icon: "📊" + category: "Monitoring" + type: "install" + version: "1.0.0" + isVerified: false + +variables: + image: + type: "string" + description: "Docker image for Dozzle" + default: "amir20/dozzle" + is_required: true + + tag: + type: "string" + description: "Docker image tag" + default: "latest" + is_required: true + + container_name: + type: "string" + description: "Name of the Dozzle container" + default: "dozzle" + is_required: true + + host_port: + type: "integer" + description: "Host port to expose Dozzle" + default: 8080 + is_required: true + + container_port: + type: "integer" + description: "Container port for Dozzle" + default: 8080 + is_required: true + + docker_socket_path: + type: "string" + description: "Path to Docker socket on host" + default: "/var/run/docker.sock" + is_required: true + + enable_actions: + type: "string" + description: "Enable container actions (start, stop, restart) - set to 'true' to enable" + default: "false" + is_required: false + + enable_shell: + type: "string" + description: "Enable shell access to containers - set to 'true' to enable" + default: "false" + is_required: false + +execution: + run: + - name: "Pull Dozzle image" + type: "docker" + properties: + action: "pull" + image: "{{ image }}" + tag: "{{ tag }}" + timeout: 300 + + - name: "Run Dozzle container" + type: "docker" + properties: + action: "run" + name: "{{ container_name }}" + image: "{{ image }}" + tag: "{{ tag }}" + ports: "{{ host_port }}:{{ container_port }}" + restart: "unless-stopped" + env: + DOZZLE_LEVEL: "info" + DOZZLE_TAILSIZE: "300" + DOZZLE_FILTER: "" + DOZZLE_ACTIONS: "{{ enable_actions }}" + DOZZLE_SHELL: "{{ enable_shell }}" + volumes: + - "{{ docker_socket_path }}:/var/run/docker.sock:ro" + timeout: 180 + + validate: + - name: "Check Dozzle HTTP response" + type: "command" + properties: + cmd: "sh -c 'for i in $(seq 1 30); do code=$(curl -fsS -o /dev/null -w \"%{http_code}\\n\" http://localhost:{{ host_port }} || true); echo \"HTTP $code\"; echo $code | grep -E \"^(200|301|302)$\" && exit 0; sleep 2; done; exit 1'" + timeout: 60 + + - name: "Verify Dozzle is accessible" + type: "command" + properties: + cmd: "sh -c 'curl -fsS http://localhost:{{ host_port }} | grep -i dozzle >/dev/null && exit 0 || exit 1'" + timeout: 30 diff --git a/api/templates/deploy-excalidraw.yaml b/api/templates/deploy-excalidraw.yaml new file mode 100644 index 000000000..0fc529627 --- /dev/null +++ b/api/templates/deploy-excalidraw.yaml @@ -0,0 +1,69 @@ +metadata: + id: "deploy-excalidraw" + name: "Excalidraw" + description: "Excalidraw is a virtual collaborative whiteboard tool that lets you easily sketch diagrams that have a hand-drawn feel to them." + author: "Nixopus Team" + icon: "✏️" + category: "Containers" + type: "install" + version: "1.0.0" + isVerified: false + +variables: + image: + type: "string" + description: "Docker image for Excalidraw" + default: "excalidraw/excalidraw" + is_required: true + + tag: + type: "string" + description: "Docker image tag" + default: "latest" + is_required: true + + container_name: + type: "string" + description: "Name of the container" + default: "excalidraw" + is_required: true + + host_port: + type: "integer" + description: "Host port to expose" + default: 5000 + is_required: true + + container_port: + type: "integer" + description: "Container port to map" + default: 80 + is_required: true + +execution: + run: + - name: "Pull Excalidraw image" + type: "docker" + properties: + action: "pull" + image: "{{ image }}" + tag: "{{ tag }}" + timeout: 300 + + - name: "Run Excalidraw container" + type: "docker" + properties: + action: "run" + name: "{{ container_name }}" + image: "{{ image }}" + tag: "{{ tag }}" + ports: "{{ host_port }}:{{ container_port }}" + restart: "unless-stopped" + timeout: 120 + + validate: + - name: "Check HTTP response from Excalidraw" + type: "command" + properties: + cmd: "curl -fsS -o /dev/null -w '%{http_code}\n' http://localhost:{{ host_port }} | grep -E '^(200|301|302)$'" + timeout: 60 diff --git a/api/templates/deploy-filebrowser.yaml b/api/templates/deploy-filebrowser.yaml new file mode 100644 index 000000000..63d00eecb --- /dev/null +++ b/api/templates/deploy-filebrowser.yaml @@ -0,0 +1,87 @@ +metadata: + id: "deploy-filebrowser" + name: "File Browser" + description: "File Browser provides a web interface to manage files and folders" + author: "Nixopus Team" + icon: "🗂️" + category: "Storage" + type: "install" + version: "1.0.0" + isVerified: false + +variables: + image: + type: "string" + description: "Docker image for File Browser" + default: "filebrowser/filebrowser" + is_required: true + + tag: + type: "string" + description: "Docker image tag" + default: "latest" + is_required: true + + container_name: + type: "string" + description: "Name of the File Browser container" + default: "filebrowser" + is_required: true + + host_port: + type: "integer" + description: "Host port to expose File Browser" + default: 8080 + is_required: true + + data_volume: + type: "string" + description: "Named Docker volume for /srv" + default: "filebrowser_data" + is_required: true + + db_volume: + type: "string" + description: "Named Docker volume for /database" + default: "filebrowser_database" + is_required: true + + config_volume: + type: "string" + description: "Named Docker volume for /config" + default: "filebrowser_config" + is_required: true + +execution: + run: + - name: "Pull File Browser image" + type: "docker" + properties: + action: "pull" + image: "{{ image }}" + tag: "{{ tag }}" + timeout: 300 + + - name: "Run File Browser container" + type: "docker" + properties: + action: "run" + name: "{{ container_name }}" + image: "{{ image }}" + tag: "{{ tag }}" + ports: "{{ host_port }}:80" + restart: "unless-stopped" + volumes: + - "{{ data_volume }}:/srv" + - "{{ db_volume }}:/database" + - "{{ config_volume }}:/config" + timeout: 180 + + validate: + - name: "Check HTTP response from File Browser" + type: "command" + properties: + cmd: "sh -c 'for i in $(seq 1 30); do code=$(curl -fsS -o /dev/null -w \"%{http_code}\\n\" http://localhost:{{ host_port }} || true); echo \"HTTP $code\"; echo $code | grep -E \"^(200|301|302)$\" && exit 0; sleep 2; done; exit 1'" + timeout: 60 + + diff --git a/api/templates/deploy-firefly-iii.yaml b/api/templates/deploy-firefly-iii.yaml new file mode 100644 index 000000000..4a48b16f6 --- /dev/null +++ b/api/templates/deploy-firefly-iii.yaml @@ -0,0 +1,133 @@ +metadata: + id: "deploy-firefly-iii" + name: "Firefly III" + description: "Firefly III is a free and open-source personal finance manager. It helps you keep track of expenses, income, budgets, and everything in between. It supports credit cards, has an envelope budgeting system, categories, and much more." + author: "Nixopus Team" + icon: "🔥" + category: "Containers" + type: "install" + version: "1.0.0" + isVerified: false + +variables: + image: + type: "string" + description: "Docker image for Firefly III" + default: "fireflyiii/core" + is_required: true + + tag: + type: "string" + description: "Docker image tag" + default: "latest" + is_required: true + + container_name: + type: "string" + description: "Name of the Firefly III container" + default: "firefly-iii" + is_required: true + + host_port: + type: "integer" + description: "Host port to expose Firefly III" + default: 80 + is_required: true + + container_port: + type: "integer" + description: "Container port for Firefly III" + default: 8080 + is_required: true + + app_key: + type: "string" + description: "Application key (32 character random string)" + default: "CHANGEME_32_CHARS" + is_required: true + + db_connection: + type: "string" + description: "Database connection type" + default: "mysql" + is_required: true + + db_host: + type: "string" + description: "Database host (use 'host.docker.internal' for host machine, or IP address for Linux)" + default: "host.docker.internal" + is_required: true + + db_port: + type: "integer" + description: "Database port" + default: 3306 + is_required: true + + db_database: + type: "string" + description: "Database name" + default: "firefly" + is_required: true + + db_username: + type: "string" + description: "Database username" + default: "firefly" + is_required: true + + db_password: + type: "string" + description: "Database password" + default: "firefly" + is_required: true + + upload_volume_name: + type: "string" + description: "Named Docker volume for Firefly III uploads (will be created automatically)" + default: "firefly_iii_upload" + is_required: true + +execution: + run: + - name: "Pull Firefly III image" + type: "docker" + properties: + action: "pull" + image: "{{ image }}" + tag: "{{ tag }}" + timeout: 300 + + - name: "Run Firefly III container" + type: "docker" + properties: + action: "run" + name: "{{ container_name }}" + image: "{{ image }}" + tag: "{{ tag }}" + ports: "{{ host_port }}:{{ container_port }}" + restart: "unless-stopped" + env: + APP_KEY: "{{ app_key }}" + DB_CONNECTION: "{{ db_connection }}" + DB_HOST: "{{ db_host }}" + DB_PORT: "{{ db_port }}" + DB_DATABASE: "{{ db_database }}" + DB_USERNAME: "{{ db_username }}" + DB_PASSWORD: "{{ db_password }}" + volumes: + - "{{ upload_volume_name }}:/var/www/html/storage/upload" + timeout: 300 + + validate: + - name: "Check Firefly III HTTP response" + type: "command" + properties: + cmd: "sh -c 'for i in $(seq 1 60); do code=$(curl -fsS -o /dev/null -w \"%{http_code}\\n\" http://localhost:{{ host_port }} || true); echo \"HTTP $code\"; echo $code | grep -E \"^(200|301|302)$\" && exit 0; sleep 5; done; exit 1'" + timeout: 300 + + - name: "Verify Firefly III is accessible" + type: "command" + properties: + cmd: "sh -c 'curl -fsS http://localhost:{{ host_port }} | grep -i firefly >/dev/null && exit 0 || exit 1'" + timeout: 30 diff --git a/api/templates/deploy-homer.yaml b/api/templates/deploy-homer.yaml new file mode 100644 index 000000000..ebb0db08b --- /dev/null +++ b/api/templates/deploy-homer.yaml @@ -0,0 +1,83 @@ +metadata: + id: "deploy-homer" + name: "Homer" + description: "Homer is a simple static homepage for your server. It features a simple yaml configuration and a clean interface. Perfect to serve as a dashboard or homepage to your services with an easy to edit yaml configuration." + author: "Nixopus Team" + icon: "🏠" + category: "Containers" + type: "install" + version: "1.0.0" + isVerified: false + +variables: + image: + type: "string" + description: "Docker image for Homer" + default: "b4bz/homer" + is_required: true + + tag: + type: "string" + description: "Docker image tag" + default: "latest" + is_required: true + + container_name: + type: "string" + description: "Name of the Homer container" + default: "homer" + is_required: true + + host_port: + type: "integer" + description: "Host port to expose Homer" + default: 8080 + is_required: true + + container_port: + type: "integer" + description: "Container port for Homer" + default: 8080 + is_required: true + + config_volume_name: + type: "string" + description: "Named Docker volume for Homer configuration files" + default: "homer-config" + is_required: true + +execution: + run: + - name: "Pull Homer image" + type: "docker" + properties: + action: "pull" + image: "{{ image }}" + tag: "{{ tag }}" + timeout: 300 + + - name: "Run Homer container" + type: "docker" + properties: + action: "run" + name: "{{ container_name }}" + image: "{{ image }}" + tag: "{{ tag }}" + ports: "{{ host_port }}:{{ container_port }}" + restart: "unless-stopped" + volumes: + - "{{ config_volume_name }}:/www/assets" + timeout: 180 + + validate: + - name: "Check Homer HTTP response" + type: "command" + properties: + cmd: "sh -c 'for i in $(seq 1 30); do code=$(curl -fsS -o /dev/null -w \"%{http_code}\\n\" http://localhost:{{ host_port }} || true); echo \"HTTP $code\"; echo $code | grep -E \"^(200|301|302)$\" && exit 0; sleep 2; done; exit 1'" + timeout: 60 + + - name: "Verify Homer is accessible" + type: "command" + properties: + cmd: "sh -c 'curl -fsS http://localhost:{{ host_port }} | grep -i homer >/dev/null && exit 0 || exit 1'" + timeout: 30 diff --git a/api/templates/deploy-linkding.yaml b/api/templates/deploy-linkding.yaml new file mode 100644 index 000000000..de4efdc21 --- /dev/null +++ b/api/templates/deploy-linkding.yaml @@ -0,0 +1,71 @@ +metadata: + id: "deploy-linkding" + name: "Linkding" + description: "Linkding is a self-hosted bookmark manager that allows you to save, organize, and search your bookmarks. It provides a clean, modern interface for managing your bookmarks with features like tagging, full-text search, and import/export capabilities." + author: "Nixopus Team" + icon: "🔗" + category: "Containers" + type: "install" + version: "1.0.0" + isVerified: false + +variables: + image: + type: "string" + description: "Docker image for Linkding" + default: "sissbruecker/linkding" + is_required: true + + tag: + type: "string" + description: "Docker image tag" + default: "latest" + is_required: true + + container_name: + type: "string" + description: "Name of the Linkding container" + default: "linkding" + is_required: true + + host_port: + type: "integer" + description: "Host port to expose Linkding" + default: 9090 + is_required: true + + container_port: + type: "integer" + description: "Container port for Linkding" + default: 9090 + is_required: true + + data_volume_name: + type: "string" + description: "Named Docker volume for Linkding data persistence" + default: "linkding-data" + is_required: true + +execution: + run: + - name: "Pull Linkding image" + type: "docker" + properties: + action: "pull" + image: "{{ image }}" + tag: "{{ tag }}" + timeout: 300 + + - name: "Run Linkding container" + type: "docker" + properties: + action: "run" + name: "{{ container_name }}" + image: "{{ image }}" + tag: "{{ tag }}" + ports: "{{ host_port }}:{{ container_port }}" + restart: "unless-stopped" + volumes: + - "{{ data_volume_name }}:/etc/linkding/data" + timeout: 180 + diff --git a/api/templates/deploy-minio.yaml b/api/templates/deploy-minio.yaml new file mode 100644 index 000000000..10cee7951 --- /dev/null +++ b/api/templates/deploy-minio.yaml @@ -0,0 +1,99 @@ +metadata: + id: "deploy-minio" + name: "MinIO" + description: "Minio is a high-performance, S3-compatible object storage system designed for private cloud and AI/ML workloads" + author: "Nixopus Team" + icon: "🗄️" + category: "Storage" + type: "install" + version: "1.0.0" + isVerified: false + +variables: + image: + type: "string" + description: "Docker image for MinIO" + default: "minio/minio" + is_required: true + + tag: + type: "string" + description: "Docker image tag" + default: "latest" + is_required: true + + container_name: + type: "string" + description: "Name of the MinIO container" + default: "minio" + is_required: true + + api_port: + type: "integer" + description: "Host port to expose MinIO API" + default: 9000 + is_required: true + + console_port: + type: "integer" + description: "Host port to expose MinIO Console" + default: 9001 + is_required: true + + access_key: + type: "string" + description: "MINIO_ROOT_USER" + default: "minioadmin" + is_required: true + + secret_key: + type: "string" + description: "MINIO_ROOT_PASSWORD" + default: "minioadmin" + is_required: true + + default_buckets: + type: "string" + description: "Comma-separated list of buckets to create (optional, official image ignores this)" + default: "" + is_required: false + + volume_name: + type: "string" + description: "Named Docker volume for MinIO data" + default: "minio-data" + is_required: true + +execution: + run: + - name: "Pull MinIO image" + type: "docker" + properties: + action: "pull" + image: "{{ image }}" + tag: "{{ tag }}" + timeout: 300 + + - name: "Run MinIO container" + type: "docker" + properties: + action: "run" + name: "{{ container_name }}" + image: "{{ image }}" + tag: "{{ tag }}" + ports: "{{ api_port }}:9000,{{ console_port }}:9001" + restart: "unless-stopped" + env: + MINIO_ROOT_USER: "{{ access_key }}" + MINIO_ROOT_PASSWORD: "{{ secret_key }}" + volumes: + - "{{ volume_name }}:/data" + cmd: "server /data --console-address :9001" + timeout: 180 + + validate: + - name: "Check MinIO liveness" + type: "command" + properties: + cmd: "sh -c 'for i in $(seq 1 60); do curl -fsS --connect-timeout 2 -m 5 http://localhost:{{ api_port }}/minio/health/live >/dev/null && exit 0; sleep 3; done; exit 1'" + timeout: 180 diff --git a/api/templates/deploy-myspeed.yaml b/api/templates/deploy-myspeed.yaml new file mode 100644 index 000000000..2e6de244f --- /dev/null +++ b/api/templates/deploy-myspeed.yaml @@ -0,0 +1,83 @@ +metadata: + id: "deploy-myspeed" + name: "MySpeed" + description: "MySpeed is a self-hosted internet speed testing application that provides accurate speed measurements and historical tracking. It offers a clean interface for monitoring your internet connection performance over time." + author: "Nixopus Team" + icon: "⚡" + category: "Monitoring" + type: "install" + version: "1.0.0" + isVerified: false + +variables: + image: + type: "string" + description: "Docker image for MySpeed" + default: "germannewsmaker/myspeed" + is_required: true + + tag: + type: "string" + description: "Docker image tag" + default: "latest" + is_required: true + + container_name: + type: "string" + description: "Name of the MySpeed container" + default: "myspeed" + is_required: true + + host_port: + type: "integer" + description: "Host port to expose MySpeed" + default: 5216 + is_required: true + + container_port: + type: "integer" + description: "Container port for MySpeed" + default: 5216 + is_required: true + + data_volume_name: + type: "string" + description: "Named Docker volume for MySpeed data persistence" + default: "myspeed" + is_required: true + +execution: + run: + - name: "Pull MySpeed image" + type: "docker" + properties: + action: "pull" + image: "{{ image }}" + tag: "{{ tag }}" + timeout: 300 + + - name: "Run MySpeed container" + type: "docker" + properties: + action: "run" + name: "{{ container_name }}" + image: "{{ image }}" + tag: "{{ tag }}" + ports: "{{ host_port }}:{{ container_port }}" + restart: "unless-stopped" + volumes: + - "{{ data_volume_name }}:/myspeed/data" + timeout: 180 + + validate: + - name: "Check MySpeed HTTP response" + type: "command" + properties: + cmd: "sh -c 'for i in $(seq 1 30); do code=$(curl -fsS -o /dev/null -w \"%{http_code}\\n\" http://localhost:{{ host_port }} || true); echo \"HTTP $code\"; echo $code | grep -E \"^(200|301|302)$\" && exit 0; sleep 2; done; exit 1'" + timeout: 60 + + - name: "Verify MySpeed is accessible" + type: "command" + properties: + cmd: "sh -c 'curl -fsS http://localhost:{{ host_port }} | grep -i \"myspeed\" >/dev/null && exit 0 || exit 1'" + timeout: 30 diff --git a/api/templates/deploy-ollama.yaml b/api/templates/deploy-ollama.yaml new file mode 100644 index 000000000..7a4b54652 --- /dev/null +++ b/api/templates/deploy-ollama.yaml @@ -0,0 +1,71 @@ +metadata: + id: "deploy-ollama" + name: "Ollama" + description: "Ollama is a tool for running large language models locally. It provides a simple way to run, create, and share large language models on your own machine with a REST API and command-line interface." + author: "Nixopus Team" + icon: "🤖" + category: "Development" + type: "install" + version: "1.0.0" + isVerified: false + +variables: + image: + type: "string" + description: "Docker image for Ollama" + default: "ollama/ollama" + is_required: true + + tag: + type: "string" + description: "Docker image tag" + default: "latest" + is_required: true + + container_name: + type: "string" + description: "Name of the Ollama container" + default: "ollama" + is_required: true + + host_port: + type: "integer" + description: "Host port to expose Ollama API" + default: 11434 + is_required: true + + container_port: + type: "integer" + description: "Container port for Ollama API" + default: 11434 + is_required: true + + data_volume_name: + type: "string" + description: "Named Docker volume for Ollama models and data" + default: "ollama" + is_required: true + +execution: + run: + - name: "Pull Ollama image" + type: "docker" + properties: + action: "pull" + image: "{{ image }}" + tag: "{{ tag }}" + timeout: 300 + + - name: "Run Ollama container" + type: "docker" + properties: + action: "run" + name: "{{ container_name }}" + image: "{{ image }}" + tag: "{{ tag }}" + ports: "{{ host_port }}:{{ container_port }}" + restart: "unless-stopped" + volumes: + - "{{ data_volume_name }}:/root/.ollama" + timeout: 180 + diff --git a/api/templates/deploy-postgres.yaml b/api/templates/deploy-postgres.yaml new file mode 100644 index 000000000..50928b63c --- /dev/null +++ b/api/templates/deploy-postgres.yaml @@ -0,0 +1,112 @@ +metadata: + id: "deploy-postgres" + name: "PostgreSQL" + description: "PostgreSQL is a powerful, open source object-relational database system with over 30 years of active development that has earned it a strong reputation for reliability, feature robustness, and performance." + author: "Nixopus Team" + icon: "🐘" + category: "Database" + type: "install" + version: "1.0.0" + isVerified: false + +variables: + image: + type: "string" + description: "Docker image for PostgreSQL" + default: "postgres" + is_required: true + + tag: + type: "string" + description: "Docker image tag" + default: "15-alpine" + is_required: true + + container_name: + type: "string" + description: "Name of the PostgreSQL container" + default: "postgres" + is_required: true + + host_port: + type: "integer" + description: "Host port to expose PostgreSQL" + default: 5432 + is_required: true + + container_port: + type: "integer" + description: "Container port for PostgreSQL" + default: 5432 + is_required: true + + database_name: + type: "string" + description: "Name of the default database to create" + default: "postgres" + is_required: true + + postgres_user: + type: "string" + description: "PostgreSQL superuser name" + default: "postgres" + is_required: true + + postgres_password: + type: "string" + description: "PostgreSQL superuser password" + default: "postgres" + is_required: true + + data_volume_name: + type: "string" + description: "Named Docker volume for PostgreSQL data persistence" + default: "postgres-data" + is_required: true + + init_scripts_volume: + type: "string" + description: "Named Docker volume for initialization scripts (optional)" + default: "postgres-init-scripts" + is_required: false + +execution: + run: + - name: "Pull PostgreSQL image" + type: "docker" + properties: + action: "pull" + image: "{{ image }}" + tag: "{{ tag }}" + timeout: 300 + + - name: "Run PostgreSQL container" + type: "docker" + properties: + action: "run" + name: "{{ container_name }}" + image: "{{ image }}" + tag: "{{ tag }}" + ports: "{{ host_port }}:{{ container_port }}" + restart: "unless-stopped" + env: + POSTGRES_DB: "{{ database_name }}" + POSTGRES_USER: "{{ postgres_user }}" + POSTGRES_PASSWORD: "{{ postgres_password }}" + volumes: + - "{{ data_volume_name }}:/var/lib/postgresql/data" + - "{{ init_scripts_volume }}:/docker-entrypoint-initdb.d" + timeout: 180 + + validate: + - name: "Check PostgreSQL connection" + type: "command" + properties: + cmd: "sh -c 'for i in $(seq 1 30); do docker exec {{ container_name }} pg_isready -U {{ postgres_user }} -d {{ database_name }} && exit 0; sleep 2; done; exit 1'" + timeout: 60 + + - name: "Verify PostgreSQL is accepting connections" + type: "command" + properties: + cmd: "sh -c 'for i in $(seq 1 30); do docker exec {{ container_name }} psql -U {{ postgres_user }} -d {{ database_name }} -c \"SELECT 1;\" >/dev/null 2>&1 && exit 0; sleep 2; done; exit 1'" + timeout: 60 diff --git a/api/templates/deploy-redis.yaml b/api/templates/deploy-redis.yaml new file mode 100644 index 000000000..731552762 --- /dev/null +++ b/api/templates/deploy-redis.yaml @@ -0,0 +1,111 @@ +metadata: + id: "deploy-redis" + name: "Redis" + description: "Redis is an open source, in-memory data structure store, used as a database, cache, and message broker. Redis provides data structures such as strings, hashes, lists, sets, sorted sets with range queries, bitmaps, hyperloglogs, geospatial indexes, and streams." + author: "Nixopus Team" + icon: "🔴" + category: "Database" + type: "install" + version: "1.0.0" + isVerified: false + +variables: + image: + type: "string" + description: "Docker image for Redis" + default: "redis" + is_required: true + + tag: + type: "string" + description: "Docker image tag" + default: "7-alpine" + is_required: true + + container_name: + type: "string" + description: "Name of the Redis container" + default: "redis" + is_required: true + + host_port: + type: "integer" + description: "Host port to expose Redis" + default: 6379 + is_required: true + + container_port: + type: "integer" + description: "Container port for Redis" + default: 6379 + is_required: true + + redis_password: + type: "string" + description: "Redis password for authentication (leave empty for no password)" + default: "" + is_required: false + + data_volume_name: + type: "string" + description: "Named Docker volume for Redis data persistence" + default: "redis-data" + is_required: true + + config_volume_name: + type: "string" + description: "Named Docker volume for Redis configuration files (optional)" + default: "redis-config" + is_required: false + + maxmemory: + type: "string" + description: "Maximum memory Redis can use (e.g., '256mb', '1gb')" + default: "256mb" + is_required: false + + maxmemory_policy: + type: "string" + description: "Policy for evicting keys when maxmemory is reached" + default: "allkeys-lru" + is_required: false + +execution: + run: + - name: "Pull Redis image" + type: "docker" + properties: + action: "pull" + image: "{{ image }}" + tag: "{{ tag }}" + timeout: 300 + + - name: "Run Redis container" + type: "docker" + properties: + action: "run" + name: "{{ container_name }}" + image: "{{ image }}" + tag: "{{ tag }}" + ports: "{{ host_port }}:{{ container_port }}" + restart: "unless-stopped" + env: + REDIS_PASSWORD: "{{ redis_password }}" + volumes: + - "{{ data_volume_name }}:/data" + - "{{ config_volume_name }}:/usr/local/etc/redis" + cmd: "redis-server --appendonly yes --maxmemory {{ maxmemory }} --maxmemory-policy {{ maxmemory_policy }}" + timeout: 180 + + validate: + - name: "Check Redis connection" + type: "command" + properties: + cmd: "sh -c 'for i in $(seq 1 30); do docker exec {{ container_name }} redis-cli ping && exit 0; sleep 2; done; exit 1'" + timeout: 60 + + - name: "Verify Redis is accepting commands" + type: "command" + properties: + cmd: "sh -c 'for i in $(seq 1 30); do docker exec {{ container_name }} redis-cli set test_key test_value && docker exec {{ container_name }} redis-cli get test_key | grep -q test_value && docker exec {{ container_name }} redis-cli del test_key && exit 0; sleep 2; done; exit 1'" + timeout: 60 diff --git a/api/templates/deploy-semaphore.yaml b/api/templates/deploy-semaphore.yaml new file mode 100644 index 000000000..d59fb913d --- /dev/null +++ b/api/templates/deploy-semaphore.yaml @@ -0,0 +1,119 @@ +metadata: + id: "deploy-semaphore" + name: "Semaphore" + description: "Semaphore is a modern CI/CD platform with an intuitive interface built for DevOps teams. It supports Ansible, Terraform, OpenTofu, Bash, PowerShell, Python, and Terragrunt, allowing you to effortlessly manage tasks with a modern, user-friendly interface." + author: "Nixopus Team" + icon: "🚦" + category: "Containers" + type: "install" + version: "1.0.0" + isVerified: false + +variables: + image: + type: "string" + description: "Docker image for Semaphore" + default: "public.ecr.aws/semaphore/pro/server" + is_required: true + + tag: + type: "string" + description: "Docker image tag" + default: "v2.16.18" + is_required: true + + container_name: + type: "string" + description: "Name of the Semaphore container" + default: "semaphore" + is_required: true + + host_port: + type: "integer" + description: "Host port to expose Semaphore" + default: 3000 + is_required: true + + container_port: + type: "integer" + description: "Container port for Semaphore" + default: 3000 + is_required: true + + db_dialect: + type: "string" + description: "Database dialect (sqlite, mysql, postgres)" + default: "sqlite" + is_required: true + + admin_username: + type: "string" + description: "Admin username" + default: "admin" + is_required: true + + admin_password: + type: "string" + description: "Admin password" + default: "changeme" + is_required: true + + admin_name: + type: "string" + description: "Admin display name" + default: "Admin" + is_required: true + + admin_email: + type: "string" + description: "Admin email address" + default: "admin@localhost" + is_required: true + + data_volume_name: + type: "string" + description: "Named Docker volume for Semaphore data persistence" + default: "semaphore-data" + is_required: true + +execution: + run: + - name: "Pull Semaphore image" + type: "docker" + properties: + action: "pull" + image: "{{ image }}" + tag: "{{ tag }}" + timeout: 300 + + - name: "Run Semaphore container" + type: "docker" + properties: + action: "run" + name: "{{ container_name }}" + image: "{{ image }}" + tag: "{{ tag }}" + ports: "{{ host_port }}:{{ container_port }}" + restart: "unless-stopped" + env: + SEMAPHORE_DB_DIALECT: "{{ db_dialect }}" + SEMAPHORE_ADMIN: "{{ admin_username }}" + SEMAPHORE_ADMIN_PASSWORD: "{{ admin_password }}" + SEMAPHORE_ADMIN_NAME: "{{ admin_name }}" + SEMAPHORE_ADMIN_EMAIL: "{{ admin_email }}" + volumes: + - "{{ data_volume_name }}:/var/lib/semaphore" + timeout: 300 + + validate: + - name: "Check Semaphore HTTP response" + type: "command" + properties: + cmd: "sh -c 'for i in $(seq 1 60); do code=$(curl -fsS -o /dev/null -w \"%{http_code}\\n\" http://localhost:{{ host_port }} || true); echo \"HTTP $code\"; echo $code | grep -E \"^(200|301|302)$\" && exit 0; sleep 5; done; exit 1'" + timeout: 300 + + - name: "Verify Semaphore is accessible" + type: "command" + properties: + cmd: "sh -c 'curl -fsS http://localhost:{{ host_port }} | grep -i semaphore >/dev/null && exit 0 || exit 1'" + timeout: 30 diff --git a/api/templates/deploy-slash.yaml b/api/templates/deploy-slash.yaml new file mode 100644 index 000000000..836c6b629 --- /dev/null +++ b/api/templates/deploy-slash.yaml @@ -0,0 +1,70 @@ +metadata: + id: "deploy-slash" + name: "Slash" + description: "An open source, self-hosted platform for sharing and managing your most frequently used links. Easily create customizable, human-readable shortcuts to streamline your link management." + author: "Nixopus Team" + icon: "🔗" + category: "Containers" + type: "install" + version: "1.0.0" + isVerified: false + +variables: + image: + type: "string" + description: "Docker image for Slash" + default: "yourselfhosted/slash" + is_required: true + + tag: + type: "string" + description: "Docker image tag" + default: "latest" + is_required: true + + container_name: + type: "string" + description: "Name of the Slash container" + default: "slash" + is_required: true + + host_port: + type: "integer" + description: "Host port to expose Slash" + default: 5231 + is_required: true + + container_port: + type: "integer" + description: "Container port for Slash" + default: 5231 + is_required: true + + data_volume_name: + type: "string" + description: "Named Docker volume for Slash data persistence" + default: "slash-data" + is_required: true + +execution: + run: + - name: "Pull Slash image" + type: "docker" + properties: + action: "pull" + image: "{{ image }}" + tag: "{{ tag }}" + timeout: 300 + + - name: "Run Slash container" + type: "docker" + properties: + action: "run" + name: "{{ container_name }}" + image: "{{ image }}" + tag: "{{ tag }}" + ports: "{{ host_port }}:{{ container_port }}" + restart: "unless-stopped" + volumes: + - "{{ data_volume_name }}:/var/opt/slash" + timeout: 180 diff --git a/api/templates/deploy-speedtest-tracker.yaml b/api/templates/deploy-speedtest-tracker.yaml new file mode 100644 index 000000000..3d916efad --- /dev/null +++ b/api/templates/deploy-speedtest-tracker.yaml @@ -0,0 +1,118 @@ +metadata: + id: "deploy-speedtest-tracker" + name: "Speedtest Tracker" + description: "Speedtest Tracker is a self-hosted application for tracking internet speed test results over time. It provides detailed analytics, historical data, and automated speed testing with support for multiple databases and SSL certificates." + author: "Nixopus Team" + icon: "📡" + category: "Monitoring" + type: "install" + version: "1.0.0" + isVerified: false + +variables: + image: + type: "string" + description: "Docker image for Speedtest Tracker" + default: "lscr.io/linuxserver/speedtest-tracker" + is_required: true + + tag: + type: "string" + description: "Docker image tag" + default: "latest" + is_required: true + + container_name: + type: "string" + description: "Name of the Speedtest Tracker container" + default: "speedtest-tracker" + is_required: true + + http_port: + type: "integer" + description: "Host port to expose HTTP interface" + default: 8080 + is_required: true + + https_port: + type: "integer" + description: "Host port to expose HTTPS interface" + default: 8443 + is_required: true + + container_http_port: + type: "integer" + description: "Container port for HTTP interface" + default: 80 + is_required: true + + container_https_port: + type: "integer" + description: "Container port for HTTPS interface" + default: 443 + is_required: true + + puid: + type: "integer" + description: "User ID for the container user" + default: 1000 + is_required: true + + pgid: + type: "integer" + description: "Group ID for the container user" + default: 1000 + is_required: true + + app_key: + type: "string" + description: "Application key for encryption (generate with: echo -n 'base64:'; openssl rand -base64 32)" + default: "" + is_required: true + + db_connection: + type: "string" + description: "Database connection type (sqlite, mysql, pgsql)" + default: "sqlite" + is_required: true + + data_volume_name: + type: "string" + description: "Named Docker volume for Speedtest Tracker data and configuration" + default: "speedtest-tracker-data" + is_required: true + + ssl_keys_volume_name: + type: "string" + description: "Named Docker volume for custom SSL certificates (optional)" + default: "speedtest-tracker-ssl" + is_required: false + +execution: + run: + - name: "Pull Speedtest Tracker image" + type: "docker" + properties: + action: "pull" + image: "{{ image }}" + tag: "{{ tag }}" + timeout: 300 + + - name: "Run Speedtest Tracker container" + type: "docker" + properties: + action: "run" + name: "{{ container_name }}" + image: "{{ image }}" + tag: "{{ tag }}" + ports: "{{ http_port }}:{{ container_http_port }},{{ https_port }}:{{ container_https_port }}" + restart: "unless-stopped" + env: + PUID: "{{ puid }}" + PGID: "{{ pgid }}" + APP_KEY: "{{ app_key }}" + DB_CONNECTION: "{{ db_connection }}" + volumes: + - "{{ data_volume_name }}:/config" + - "{{ ssl_keys_volume_name }}:/config/keys" + timeout: 300 diff --git a/api/templates/deploy-stirling-pdf.yaml b/api/templates/deploy-stirling-pdf.yaml new file mode 100644 index 000000000..8c03d0869 --- /dev/null +++ b/api/templates/deploy-stirling-pdf.yaml @@ -0,0 +1,115 @@ +metadata: + id: "deploy-stirling-pdf" + name: "Stirling PDF" + description: "Stirling PDF is a powerful, self-hosted web-based PDF manipulation suite." + author: "Nixopus Team" + icon: "📄" + category: "Storage" + type: "install" + version: "1.0.0" + isVerified: false + +variables: + image: + type: "string" + description: "Docker image for Stirling PDF" + default: "docker.stirlingpdf.com/stirlingtools/stirling-pdf" + is_required: true + + tag: + type: "string" + description: "Docker image tag" + default: "latest" + is_required: true + + container_name: + type: "string" + description: "Name of the Stirling PDF container" + default: "stirling-pdf" + is_required: true + + host_port: + type: "integer" + description: "Host port to expose Stirling PDF" + default: 8080 + is_required: true + + tessdata_volume: + type: "string" + description: "Named Docker volume for Tesseract data (/usr/share/tessdata)" + default: "stirlingpdf_tessdata" + is_required: true + + configs_volume: + type: "string" + description: "Named Docker volume for configs (/configs)" + default: "stirlingpdf_configs" + is_required: true + + custom_files_volume: + type: "string" + description: "Named Docker volume for custom files (/customFiles)" + default: "stirlingpdf_custom_files" + is_required: true + + logs_volume: + type: "string" + description: "Named Docker volume for logs (/logs)" + default: "stirlingpdf_logs" + is_required: true + + pipeline_volume: + type: "string" + description: "Named Docker volume for pipeline (/pipeline)" + default: "stirlingpdf_pipeline" + is_required: true + + disable_additional_features: + type: "boolean" + description: "Set DISABLE_ADDITIONAL_FEATURES=true to disable extras" + default: true + is_required: false + + langs: + type: "string" + description: "LANGS environment variable (comma-separated locale codes)" + default: "en_GB" + is_required: false + +execution: + run: + - name: "Pull Stirling PDF image" + type: "docker" + properties: + action: "pull" + image: "{{ image }}" + tag: "{{ tag }}" + timeout: 300 + + - name: "Run Stirling PDF container" + type: "docker" + properties: + action: "run" + name: "{{ container_name }}" + image: "{{ image }}" + tag: "{{ tag }}" + ports: "{{ host_port }}:8080" + restart: "unless-stopped" + env: + DISABLE_ADDITIONAL_FEATURES: "{{ disable_additional_features }}" + LANGS: "{{ langs }}" + volumes: + - "{{ tessdata_volume }}:/usr/share/tessdata" + - "{{ configs_volume }}:/configs" + - "{{ custom_files_volume }}:/customFiles" + - "{{ logs_volume }}:/logs" + - "{{ pipeline_volume }}:/pipeline" + timeout: 180 + + validate: + - name: "Check HTTP response from Stirling PDF" + type: "command" + properties: + cmd: "sh -c 'for i in $(seq 1 30); do code=$(curl -fsS -o /dev/null -w \"%{http_code}\\n\" http://localhost:{{ host_port }} || true); echo \"HTTP $code\"; echo $code | grep -E \"^(200|301|302)$\" && exit 0; sleep 2; done; exit 1'" + timeout: 60 + diff --git a/api/templates/deploy-uptime-kuma.yaml b/api/templates/deploy-uptime-kuma.yaml new file mode 100644 index 000000000..890adfbd9 --- /dev/null +++ b/api/templates/deploy-uptime-kuma.yaml @@ -0,0 +1,73 @@ +metadata: + id: "deploy-uptime-kuma" + name: "Uptime Kuma" + description: "Uptime Kuma is a self-hosted monitoring tool" + author: "Nixopus Team" + icon: "🕒" + category: "Monitoring" + type: "install" + version: "1.0.0" + isVerified: false + +variables: + image: + type: "string" + description: "Docker image for Uptime Kuma" + default: "louislam/uptime-kuma" + is_required: true + + tag: + type: "string" + description: "Docker image tag" + default: "1" + is_required: true + + container_name: + type: "string" + description: "Name of the Uptime Kuma container" + default: "uptime-kuma" + is_required: true + + host_port: + type: "integer" + description: "Host port to expose Uptime Kuma" + default: 3001 + is_required: true + + volume_name: + type: "string" + description: "Named Docker volume for persistent data" + default: "uptime-kuma" + is_required: true + +execution: + run: + - name: "Pull Uptime Kuma image" + type: "docker" + properties: + action: "pull" + image: "{{ image }}" + tag: "{{ tag }}" + timeout: 300 + + - name: "Run Uptime Kuma container" + type: "docker" + properties: + action: "run" + name: "{{ container_name }}" + image: "{{ image }}" + tag: "{{ tag }}" + ports: "{{ host_port }}:3001" + restart: "always" + volumes: + - "{{ volume_name }}:/app/data" + timeout: 180 + + validate: + - name: "Check HTTP response from Uptime Kuma" + type: "command" + properties: + cmd: "sh -c 'for i in $(seq 1 30); do code=$(curl -fsS -o /dev/null -w \"%{http_code}\\n\" http://localhost:{{ host_port }} || true); echo \"HTTP $code\"; echo $code | grep -E \"^(200|301|302)$\" && exit 0; sleep 2; done; exit 1'" + timeout: 60 + + diff --git a/api/templates/deploy-url-to-png.yaml b/api/templates/deploy-url-to-png.yaml new file mode 100644 index 000000000..b384065ed --- /dev/null +++ b/api/templates/deploy-url-to-png.yaml @@ -0,0 +1,75 @@ +metadata: + id: "deploy-url-to-png" + name: "URL to PNG" + description: "A URL to PNG generator over HTTP with a fairly simple API accessed via query params passed to the server. Convert any webpage to a PNG image with customizable parameters like width, height, and quality." + author: "Nixopus Team" + icon: "🖼️" + category: "Containers" + type: "install" + version: "1.0.0" + isVerified: false + +variables: + image: + type: "string" + description: "Docker image for URL to PNG" + default: "ghcr.io/jasonraimondi/url-to-png" + is_required: true + + tag: + type: "string" + description: "Docker image tag" + default: "latest" + is_required: true + + container_name: + type: "string" + description: "Name of the URL to PNG container" + default: "url-to-png" + is_required: true + + host_port: + type: "integer" + description: "Host port to expose URL to PNG service" + default: 3089 + is_required: true + + container_port: + type: "integer" + description: "Container port for URL to PNG service" + default: 3089 + is_required: true + +execution: + run: + - name: "Pull URL to PNG image" + type: "docker" + properties: + action: "pull" + image: "{{ image }}" + tag: "{{ tag }}" + timeout: 300 + + - name: "Run URL to PNG container" + type: "docker" + properties: + action: "run" + name: "{{ container_name }}" + image: "{{ image }}" + tag: "{{ tag }}" + ports: "{{ host_port }}:{{ container_port }}" + restart: "unless-stopped" + timeout: 180 + + validate: + - name: "Check URL to PNG HTTP response" + type: "command" + properties: + cmd: "sh -c 'for i in $(seq 1 30); do code=$(curl -fsS -o /dev/null -w \"%{http_code}\\n\" http://localhost:{{ host_port }} || true); echo \"HTTP $code\"; echo $code | grep -E \"^(200|301|302)$\" && exit 0; sleep 2; done; exit 1'" + timeout: 60 + + - name: "Verify URL to PNG service is accessible" + type: "command" + properties: + cmd: "sh -c 'curl -fsS http://localhost:{{ host_port }} | grep -i \"url.*png\" >/dev/null && exit 0 || exit 1'" + timeout: 30 diff --git a/api/templates/fail2ban.yaml b/api/templates/fail2ban.yaml new file mode 100644 index 000000000..9f22c0d91 --- /dev/null +++ b/api/templates/fail2ban.yaml @@ -0,0 +1,77 @@ +metadata: + id: "fail2ban-ssh" + name: "Fail2ban SSH Protection" + description: "Protects SSH from brute-force attacks using Fail2ban with basic configuration." + author: "Nixopus Team" + icon: "🔒" + category: "Security" + type: "install" + version: "1.0.0" + isVerified: false + +variables: + ssh_port: + type: "integer" + description: "SSH port to monitor" + default: 22 + is_required: true + + max_retry: + type: "integer" + description: "Maximum SSH login attempts before ban" + default: 5 + is_required: true + + ban_time: + type: "string" + description: "Ban duration (e.g., 10m, 1h, 1d)" + default: "1h" + is_required: true + +execution: + install: + - name: "Install fail2ban package" + type: "package" + properties: + name: "fail2ban" + action: "install" + + - name: "Create SSH jail configuration" + type: "file" + properties: + action: "create" + dest: "/etc/fail2ban/jail.local" + content: | + [DEFAULT] + ignoreip = 127.0.0.1/8 ::1 + + [sshd] + enabled = true + port = {{ ssh_port }} + filter = sshd + logpath = /var/log/auth.log + maxretry = {{ max_retry }} + bantime = {{ ban_time }} + mode: "0644" + owner: "root" + group: "root" + + - name: "Start fail2ban service" + type: "service" + properties: + name: "fail2ban" + action: "start" + state: "started" + + validate: + - name: "Verify fail2ban service is running" + type: "command" + properties: + cmd: "systemctl is-active fail2ban" + timeout: 10 + + - name: "Check SSH jail status" + type: "command" + properties: + cmd: "fail2ban-client status sshd" + timeout: 30 diff --git a/docs/extensions/index.md b/docs/extensions/index.md new file mode 100644 index 000000000..8c781f826 --- /dev/null +++ b/docs/extensions/index.md @@ -0,0 +1,78 @@ +### Nixopus Extensions - Current Supported Spec + +#### Execution Phases +- run: main steps +- validate: post-check steps + +#### Variables +- Refer with `{{ variable_name }}` inside properties +- Special: `{{ uploaded_file_path }}` when a file is sent via multipart to the run endpoint + +#### Step Types + +- command + - properties: + - cmd: string (required) — shell command to execute + - notes: variables interpolated; runs over SSH + +- file + - properties: + - action: string (required) — one of: `move`, `copy`, `upload`, `delete`, `mkdir` + - src: string — source path (required for move/copy/upload) + - dest: string — destination path (required for move/copy/upload; target for delete/mkdir) + - behavior: + - move: SFTP rename + - copy: remote `cp -r` + - upload: SFTP upload from local API host path to remote dest + - delete: SFTP remove + - mkdir: SFTP mkdir -p + +- service + - properties: + - name: string (required) + - action: string (required) — e.g. `start`, `stop`, `restart`, `enable`, `disable` + - behavior: prefers `systemctl`; falls back to `service` + +- user + - properties: + - action: string (required) — one of: `ensure`, `modify`, `delete`, `check`, `add_groups`, `remove_groups` + - username: string (required) + - shell: string (optional) — e.g. `/bin/bash` + - home: string (optional) — e.g. `/home/deploy` + - groups: string (optional) — comma-separated groups, e.g. `sudo,docker` + - behavior: + - ensure: creates user if missing, then applies shell/home/groups + - modify: applies provided shell/home/groups + - delete: removes user and home (`userdel -r`) + - check: prints `exists` or `missing` to step output + - add_groups: adds user to each group listed in `groups` + - remove_groups: removes user from each group listed in `groups` + +#### Common Options +- ignore_errors: boolean (optional) — on failure, continue to next step +- timeout: number (optional, seconds) — applied to command/service steps via `timeout` + +#### Example + +```yaml +execution: + run: + - name: "Upload file to remote" + type: "file" + properties: + action: "upload" + src: "{{ uploaded_file_path }}" + dest: "/tmp/remote.bin" + + - name: "Restart Nginx" + type: "service" + properties: + name: "nginx" + action: "restart" + + validate: + - name: "Check file exists" + type: "command" + properties: + cmd: "test -f /tmp/remote.bin" +``` diff --git a/view/app/containers/page.tsx b/view/app/containers/page.tsx index e91021713..db6e9cc54 100644 --- a/view/app/containers/page.tsx +++ b/view/app/containers/page.tsx @@ -193,7 +193,9 @@ export default function ContainersPage() { {totalCount > 0 && (
{JSON.stringify(v, null, 2)}+ ) : ( + String(v) + )} +
+ Try adjusting your search or filters to find more extensions. +
+{t('extensions.subtitle')}
+