* feat: support customizing guest portal logo and background with `unifi_portal_file` and `unifi_setting_guest_access` resources * ci: run acceptance tests on go.mod changes * f
180 lines
6.2 KiB
Go
180 lines
6.2 KiB
Go
package portal
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"github.com/filipowm/go-unifi/unifi"
|
|
"github.com/filipowm/terraform-provider-unifi/internal/provider/base"
|
|
ut "github.com/filipowm/terraform-provider-unifi/internal/provider/types"
|
|
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
|
|
"github.com/hashicorp/terraform-plugin-framework/diag"
|
|
"github.com/hashicorp/terraform-plugin-framework/resource"
|
|
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
|
|
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
|
|
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
|
|
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
|
|
"github.com/hashicorp/terraform-plugin-framework/types"
|
|
"os"
|
|
)
|
|
|
|
var (
|
|
_ resource.Resource = &portalFileResource{}
|
|
_ resource.ResourceWithConfigure = &portalFileResource{}
|
|
_ resource.ResourceWithImportState = &portalFileResource{}
|
|
_ base.Resource = &portalFileResource{}
|
|
)
|
|
|
|
type portalFileResource struct {
|
|
*base.GenericResource[*portalFileModel]
|
|
}
|
|
|
|
type portalFileModel struct {
|
|
base.Model
|
|
Filename types.String `tfsdk:"filename"`
|
|
FilePath types.String `tfsdk:"file_path"`
|
|
ContentType types.String `tfsdk:"content_type"`
|
|
FileSize types.Int64 `tfsdk:"file_size"`
|
|
MD5 types.String `tfsdk:"md5"`
|
|
URL types.String `tfsdk:"url"`
|
|
LastModified types.Int64 `tfsdk:"last_modified"`
|
|
}
|
|
|
|
func (m *portalFileModel) Merge(_ context.Context, data interface{}) diag.Diagnostics {
|
|
var diags diag.Diagnostics
|
|
portalFile, ok := data.(*unifi.PortalFile)
|
|
if !ok {
|
|
diags.AddError("Invalid data type", fmt.Sprintf("Expected *unifi.PortalFile, got: %T", data))
|
|
return diags
|
|
}
|
|
|
|
m.ID = types.StringValue(portalFile.ID)
|
|
m.Filename = types.StringValue(portalFile.Filename)
|
|
m.ContentType = types.StringValue(portalFile.ContentType)
|
|
m.FileSize = types.Int64Value(int64(portalFile.FileSize))
|
|
m.MD5 = types.StringValue(portalFile.MD5)
|
|
m.URL = types.StringValue(portalFile.URL)
|
|
m.LastModified = types.Int64Value(int64(portalFile.LastModified))
|
|
|
|
return diags
|
|
}
|
|
|
|
func (m *portalFileModel) AsUnifiModel(_ context.Context) (interface{}, diag.Diagnostics) {
|
|
// Not used for upload - we don't convert the model to a UniFi model
|
|
// The file path is used directly for upload
|
|
return nil, diag.Diagnostics{}
|
|
}
|
|
|
|
func NewPortalFileResource() resource.Resource {
|
|
return &portalFileResource{
|
|
GenericResource: base.NewGenericResource(
|
|
"unifi_portal_file",
|
|
func() *portalFileModel { return &portalFileModel{} },
|
|
base.ResourceFunctions{
|
|
Read: func(ctx context.Context, client *base.Client, site, id string) (interface{}, error) {
|
|
return client.GetPortalFile(ctx, site, id)
|
|
},
|
|
Create: nil, // Custom implementation in CreateWithContext
|
|
Update: nil, // Portal files cannot be updated, only replaced
|
|
Delete: func(ctx context.Context, client *base.Client, site, id string) error {
|
|
return client.DeletePortalFile(ctx, site, id)
|
|
},
|
|
},
|
|
),
|
|
}
|
|
}
|
|
|
|
func (r *portalFileResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
|
|
resp.Schema = schema.Schema{
|
|
MarkdownDescription: "The `unifi_portal_file` resource manages files uploaded to the UniFi guest portal. " +
|
|
"This resource allows you to upload images that can be used in customizing " +
|
|
"the UniFi guest portal interface.\n\n" +
|
|
"**Note:** This resource uploads files to the UniFi controller. The file must exist on the local filesystem " +
|
|
"where Terraform is executed.",
|
|
|
|
Attributes: map[string]schema.Attribute{
|
|
"id": ut.ID(),
|
|
"site": ut.SiteAttribute(),
|
|
"file_path": schema.StringAttribute{
|
|
MarkdownDescription: "Path to the file on the local filesystem to upload to the UniFi controller. " +
|
|
"The file must exist and be readable.",
|
|
Required: true,
|
|
PlanModifiers: []planmodifier.String{
|
|
stringplanmodifier.RequiresReplace(),
|
|
},
|
|
Validators: []validator.String{
|
|
stringvalidator.LengthAtLeast(1),
|
|
},
|
|
},
|
|
"filename": schema.StringAttribute{
|
|
MarkdownDescription: "Name of the file as stored in the UniFi controller.",
|
|
Computed: true,
|
|
},
|
|
"content_type": schema.StringAttribute{
|
|
MarkdownDescription: "MIME type of the file.",
|
|
Computed: true,
|
|
},
|
|
"file_size": schema.Int64Attribute{
|
|
MarkdownDescription: "Size of the file in bytes.",
|
|
Computed: true,
|
|
},
|
|
"md5": schema.StringAttribute{
|
|
MarkdownDescription: "MD5 hash of the file content.",
|
|
Computed: true,
|
|
},
|
|
"url": schema.StringAttribute{
|
|
MarkdownDescription: "URL where the file can be accessed on the UniFi controller.",
|
|
Computed: true,
|
|
},
|
|
"last_modified": schema.Int64Attribute{
|
|
MarkdownDescription: "Timestamp when the file was last modified.",
|
|
Computed: true,
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func (r *portalFileResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
|
|
var data portalFileModel
|
|
|
|
// Read Terraform plan data into the model
|
|
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
|
|
if resp.Diagnostics.HasError() {
|
|
return
|
|
}
|
|
|
|
// Get file path
|
|
filePath := data.FilePath.ValueString()
|
|
if filePath == "" {
|
|
resp.Diagnostics.AddError("File path is required", "A valid file path must be provided")
|
|
return
|
|
}
|
|
|
|
// Check if file exists
|
|
_, err := os.Stat(filePath)
|
|
if err != nil {
|
|
resp.Diagnostics.AddError("Invalid file path", fmt.Sprintf("Error accessing file: %s", err))
|
|
return
|
|
}
|
|
site := r.GetClient().ResolveSite(&data)
|
|
|
|
portalFile, err := r.GetClient().UploadPortalFile(ctx, site, filePath)
|
|
if err != nil {
|
|
resp.Diagnostics.AddError("Error uploading file", fmt.Sprintf("Could not upload file: %s", err))
|
|
return
|
|
}
|
|
|
|
// Map response back to model
|
|
resp.Diagnostics.Append(data.Merge(ctx, portalFile)...)
|
|
if resp.Diagnostics.HasError() {
|
|
return
|
|
}
|
|
data.Site = types.StringValue(site)
|
|
|
|
// Save data into Terraform state
|
|
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
|
}
|
|
|
|
func (r *portalFileResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
|
|
resp.Diagnostics.AddError("Import is not supported", "The `unifi_portal_file` resource does not support import")
|
|
}
|