package client import ( "encoding/json" "time" ) // User is the public user representation returned by the API. type User struct { ID int `json:"id"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` Name string `json:"name"` Email string `json:"email"` Color string `json:"color"` Activated bool `json:"activated"` Role string `json:"role"` Permissions []string `json:"permissions"` } // AdminUserInviteInput contains the identity fields for an administrator-created // account invitation. type AdminUserInviteInput struct { Name string `json:"name"` Email string `json:"email"` } // IsAdmin reports whether the user can manage application roles. func (user User) IsAdmin() bool { return user.Can("roles:manage") } // Can reports whether the API-resolved application permissions contain permission. func (user User) Can(permission string) bool { for _, granted := range user.Permissions { if granted == permission || granted == "*" { return true } } return false } // AccountSession describes one server-side login session for the current user. type AccountSession struct { ID string `json:"id"` CreatedAt time.Time `json:"created_at"` ExpiresAt time.Time `json:"expires_at"` Current bool `json:"current"` } // Garden is the public garden representation returned by the API. type Garden struct { ID int `json:"id"` Name string `json:"name"` Description string `json:"description"` ImageData string `json:"image_data,omitempty"` ImageID *int `json:"image_id,omitempty"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` Version int `json:"version"` Role string `json:"role"` Permissions []string `json:"permissions"` } // Can reports whether the authenticated member has a concrete permission in // this garden. The API resolves role defaults and garden-specific overrides. func (garden Garden) Can(permission string) bool { for _, granted := range garden.Permissions { if granted == permission || granted == "*" || granted == "garden:*" && permission != "garden:delete" { return true } } if garden.Permissions != nil { return false } // Compatibility for older API responses. Custom roles deliberately have no // fallback: their effective permissions must always come from the API. switch garden.Role { case "owner": return validLegacyGardenPermission(permission) case "admin": return validLegacyGardenPermission(permission) && permission != "garden:delete" case "member": switch permission { case "garden:read", "content:write", "plants:create", "plants:read:own", "plants:read:other", "plants:update:own", "plants:delete:own", "locations:create", "locations:read:own", "locations:read:other", "locations:update:own", "locations:delete:own", "tasks:create", "tasks:read:own", "tasks:read:other", "tasks:update:own", "tasks:delete:own", "tasks:complete:own", "tasks:complete:other": return true } case "viewer": return permission == "garden:read" || permission == "plants:read:own" || permission == "plants:read:other" || permission == "locations:read:own" || permission == "locations:read:other" || permission == "tasks:read:own" || permission == "tasks:read:other" case "worker": return permission == "garden:read" || permission == "tasks:read:own" || permission == "tasks:read:other" || permission == "tasks:complete:own" || permission == "tasks:complete:other" } return false } func validLegacyGardenPermission(permission string) bool { switch permission { case "garden:read", "garden:update", "garden:delete", "content:write", "members:write", "species:write", "plants:create", "plants:read:own", "plants:read:other", "plants:update:own", "plants:update:other", "plants:delete:own", "plants:delete:other", "locations:create", "locations:read:own", "locations:read:other", "locations:update:own", "locations:update:other", "locations:delete:own", "locations:delete:other", "tasks:create", "tasks:read:own", "tasks:read:other", "tasks:update:own", "tasks:update:other", "tasks:delete:own", "tasks:delete:other", "tasks:complete:own", "tasks:complete:other": return true } return false } // GardenMember describes a user's role within one garden. type GardenMember struct { GardenID int `json:"garden_id"` UserID int `json:"user_id"` Role string `json:"role"` JoinedAt time.Time `json:"joined_at"` Name string `json:"name"` Email string `json:"email"` } // GardenInvite describes a pending invitation to a garden. type GardenInvite struct { ID int `json:"id"` GardenID int `json:"garden_id"` Email string `json:"email"` Role string `json:"role"` InvitedBy int `json:"invited_by"` ExpiresAt time.Time `json:"expires_at"` CreatedAt time.Time `json:"created_at"` } // GardenInviteInput contains the recipient and role for a garden invitation. type GardenInviteInput struct { Email string `json:"email"` Role string `json:"role"` } // Role is an application or garden role with its base permissions. type Role struct { Name string `json:"name"` Scope string `json:"scope"` GardenID *int `json:"garden_id,omitempty"` Label string `json:"label"` System bool `json:"system"` Permissions []string `json:"permissions"` } // RoleInput contains writable role fields. type RoleInput struct { Name string `json:"name,omitempty"` Scope string `json:"scope,omitempty"` Label string `json:"label"` Permissions []string `json:"permissions"` } // GardenRoleSetting combines a role template with its effective permissions in // one garden. type GardenRoleSetting struct { Role Role `json:"role"` EffectivePermissions []string `json:"effective_permissions"` } // CreateGardenInput contains writable fields for a new garden. type CreateGardenInput struct { Name string `json:"name"` Description string `json:"description"` ImageData string `json:"image_data,omitempty"` ImageID *int `json:"image_id,omitempty"` } // UpdateGardenInput contains optional garden fields for a partial update. type UpdateGardenInput struct { Name *string `json:"name,omitempty"` Description *string `json:"description,omitempty"` ImageData *string `json:"image_data,omitempty"` ImageID *int `json:"image_id,omitempty"` } // Species describes global or garden-specific plant master data. type Species struct { ID int `json:"id"` GardenID *int `json:"garden_id,omitempty"` CommonName string `json:"common_name"` Cultivar string `json:"cultivar"` BotanicalName string `json:"botanical_name"` CategoryID *int `json:"category_id,omitempty"` Category string `json:"category"` SunExposure *string `json:"sun_exposure,omitempty"` SoilCondition *string `json:"soil_condition,omitempty"` SoilReaction *string `json:"soil_reaction,omitempty"` WinterProtection *string `json:"winter_protection,omitempty"` SpacingCM *int `json:"spacing_cm,omitempty"` HeightCM *int `json:"height_cm,omitempty"` SowMonthFrom *int `json:"sow_month_from,omitempty"` SowDayFrom *int `json:"sow_day_from,omitempty"` SowMonthTo *int `json:"sow_month_to,omitempty"` SowDayTo *int `json:"sow_day_to,omitempty"` PlantingMonthFrom *int `json:"planting_month_from,omitempty"` PlantingDayFrom *int `json:"planting_day_from,omitempty"` PlantingMonthTo *int `json:"planting_month_to,omitempty"` PlantingDayTo *int `json:"planting_day_to,omitempty"` HarvestMonthFrom *int `json:"harvest_month_from,omitempty"` HarvestDayFrom *int `json:"harvest_day_from,omitempty"` HarvestMonthTo *int `json:"harvest_month_to,omitempty"` HarvestDayTo *int `json:"harvest_day_to,omitempty"` Notes string `json:"notes"` ImageData string `json:"image_data,omitempty"` ImageID *int `json:"image_id,omitempty"` Attributes json.RawMessage `json:"attributes"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` Version int `json:"version"` Tags []string `json:"tags,omitempty"` CreatedBy int `json:"created_by"` UpdatedBy int `json:"updated_by"` } // SpeciesInput contains optional species fields used for creation and updates. type SpeciesInput struct { Global bool `json:"global,omitempty"` Tags []string `json:"tags,omitempty"` CommonName *string `json:"common_name,omitempty"` Cultivar *string `json:"cultivar,omitempty"` BotanicalName *string `json:"botanical_name,omitempty"` CategoryID *int `json:"category_id,omitempty"` ClearCategoryID bool `json:"clear_category_id,omitempty"` SunExposure *string `json:"sun_exposure,omitempty"` SoilCondition *string `json:"soil_condition,omitempty"` SoilReaction *string `json:"soil_reaction,omitempty"` WinterProtection *string `json:"winter_protection,omitempty"` SpacingCM *int `json:"spacing_cm,omitempty"` HeightCM *int `json:"height_cm,omitempty"` SowMonthFrom *int `json:"sow_month_from,omitempty"` SowDayFrom *int `json:"sow_day_from,omitempty"` ClearSowDayFrom bool `json:"clear_sow_day_from,omitempty"` SowMonthTo *int `json:"sow_month_to,omitempty"` SowDayTo *int `json:"sow_day_to,omitempty"` ClearSowDayTo bool `json:"clear_sow_day_to,omitempty"` PlantingMonthFrom *int `json:"planting_month_from,omitempty"` PlantingDayFrom *int `json:"planting_day_from,omitempty"` ClearPlantingDayFrom bool `json:"clear_planting_day_from,omitempty"` PlantingMonthTo *int `json:"planting_month_to,omitempty"` PlantingDayTo *int `json:"planting_day_to,omitempty"` ClearPlantingDayTo bool `json:"clear_planting_day_to,omitempty"` HarvestMonthFrom *int `json:"harvest_month_from,omitempty"` HarvestDayFrom *int `json:"harvest_day_from,omitempty"` ClearHarvestDayFrom bool `json:"clear_harvest_day_from,omitempty"` HarvestMonthTo *int `json:"harvest_month_to,omitempty"` HarvestDayTo *int `json:"harvest_day_to,omitempty"` ClearHarvestDayTo bool `json:"clear_harvest_day_to,omitempty"` ClearSowRange bool `json:"clear_sow_range,omitempty"` ClearPlantingRange bool `json:"clear_planting_range,omitempty"` ClearHarvestRange bool `json:"clear_harvest_range,omitempty"` Notes *string `json:"notes,omitempty"` ImageData *string `json:"image_data,omitempty"` ImageID *int `json:"image_id,omitempty"` Attributes json.RawMessage `json:"attributes,omitempty"` } // SpeciesCategory classifies species and optionally supplies a lifecycle. type SpeciesCategory struct { ID int `json:"id"` Name string `json:"name"` SortOrder int `json:"sort_order"` Active bool `json:"active"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` Version int `json:"version"` Lifecycle *string `json:"lifecycle,omitempty"` } // SpeciesCategoryInput contains writable category fields. type SpeciesCategoryInput struct { Name *string `json:"name,omitempty"` SortOrder *int `json:"sort_order,omitempty"` Active *bool `json:"active,omitempty"` Lifecycle *string `json:"lifecycle,omitempty"` } // TaskPriority maps a display name to the numeric priority stored on tasks. type TaskPriority struct { ID int `json:"id"` Name string `json:"name"` Value int `json:"value"` SortOrder int `json:"sort_order"` Active bool `json:"active"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` Version int `json:"version"` } // TaskPriorityInput contains writable priority-catalogue fields. type TaskPriorityInput struct { Name *string `json:"name,omitempty"` Value *int `json:"value,omitempty"` SortOrder *int `json:"sort_order,omitempty"` Active *bool `json:"active,omitempty"` } // Plant represents a plant instance owned by a garden. type Plant struct { ID int `json:"id"` GardenID int `json:"garden_id"` SpeciesID *int `json:"species_id,omitempty"` Name string `json:"name"` Notes string `json:"notes"` ImageData string `json:"image_data,omitempty"` ImageID *int `json:"image_id,omitempty"` AcquiredAt *time.Time `json:"acquired_at,omitempty"` Status string `json:"status"` RemovedAt *time.Time `json:"removed_at,omitempty"` Attributes json.RawMessage `json:"attributes"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` Version int `json:"version"` Tags []string `json:"tags,omitempty"` CreatedBy int `json:"created_by"` UpdatedBy int `json:"updated_by"` PlantedBy int `json:"planted_by"` PlantedByName string `json:"planted_by_name,omitempty"` } // PlantInput contains optional plant fields used for creation and updates. type PlantInput struct { Tags []string `json:"tags,omitempty"` SpeciesID *int `json:"species_id,omitempty"` ClearSpeciesID bool `json:"clear_species_id,omitempty"` Name *string `json:"name,omitempty"` Notes *string `json:"notes,omitempty"` ImageData *string `json:"image_data,omitempty"` ImageID *int `json:"image_id,omitempty"` AcquiredAt *time.Time `json:"acquired_at,omitempty"` ClearAcquiredAt bool `json:"clear_acquired_at,omitempty"` Status *string `json:"status,omitempty"` RemovedAt *time.Time `json:"removed_at,omitempty"` Attributes json.RawMessage `json:"attributes,omitempty"` } // Location describes a hierarchical place within a garden. type Location struct { ID int `json:"id"` GardenID int `json:"garden_id"` ParentID *int `json:"parent_id,omitempty"` Name string `json:"name"` Description string `json:"description"` ImageData string `json:"image_data,omitempty"` ImageID *int `json:"image_id,omitempty"` Kind string `json:"kind"` AreaSQM *float64 `json:"area_sqm,omitempty"` SunExposure *string `json:"sun_exposure,omitempty"` SoilCondition *string `json:"soil_condition,omitempty"` SoilReaction *string `json:"soil_reaction,omitempty"` Attributes json.RawMessage `json:"attributes"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` Version int `json:"version"` CreatedBy int `json:"created_by"` UpdatedBy int `json:"updated_by"` } // LocationInput contains optional location fields used for creation and updates. type LocationInput struct { ParentID *int `json:"parent_id,omitempty"` ClearParentID bool `json:"clear_parent_id,omitempty"` Name *string `json:"name,omitempty"` Description *string `json:"description,omitempty"` ImageData *string `json:"image_data,omitempty"` ImageID *int `json:"image_id,omitempty"` Kind *string `json:"kind,omitempty"` AreaSQM *float64 `json:"area_sqm,omitempty"` SunExposure *string `json:"sun_exposure,omitempty"` SoilCondition *string `json:"soil_condition,omitempty"` SoilReaction *string `json:"soil_reaction,omitempty"` Attributes json.RawMessage `json:"attributes,omitempty"` } // PlantLocation represents a plant's assignment to a location. type PlantLocation struct { ID int `json:"id"` PlantID int `json:"plant_id"` LocationID int `json:"location_id"` Quantity int `json:"quantity"` PlantedAt *time.Time `json:"planted_at,omitempty"` RemovedAt *time.Time `json:"removed_at,omitempty"` Notes string `json:"notes"` CreatedAt time.Time `json:"created_at"` Version int `json:"version"` } // PlantLocationInput contains optional assignment fields used for creation and updates. type PlantLocationInput struct { LocationID *int `json:"location_id,omitempty"` Quantity *int `json:"quantity,omitempty"` PlantedAt *time.Time `json:"planted_at,omitempty"` ClearPlantedAt bool `json:"clear_planted_at,omitempty"` RemovedAt *time.Time `json:"removed_at,omitempty"` Notes *string `json:"notes,omitempty"` } // Task represents a manual or generated garden work item. type Task struct { ID int `json:"id"` GardenID int `json:"garden_id"` PlantID *int `json:"plant_id,omitempty"` LocationID *int `json:"location_id,omitempty"` TemplateID *int `json:"template_id,omitempty"` Title string `json:"title"` Description string `json:"description"` DueAtStart *time.Time `json:"due_at_start,omitempty"` DueAtEnd *time.Time `json:"due_at_end,omitempty"` GeneratedFor *time.Time `json:"generated_for,omitempty"` Recurrence string `json:"recurrence,omitempty"` RecurrenceInterval int `json:"recurrence_interval"` RepeatFromID *int `json:"repeat_from_id,omitempty"` CompletedAt *time.Time `json:"completed_at,omitempty"` CompletedBy *int `json:"completed_by,omitempty"` Priority int `json:"priority"` Active *bool `json:"active,omitempty"` CreatedBy int `json:"created_by"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` Version int `json:"version"` Tags []string `json:"tags,omitempty"` PlantStatusOnCompletion *string `json:"plant_status_on_completion,omitempty"` } // TaskInput contains writable fields for task creation and partial updates. type TaskInput struct { Tags []string `json:"tags,omitempty"` PlantID *int `json:"plant_id,omitempty"` LocationID *int `json:"location_id,omitempty"` ClearPlantID bool `json:"clear_plant_id,omitempty"` ClearLocationID bool `json:"clear_location_id,omitempty"` Title *string `json:"title,omitempty"` Description *string `json:"description,omitempty"` DueAtStart *time.Time `json:"due_at_start,omitempty"` DueAtEnd *time.Time `json:"due_at_end,omitempty"` ClearDueAtStart bool `json:"clear_due_at_start,omitempty"` ClearDueAtEnd bool `json:"clear_due_at_end,omitempty"` Recurrence *string `json:"recurrence,omitempty"` RecurrenceInterval *int `json:"recurrence_interval,omitempty"` Priority *int `json:"priority,omitempty"` Active *bool `json:"active,omitempty"` Completed *bool `json:"completed,omitempty"` PlantStatusOnCompletion *string `json:"plant_status_on_completion,omitempty"` } // CareInstruction is garden-specific cultivation guidance for a species. type CareInstruction struct { ID int `json:"id"` SpeciesID int `json:"species_id"` Text string `json:"text"` Status string `json:"status"` CreatedBy int `json:"created_by"` UpdatedBy int `json:"updated_by"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` Version int `json:"version"` } // CareInstructionInput contains writable care-instruction fields. type CareInstructionInput struct { Text *string `json:"text,omitempty"` Status *string `json:"status,omitempty"` } // JournalEntry is a garden journal or pinboard entry with related media. type JournalEntry struct { ID int `json:"id"` GardenID int `json:"garden_id"` AuthorID int `json:"author_id"` AuthorName string `json:"author_name"` AuthorColor string `json:"author_color"` EntryType string `json:"entry_type"` Title string `json:"title"` Body string `json:"body"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` Version int `json:"version"` Tags []string `json:"tags,omitempty"` Attachments []JournalAttachment `json:"attachments,omitempty"` } // JournalEntryInput contains writable journal-entry fields. type JournalEntryInput struct { Title *string `json:"title,omitempty"` Body *string `json:"body,omitempty"` CreatedAt *time.Time `json:"created_at,omitempty"` Tags []string `json:"tags,omitempty"` EntryType *string `json:"entry_type,omitempty"` } // JournalAttachment describes attachment metadata without its binary body. type JournalAttachment struct { ID int `json:"id"` EntryID int `json:"entry_id"` FileName string `json:"file_name"` MediaType string `json:"media_type"` Size int64 `json:"size"` CreatedAt time.Time `json:"created_at"` ImageID *int `json:"image_id,omitempty"` } // JournalAttachmentData combines attachment metadata with its binary body. type JournalAttachmentData struct { JournalAttachment Data []byte } // SpeciesTaskTemplate describes a rule for generating tasks from species data. type SpeciesTaskTemplate struct { ID int `json:"id"` SpeciesID int `json:"species_id"` Origin string `json:"origin"` Title string `json:"title"` Description string `json:"description"` TriggerType string `json:"trigger_type"` MonthFrom *int `json:"month_from,omitempty"` DayFrom *int `json:"day_from,omitempty"` MonthTo *int `json:"month_to,omitempty"` DayTo *int `json:"day_to,omitempty"` OffsetDaysFrom *int `json:"offset_days_from,omitempty"` OffsetDaysTo *int `json:"offset_days_to,omitempty"` IntervalDays *int `json:"interval_days,omitempty"` TriggerOffset int `json:"trigger_offset"` TriggerOffsetUnit string `json:"trigger_offset_unit"` Duration int `json:"duration"` DurationUnit string `json:"duration_unit"` Recurrence string `json:"recurrence,omitempty"` RecurrenceInterval int `json:"recurrence_interval"` Priority int `json:"priority"` Active bool `json:"active"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` Version int `json:"version"` } // ApplicationSettings contains application-wide lifecycle automation settings. type ApplicationSettings struct { LifecycleStatusEnabled bool `json:"lifecycle_status_enabled"` LifecycleRemovalMonth int `json:"lifecycle_removal_month"` LifecycleRemovalDay int `json:"lifecycle_removal_day"` Timezone string `json:"timezone"` UpdatedAt time.Time `json:"updated_at"` Version int `json:"version"` } // ApplicationSettingsInput contains writable lifecycle automation settings. type ApplicationSettingsInput struct { LifecycleStatusEnabled *bool `json:"lifecycle_status_enabled,omitempty"` LifecycleRemovalMonth *int `json:"lifecycle_removal_month,omitempty"` LifecycleRemovalDay *int `json:"lifecycle_removal_day,omitempty"` Timezone *string `json:"timezone,omitempty"` } // EnvironmentVariable describes one effective process configuration value. // Sensitive values are masked by the API before they leave the process. type EnvironmentVariable struct { Component string `json:"component"` Name string `json:"name"` Value string `json:"value"` } // SpeciesTaskTemplateInput contains writable task-template fields. type SpeciesTaskTemplateInput struct { Title *string `json:"title,omitempty"` Description *string `json:"description,omitempty"` TriggerType *string `json:"trigger_type,omitempty"` MonthFrom *int `json:"month_from,omitempty"` DayFrom *int `json:"day_from,omitempty"` ClearDayFrom bool `json:"clear_day_from,omitempty"` MonthTo *int `json:"month_to,omitempty"` DayTo *int `json:"day_to,omitempty"` ClearDayTo bool `json:"clear_day_to,omitempty"` OffsetDaysFrom *int `json:"offset_days_from,omitempty"` OffsetDaysTo *int `json:"offset_days_to,omitempty"` IntervalDays *int `json:"interval_days,omitempty"` ClearIntervalDays bool `json:"clear_interval_days,omitempty"` TriggerOffset *int `json:"trigger_offset,omitempty"` TriggerOffsetUnit *string `json:"trigger_offset_unit,omitempty"` Duration *int `json:"duration,omitempty"` DurationUnit *string `json:"duration_unit,omitempty"` Recurrence *string `json:"recurrence,omitempty"` RecurrenceInterval *int `json:"recurrence_interval,omitempty"` Priority *int `json:"priority,omitempty"` Active *bool `json:"active,omitempty"` } // Credentials contains email and password authentication input. type Credentials struct { Email string `json:"email"` Password string `json:"password"` } // RegisterUserInput contains the fields required to register an account. type RegisterUserInput struct { Name string `json:"name"` Email string `json:"email"` Password string `json:"password"` } // TokenInput wraps a plaintext activation or reset token. type TokenInput struct { Token string `json:"token"` } // EmailInput wraps an email address for token requests. type EmailInput struct { Email string `json:"email"` } // UpdatePasswordInput contains a new password and its reset token. type UpdatePasswordInput struct { Password string `json:"password"` Token string `json:"token"` } // AuthenticationToken is a bearer token and its expiration time. type AuthenticationToken struct { Token string `json:"token"` Expiry time.Time `json:"expiry"` } // Health describes API availability and build information. type Health struct { Status string `json:"status"` ServerTime time.Time `json:"server_time"` SystemInfo SystemInfo `json:"system_info"` } // SystemInfo identifies the running API environment and version. type SystemInfo struct { Environment string `json:"environment"` Version string `json:"version"` }