diff --git a/coolify/project/model.go b/coolify/project/model.go index 93dbf4b..d8ce47b 100644 --- a/coolify/project/model.go +++ b/coolify/project/model.go @@ -11,10 +11,8 @@ type ProjectModel struct { Description types.String `tfsdk:"description"` } -type ProjectWithEnvironmentsModel struct { - Id types.String `tfsdk:"id"` - Name types.String `tfsdk:"name"` - Description types.String `tfsdk:"description"` +type ProjectDataSourceModel struct { + ProjectModel Environments *[]EnvironmentModel `tfsdk:"environments"` } @@ -27,7 +25,7 @@ type EnvironmentModel struct { UpdatedAt types.String `tfsdk:"updated_at"` } -func mapProjectDataSourceModel(projectData *ProjectWithEnvironmentsModel, project *coolify_sdk.Project) { +func mapCommonProjectFields(projectData *ProjectModel, project *coolify_sdk.Project) { projectData.Id = types.StringValue(project.UUID) projectData.Name = types.StringValue(project.Name) if project.Description != nil { @@ -35,6 +33,10 @@ func mapProjectDataSourceModel(projectData *ProjectWithEnvironmentsModel, projec } else { projectData.Description = types.StringNull() } +} + +func mapProjectDataSourceModel(projectData *ProjectDataSourceModel, project *coolify_sdk.Project) { + mapCommonProjectFields(&projectData.ProjectModel, project) if project.Environments != nil { environments := make([]EnvironmentModel, len(project.Environments)) @@ -60,11 +62,5 @@ func mapProjectDataSourceModel(projectData *ProjectWithEnvironmentsModel, projec } func mapProjectResourceModel(projectData *ProjectModel, project *coolify_sdk.Project) { - projectData.Id = types.StringValue(project.UUID) - projectData.Name = types.StringValue(project.Name) - if project.Description != nil { - projectData.Description = types.StringValue(*project.Description) - } else { - projectData.Description = types.StringNull() - } + mapCommonProjectFields(projectData, project) } diff --git a/coolify/project/read.go b/coolify/project/read.go index ed51ff8..72f35a4 100644 --- a/coolify/project/read.go +++ b/coolify/project/read.go @@ -41,7 +41,7 @@ func readProject(ctx context.Context, client coolify_sdk.Sdk, id types.String, n } func (d *ProjectDataSource) ReadProjectDatasource(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { - var project ProjectWithEnvironmentsModel + var project ProjectDataSourceModel resp.Diagnostics.Append(req.Config.Get(ctx, &project)...) if resp.Diagnostics.HasError() { diff --git a/coolify/provider.go b/coolify/provider.go index bcd66d2..b7741a2 100644 --- a/coolify/provider.go +++ b/coolify/provider.go @@ -110,6 +110,7 @@ func getAPIToken(token types.String, resp *provider.ConfigureResponse) string { func (p *CoolifyProvider) Resources(ctx context.Context) []func() resource.Resource { return []func() resource.Resource{ project.NewProjectResource, + server.NewServerResource, } } diff --git a/coolify/server/create.go b/coolify/server/create.go new file mode 100644 index 0000000..b5b15a4 --- /dev/null +++ b/coolify/server/create.go @@ -0,0 +1,57 @@ +package server + +import ( + "context" + "fmt" + + coolify_sdk "github.com/marconneves/coolify-sdk-go" + + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-log/tflog" +) + +func (r *ServerResource) CreateServer(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data ServerModel + + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + + tflog.Debug(ctx, "Planned data before creation", map[string]interface{}{ + "name": data.Name.ValueString(), + "ip": data.IP.ValueString(), + "description": data.Description.ValueString(), + }) + + createDTO := coolify_sdk.CreateServerDTO{ + Name: data.Name.ValueString(), + IP: data.IP.ValueString(), + Description: data.Description.ValueString(), + IsBuildServer: false, + Port: int(data.Port.ValueInt32()), + User: data.UUID.ValueString(), + PrivateKeyUUID: data.PrivateKeyUUID.ValueString(), + } + + serverID, err := r.client.Server.Create(&createDTO) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to create server, got error: %s", err)) + return + } + + data.UUID = types.StringValue(*serverID) + tflog.Trace(ctx, "Created a server", map[string]interface{}{"server_id": serverID}) + + tflog.Debug(ctx, "Data after server creation", map[string]interface{}{ + "uuid": data.UUID.ValueString(), + "name": data.Name.ValueString(), + "ip": data.IP.ValueString(), + "description": data.Description.ValueString(), + }) + + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) + + tflog.Debug(ctx, "Server state saved to file after creation") +} diff --git a/coolify/server/delete.go b/coolify/server/delete.go new file mode 100644 index 0000000..8ddb6e8 --- /dev/null +++ b/coolify/server/delete.go @@ -0,0 +1,26 @@ +package server + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-log/tflog" +) + +func (r *ServerResource) DeleteServer(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var data ServerModel + + resp.Diagnostics.Append(req.State.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + + err := r.client.Server.Delete(data.UUID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to delete server, got error: %s", err)) + return + } + + tflog.Trace(ctx, "Deleted server", map[string]interface{}{"server_id": data.UUID.ValueString()}) +} diff --git a/coolify/server/model.go b/coolify/server/model.go index dbdd668..dee0fcc 100644 --- a/coolify/server/model.go +++ b/coolify/server/model.go @@ -1,6 +1,8 @@ package server import ( + "fmt" + "strconv" "time" "github.com/hashicorp/terraform-plugin-framework/types" @@ -8,21 +10,25 @@ import ( ) type ServerModel struct { - UUID types.String `tfsdk:"uuid"` - Name types.String `tfsdk:"name"` - IP types.String `tfsdk:"ip"` - Description types.String `tfsdk:"description"` + UUID types.String `tfsdk:"uuid"` + Name types.String `tfsdk:"name"` + Description types.String `tfsdk:"description"` + IP types.String `tfsdk:"ip"` + Port types.Int32 `tfsdk:"port"` + User types.String `tfsdk:"user"` + PrivateKeyUUID types.String `tfsdk:"private_key_uuid"` +} + +type ServerDataSourceModel struct { + ServerModel HighDiskUsageNotificationSent types.Bool `tfsdk:"high_disk_usage_notification_sent"` LogDrainNotificationSent types.Bool `tfsdk:"log_drain_notification_sent"` - Port types.String `tfsdk:"port"` - PrivateKeyID types.Int64 `tfsdk:"private_key_id"` Proxy *ProxyModel `tfsdk:"proxy"` Settings *SettingsModel `tfsdk:"settings"` SwarmCluster types.String `tfsdk:"swarm_cluster"` TeamID types.Int64 `tfsdk:"team_id"` UnreachableCount types.Int64 `tfsdk:"unreachable_count"` UnreachableNotificationSent types.Bool `tfsdk:"unreachable_notification_sent"` - User types.String `tfsdk:"user"` ValidationLogs types.String `tfsdk:"validation_logs"` CreatedAt types.String `tfsdk:"created_at"` UpdatedAt types.String `tfsdk:"updated_at"` @@ -75,7 +81,7 @@ type SettingsModel struct { UpdatedAt types.String `tfsdk:"updated_at"` } -func mapServerModel(data *ServerModel, server *coolify_sdk.Server) { +func mapCommonServerFields(data *ServerModel, server *coolify_sdk.Server) { data.UUID = types.StringValue(server.UUID) data.IP = types.StringValue(server.IP) data.Name = types.StringValue(server.Name) @@ -84,10 +90,25 @@ func mapServerModel(data *ServerModel, server *coolify_sdk.Server) { data.Description = types.StringValue(*server.Description) } + portInt64, err := strconv.ParseInt(server.Port, 10, 32) + if err != nil { + fmt.Println("Erro na conversão:", err) + return + } + + port := int32(portInt64) + + data.Port = types.Int32Value(port) +} + +func mapServerDataSourceModel(data *ServerDataSourceModel, server *coolify_sdk.Server) { + mapCommonServerFields(&data.ServerModel, server) + + // TODO: Get private key uuid founding in api + data.PrivateKeyUUID = types.StringValue(server.UUID) + data.HighDiskUsageNotificationSent = types.BoolValue(server.HighDiskUsageNotificationSent) data.LogDrainNotificationSent = types.BoolValue(server.LogDrainNotificationSent) - data.Port = types.StringValue(server.Port) - data.PrivateKeyID = types.Int64Value(int64(server.PrivateKeyID)) if server.Proxy != nil { proxy := ProxyModel{} @@ -186,3 +207,7 @@ func mapServerModel(data *ServerModel, server *coolify_sdk.Server) { data.CreatedAt = types.StringValue(server.CreatedAt.Format(time.RFC3339)) data.UpdatedAt = types.StringValue(server.UpdatedAt.Format(time.RFC3339)) } + +func mapServerResourceModel(projectData *ServerModel, project *coolify_sdk.Server) { + mapCommonServerFields(projectData, project) +} diff --git a/coolify/server/read.go b/coolify/server/read.go index 8a56731..63b513c 100644 --- a/coolify/server/read.go +++ b/coolify/server/read.go @@ -4,8 +4,11 @@ import ( "context" "fmt" + "github.com/hashicorp/terraform-plugin-framework/datasource" "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-log/tflog" coolify_sdk "github.com/marconneves/coolify-sdk-go" ) @@ -36,3 +39,54 @@ func readServer(ctx context.Context, client coolify_sdk.Sdk, id types.String, na return server, diags } + +func (s *ServerDataSource) ReadServerDataSource(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + var server ServerDataSourceModel + + diags := req.Config.Get(ctx, &server) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + serverSaved, diags := readServer(ctx, *s.client, server.UUID, server.Name) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + mapServerDataSourceModel(&server, serverSaved) + + tflog.Trace(ctx, "Successfully read team data", map[string]interface{}{ + "server_uuid": serverSaved.UUID, + "name": serverSaved.Name, + }) + + resp.Diagnostics.Append(resp.State.Set(ctx, &server)...) + +} + +func (r *ServerResource) ReadServerResource(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var server ServerModel + + diags := req.State.Get(ctx, &server) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + serverSaved, diags := readServer(ctx, *r.client, server.UUID, server.Name) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + mapServerResourceModel(&server, serverSaved) + + tflog.Trace(ctx, "Successfully read server data", map[string]interface{}{ + "server_uuid": serverSaved.UUID, + "name": serverSaved.Name, + }) + + resp.Diagnostics.Append(resp.State.Set(ctx, &server)...) +} diff --git a/coolify/server/server_data_source.go b/coolify/server/server_data_source.go index e5a1646..067c433 100644 --- a/coolify/server/server_data_source.go +++ b/coolify/server/server_data_source.go @@ -5,7 +5,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/datasource" "github.com/hashicorp/terraform-plugin-framework/datasource/schema" - "github.com/hashicorp/terraform-plugin-log/tflog" coolify_sdk "github.com/marconneves/coolify-sdk-go" configure "github.com/marconneves/terraform-provider-coolify/shared" ) @@ -51,11 +50,11 @@ func (s *ServerDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, Computed: true, Description: "Indicates if a log drain notification has been sent.", }, - "port": schema.StringAttribute{ + "port": schema.Int32Attribute{ Computed: true, Description: "The port used by the server.", }, - "private_key_id": schema.Int64Attribute{ + "private_key_uuid": schema.StringAttribute{ Computed: true, Description: "The ID of the private key associated with the server.", }, @@ -276,27 +275,5 @@ func (s *ServerDataSource) Configure(ctx context.Context, req datasource.Configu } func (s *ServerDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { - var server ServerModel - - diags := req.Config.Get(ctx, &server) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - serverSaved, diags := readServer(ctx, *s.client, server.UUID, server.Name) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - mapServerModel(&server, serverSaved) - - tflog.Trace(ctx, "Successfully read team data", map[string]interface{}{ - "server_uuid": serverSaved.UUID, - "name": serverSaved.Name, - }) - - resp.Diagnostics.Append(resp.State.Set(ctx, &server)...) - + s.ReadServerDataSource(ctx, req, resp) } diff --git a/coolify/server/server_data_source_test.go b/coolify/server/server_data_source_test.go index 1fedf9f..7db6471 100644 --- a/coolify/server/server_data_source_test.go +++ b/coolify/server/server_data_source_test.go @@ -14,7 +14,7 @@ func TestAccServerDataSource(t *testing.T) { ProtoV6ProviderFactories: tests.ProviderFactories, Steps: []resource.TestStep{ { - Config: testAccServerDataSourceConfig(`uuid = "tks4gcsgwowws8gswggkw8gs"`), + Config: testAccServerDataSourceConfig(`uuid = "kk4wskwk0k0sssc8ckcss44c"`), Check: testAccServerDataSourceCheck(), }, { @@ -35,7 +35,7 @@ data "coolify_server" "test" { func testAccServerDataSourceCheck() resource.TestCheckFunc { return resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttr("data.coolify_server.test", "uuid", "tks4gcsgwowws8gswggkw8gs"), + resource.TestCheckResourceAttr("data.coolify_server.test", "uuid", "kk4wskwk0k0sssc8ckcss44c"), resource.TestCheckResourceAttr("data.coolify_server.test", "name", "example-server"), resource.TestCheckResourceAttr("data.coolify_server.test", "ip", "localhost"), resource.TestCheckResourceAttr("data.coolify_server.test", "high_disk_usage_notification_sent", "false"), diff --git a/coolify/server/server_resource.go b/coolify/server/server_resource.go new file mode 100644 index 0000000..4693051 --- /dev/null +++ b/coolify/server/server_resource.go @@ -0,0 +1,88 @@ +package server + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + coolify_sdk "github.com/marconneves/coolify-sdk-go" + configure "github.com/marconneves/terraform-provider-coolify/shared" +) + +var _ resource.Resource = &ServerResource{} +var _ resource.ResourceWithImportState = &ServerResource{} + +func NewServerResource() resource.Resource { + return &ServerResource{} +} + +type ServerResource struct { + client *coolify_sdk.Sdk +} + +func (r *ServerResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_server" +} + +func (r *ServerResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + MarkdownDescription: "Manage Coolify servers", + + Attributes: map[string]schema.Attribute{ + "uuid": schema.StringAttribute{ + MarkdownDescription: "Server identifier", + Computed: true, + Optional: true, + }, + "name": schema.StringAttribute{ + MarkdownDescription: "Server name", + Required: true, + }, + "description": schema.StringAttribute{ + MarkdownDescription: "Server description", + Optional: true, + }, + "ip": schema.StringAttribute{ + MarkdownDescription: "Server IP address", + Required: true, + }, + "port": schema.Int32Attribute{ + MarkdownDescription: "Server port", + Required: true, + }, + "user": schema.StringAttribute{ + MarkdownDescription: "Server user", + Required: true, + }, + "private_key_uuid": schema.StringAttribute{ + Required: true, + MarkdownDescription: "The UUID of the private key associated with the server.", + }, + }, + } +} + +func (r *ServerResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + configure.ConfigureClient(ctx, req, &r.client) +} + +func (r *ServerResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + r.CreateServer(ctx, req, resp) +} + +func (r *ServerResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + r.ReadServerResource(ctx, req, resp) +} + +func (r *ServerResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + r.UpdateServer(ctx, req, resp) +} + +func (r *ServerResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + r.DeleteServer(ctx, req, resp) +} + +func (r *ServerResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} diff --git a/coolify/server/server_resource_test.go b/coolify/server/server_resource_test.go new file mode 100644 index 0000000..856b104 --- /dev/null +++ b/coolify/server/server_resource_test.go @@ -0,0 +1,55 @@ +package server_test + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/marconneves/terraform-provider-coolify/shared/tests" +) + +func TestAccServerResource(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { tests.TestAccPreCheck(t) }, + ProtoV6ProviderFactories: tests.ProviderFactories, + Steps: []resource.TestStep{ + { + Config: testAccServerResourceConfig("example-server-test", "192.168.1.1", "22", "user", "xso0ooc4o0w4cswcwws8gswg"), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("coolify_server.test", "name", "example-server-test"), + resource.TestCheckResourceAttr("coolify_server.test", "ip", "192.168.1.1"), + resource.TestCheckResourceAttr("coolify_server.test", "port", "22"), + resource.TestCheckResourceAttr("coolify_server.test", "user", "user"), + resource.TestCheckResourceAttr("coolify_server.test", "private_key_uuid", "xso0ooc4o0w4cswcwws8gswg"), + ), + }, + // { + // ResourceName: "coolify_server.test", + // ImportState: true, + // ImportStateVerify: true, + // }, + // { + // Config: testAccServerResourceConfig("updated-server", "192.168.1.2", "2222", "newuser", "xso0ooc4o0w4cswcwws8gswg"), + // Check: resource.ComposeAggregateTestCheckFunc( + // resource.TestCheckResourceAttr("coolify_server.test", "name", "updated-server"), + // resource.TestCheckResourceAttr("coolify_server.test", "ip", "192.168.1.2"), + // resource.TestCheckResourceAttr("coolify_server.test", "port", "2222"), + // resource.TestCheckResourceAttr("coolify_server.test", "user", "newuser"), + // resource.TestCheckResourceAttr("coolify_server.test", "private_key_uuid", "xso0ooc4o0w4cswcwws8gswg"), + // ), + // }, + }, + }) +} + +func testAccServerResourceConfig(name, ip, port, user, privateKeyUUID string) string { + return fmt.Sprintf(` + resource "coolify_server" "test" { + name = %[1]q + ip = %[2]q + port = %[3]q + user = %[4]q + private_key_uuid = %[5]q + } + `, name, ip, port, user, privateKeyUUID) +} diff --git a/coolify/server/update.go b/coolify/server/update.go new file mode 100644 index 0000000..ca7ce5f --- /dev/null +++ b/coolify/server/update.go @@ -0,0 +1,65 @@ +package server + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-framework/resource" + coolify_sdk "github.com/marconneves/coolify-sdk-go" +) + +func (r *ServerResource) UpdateServer(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan ServerModel + var state ServerModel + + diags := req.Plan.Get(ctx, &plan) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + diags = req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + if r.client == nil { + resp.Diagnostics.AddError("Client Error", "Client is not configured. Please ensure the provider is properly configured.") + return + } + + if state.UUID.IsNull() || state.UUID.ValueString() == "" { + resp.Diagnostics.AddError("ID Missing", "UUID is required to update the server.") + return + } + + updateDTO := coolify_sdk.UpdateServerDTO{ + Name: plan.Name.ValueString(), + IP: plan.IP.ValueString(), + Description: plan.Description.ValueString(), + IsBuildServer: false, + Port: int(plan.Port.ValueInt32()), + User: plan.UUID.ValueString(), + PrivateKeyUUID: plan.PrivateKeyUUID.ValueString(), + } + + err := r.client.Server.Update(state.UUID.ValueString(), &updateDTO) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to update server, got error: %s", err)) + return + } + + server, err := r.client.Server.Get(state.UUID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read server after update, got error: %s", err)) + return + } + + var newState ServerModel + + mapCommonServerFields(&newState, server) + + diags = resp.State.Set(ctx, &newState) + resp.Diagnostics.Append(diags...) +} diff --git a/go.mod b/go.mod index fdfbe36..8b016f6 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/hashicorp/terraform-plugin-go v0.25.0 github.com/hashicorp/terraform-plugin-log v0.9.0 github.com/hashicorp/terraform-plugin-testing v1.10.0 - github.com/marconneves/coolify-sdk-go v1.5.2 + github.com/marconneves/coolify-sdk-go v1.5.3 github.com/stretchr/testify v1.8.3 ) diff --git a/go.sum b/go.sum index e5d41ea..7f85524 100644 --- a/go.sum +++ b/go.sum @@ -121,6 +121,8 @@ github.com/marconneves/coolify-sdk-go v1.5.1 h1:UvGteZVmiuVgbxC/+d9bx/eOR1yUi3Mm github.com/marconneves/coolify-sdk-go v1.5.1/go.mod h1:uHVsnJhkh5XHqwR+vX/sFJnfn7vfmbiWBgq0aOdoiV8= github.com/marconneves/coolify-sdk-go v1.5.2 h1:bMr2Y4iVVO71vGJcuXxYPnf/kOrFF8kADdRF8pjk9i8= github.com/marconneves/coolify-sdk-go v1.5.2/go.mod h1:uHVsnJhkh5XHqwR+vX/sFJnfn7vfmbiWBgq0aOdoiV8= +github.com/marconneves/coolify-sdk-go v1.5.3 h1:+f4D5fcVt3Vn9DJxHf5VYdzK2UqPDUcuZB7C6WONH4c= +github.com/marconneves/coolify-sdk-go v1.5.3/go.mod h1:uHVsnJhkh5XHqwR+vX/sFJnfn7vfmbiWBgq0aOdoiV8= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=