From f2f387164b7da3913e3b7003ec3769e63e44e032 Mon Sep 17 00:00:00 2001 From: Russ Long Date: Mon, 1 Jun 2026 11:31:05 -0400 Subject: [PATCH] feat: implement server api data source --- internal/client/server.go | 94 +++++++++++++++++++ internal/provider/provider.go | 1 + internal/provider/server_data_source.go | 119 ++++++++++++++++++++++++ 3 files changed, 214 insertions(+) create mode 100644 internal/client/server.go create mode 100644 internal/provider/server_data_source.go diff --git a/internal/client/server.go b/internal/client/server.go new file mode 100644 index 0000000..b8b861e --- /dev/null +++ b/internal/client/server.go @@ -0,0 +1,94 @@ +package client + +import ( + "encoding/json" + "fmt" + "net/http" +) + +type ServerAbout struct { + Version string `json:"version"` + Build string `json:"build"` + NodeJS string `json:"nodejs"` + FFmpeg string `json:"ffmpeg"` + ExifTool string `json:"exiftool"` + ImageMagick string `json:"imagemagick"` + Libvips string `json:"libvips"` +} + +type ServerFeatures struct { + ConfigFile bool `json:"configFile"` + FacialRecognition bool `json:"facialRecognition"` + Map bool `json:"map"` + ReverseGeocoding bool `json:"reverseGeocoding"` + Search bool `json:"search"` + Oauth bool `json:"oauth"` + PasswordLogin bool `json:"passwordLogin"` +} + +type ServerStatistics struct { + Photos int `json:"photos"` + Videos int `json:"videos"` + Usage int64 `json:"usage"` + Users int `json:"users"` +} + +func (c *Client) GetServerAbout() (*ServerAbout, error) { + req, err := http.NewRequest("GET", fmt.Sprintf("%s/server/about", c.HostURL), nil) + if err != nil { + return nil, err + } + + body, err := c.doRequest(req) + if err != nil { + return nil, err + } + + var about ServerAbout + err = json.Unmarshal(body, &about) + if err != nil { + return nil, err + } + + return &about, nil +} + +func (c *Client) GetServerFeatures() (*ServerFeatures, error) { + req, err := http.NewRequest("GET", fmt.Sprintf("%s/server/features", c.HostURL), nil) + if err != nil { + return nil, err + } + + body, err := c.doRequest(req) + if err != nil { + return nil, err + } + + var features ServerFeatures + err = json.Unmarshal(body, &features) + if err != nil { + return nil, err + } + + return &features, nil +} + +func (c *Client) GetServerStatistics() (*ServerStatistics, error) { + req, err := http.NewRequest("GET", fmt.Sprintf("%s/server/statistics", c.HostURL), nil) + if err != nil { + return nil, err + } + + body, err := c.doRequest(req) + if err != nil { + return nil, err + } + + var stats ServerStatistics + err = json.Unmarshal(body, &stats) + if err != nil { + return nil, err + } + + return &stats, nil +} diff --git a/internal/provider/provider.go b/internal/provider/provider.go index daf1ffb..80e7a93 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -105,6 +105,7 @@ func (p *immichProvider) DataSources(ctx context.Context) []func() datasource.Da NewAlbumsDataSource, NewLibrariesDataSource, NewActivitiesDataSource, + NewServerDataSource, } } diff --git a/internal/provider/server_data_source.go b/internal/provider/server_data_source.go new file mode 100644 index 0000000..b0d6e6e --- /dev/null +++ b/internal/provider/server_data_source.go @@ -0,0 +1,119 @@ +package provider + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/immich-app/terraform-provider-immich/internal/client" +) + +// Ensure the implementation satisfies the expected interfaces. +var _ datasource.DataSource = &serverDataSource{} + +func NewServerDataSource() datasource.DataSource { + return &serverDataSource{} +} + +// serverDataSource defines the data source implementation. +type serverDataSource struct { + client *client.Client +} + +// serverDataSourceModel describes the data source data model. +type serverDataSourceModel struct { + ID types.String `tfsdk:"id"` + Version types.String `tfsdk:"version"` + Build types.String `tfsdk:"build"` + NodeJS types.String `tfsdk:"nodejs"` + FFmpeg types.String `tfsdk:"ffmpeg"` + ExifTool types.String `tfsdk:"exiftool"` + ImageMagick types.String `tfsdk:"imagemagick"` + Libvips types.String `tfsdk:"libvips"` +} + +func (d *serverDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_server_info" +} + +func (d *serverDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = schema.Schema{ + MarkdownDescription: "Retrieves information about the Immich server.", + + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Computed: true, + }, + "version": schema.StringAttribute{ + Computed: true, + MarkdownDescription: "Server version.", + }, + "build": schema.StringAttribute{ + Computed: true, + MarkdownDescription: "Build ID.", + }, + "nodejs": schema.StringAttribute{ + Computed: true, + MarkdownDescription: "Node.js version.", + }, + "ffmpeg": schema.StringAttribute{ + Computed: true, + MarkdownDescription: "FFmpeg version.", + }, + "exiftool": schema.StringAttribute{ + Computed: true, + MarkdownDescription: "ExifTool version.", + }, + "imagemagick": schema.StringAttribute{ + Computed: true, + MarkdownDescription: "ImageMagick version.", + }, + "libvips": schema.StringAttribute{ + Computed: true, + MarkdownDescription: "libvips version.", + }, + }, + } +} + +func (d *serverDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + client, ok := req.ProviderData.(*client.Client) + + if !ok { + resp.Diagnostics.AddError( + "Unexpected Data Source Configure Type", + fmt.Sprintf("Expected *client.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData), + ) + + return + } + + d.client = client +} + +func (d *serverDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + var data serverDataSourceModel + + about, err := d.client.GetServerAbout() + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read server about, got error: %s", err)) + return + } + + data.ID = types.StringValue("server_info") + data.Version = types.StringValue(about.Version) + data.Build = types.StringValue(about.Build) + data.NodeJS = types.StringValue(about.NodeJS) + data.FFmpeg = types.StringValue(about.FFmpeg) + data.ExifTool = types.StringValue(about.ExifTool) + data.ImageMagick = types.StringValue(about.ImageMagick) + data.Libvips = types.StringValue(about.Libvips) + + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +}