handler

package
v0.0.0-...-dcdd8c6 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 11, 2026 License: AGPL-3.0 Imports: 121 Imported by: 0

Documentation

Overview

All handlers in this file MUST be registered inside the admin middleware group. They do NOT perform their own admin role check — they rely on RequireAdmin() middleware.

services/api/internal/handler/admin_library_settings.go

services/api/internal/handler/admin_rename.go

All handlers in this file MUST be registered inside the admin middleware group. They do NOT perform their own admin role check — they rely on RequireAdmin() middleware.

services/api/internal/handler/admin_user_library_dir.go

services/api/internal/handler/advisory_locks.go

services/api/internal/handler/library_dir_validation.go

Index

Constants

View Source
const (
	ZoneTypeHR    = "hr"
	ZoneTypePower = "power"
	ZoneTypeSpeed = "speed"
)

Zone type constants shared with other packages.

Variables

View Source
var ErrAlreadyFused = errors.New("one or more sources already belong to a fusion")

ErrAlreadyFused is returned when a FusionMerge attempt includes a source activity that is already a member of another merged activity. The preflight check inside each merge tx (after advisory-lock acquisition) rejects this case, which protects against concurrent merges of the same [A, B] set.

Functions

func DetectLaps

func DetectLaps(activityID string, gpsStream []database.GPSStreamPoint) []model.DetectedLap

DetectLaps automatically detects laps/rounds within an activity's GPS stream. Algorithm:

  1. Build spatial hash grid (25m cells)
  2. Find most-visited cell (= likely start/finish area)
  3. Cluster passages through the hotspot (3x3 neighborhood)
  4. Extract laps between consecutive passages
  5. Validate: min 2 laps, min distance, min time, similar lengths

func MatchActivitySegments

func MatchActivitySegments(ctx context.Context, db *database.DB, activityID, userID, sportType, subType string, gearID *string, activityDate time.Time)

MatchActivitySegments matches an activity against all segments visible to the user. Uses sub-track extraction: finds traversals through segments by detecting entry/exit points near segment start/end, then verifies with Hausdorff distance. It runs as a background goroutine after activity upload (non-blocking).

func SeedProfiles

func SeedProfiles(ctx context.Context, dir string, db *database.DB, source string) (int, error)

SeedProfiles scans dir for *.json profile files and upserts them into the database as global profiles with the given source (SourceSystem or SourceFilesystem). Missing dir or no files is a no-op (returns 0, nil).

Types

type AllRanges

type AllRanges = edgetags.AllRanges

type CoordRange

type CoordRange = edgetags.CoordRange

type CreateManualActivityRequest

type CreateManualActivityRequest struct {
	SportType      string   `json:"sport_type"`
	SubType        string   `json:"sub_type,omitempty"`
	Title          string   `json:"title,omitempty"`
	Description    string   `json:"description,omitempty"`
	StartedAt      string   `json:"started_at"`
	ElapsedTimeS   float64  `json:"elapsed_time_s"`
	DistanceM      float64  `json:"distance_m,omitempty"`
	ElevationGainM float64  `json:"elevation_gain_m,omitempty"`
	ElevationLossM float64  `json:"elevation_loss_m,omitempty"`
	Tags           []string `json:"tags,omitempty"`
	GearID         string   `json:"gear_id,omitempty"`
	IsElectric     bool     `json:"is_electric,omitempty"`
	IsIndoor       bool     `json:"is_indoor,omitempty"`
	IsVirtual      bool     `json:"is_virtual,omitempty"`
	WorkoutFeel    *int     `json:"workout_feel,omitempty"`
	WorkoutRpe     *int     `json:"workout_rpe,omitempty"`
}

CreateManualActivityRequest is the JSON body for POST /api/v1/activities/manual.

type CreateObjectAccessGrantRequest

type CreateObjectAccessGrantRequest struct {
	PrincipalKind     string         `json:"principal_kind"`
	PrincipalUserID   *string        `json:"principal_user_id,omitempty"`
	PrincipalRef      *string        `json:"principal_ref,omitempty"`
	PrincipalSelector map[string]any `json:"principal_selector,omitempty"`
	LinkToken         *string        `json:"link_token,omitempty"`
	Permission        string         `json:"permission,omitempty"`
}

type CreateObjectAccessScopeRequest

type CreateObjectAccessScopeRequest struct {
	ScopeKind string                           `json:"scope_kind,omitempty"`
	Label     string                           `json:"label,omitempty"`
	Grants    []CreateObjectAccessGrantRequest `json:"grants"`
}

type CreateRouteCourseRelationshipRequest

type CreateRouteCourseRelationshipRequest struct {
	TargetPublicationID string `json:"target_publication_id"`
	RelationType        string `json:"relation_type"`
	OperationID         string `json:"operation_id"`
}

type CreateRouteCourseRelationshipResponse

type CreateRouteCourseRelationshipResponse struct {
	RelationshipID string `json:"relationship_id"`
}

type CreateRoutePublicationPrivacyReviewRequest

type CreateRoutePublicationPrivacyReviewRequest struct {
	RouteSnapshotID    string `json:"route_snapshot_id,omitempty"`
	SnapshotID         string `json:"snapshot_id,omitempty"`
	SourceRouteVersion int64  `json:"source_route_version"`
	AudienceClass      string `json:"audience_class"`
	OwnerConfirmed     bool   `json:"owner_confirmed"`
}

type CreateRoutePublicationPrivacyReviewResponse

type CreateRoutePublicationPrivacyReviewResponse struct {
	RouteSnapshotID string `json:"route_snapshot_id"`
	AudienceClass   string `json:"audience_class"`
	ReviewHash      string `json:"review_hash"`
}

type CreateRouteRelationshipRequest

type CreateRouteRelationshipRequest struct {
	TargetRouteID string `json:"target_route_id"`
	RelationType  string `json:"relation_type"`
	OperationID   string `json:"operation_id"`
}

type CreateRouteRelationshipResponse

type CreateRouteRelationshipResponse struct {
	RelationshipID string `json:"relationship_id"`
}

type CreateRouteSnapshotRequest

type CreateRouteSnapshotRequest struct {
	SourceRouteVersion int64  `json:"source_route_version"`
	OperationID        string `json:"operation_id"`
	Reason             string `json:"reason,omitempty"`
}

type CreateRouteSnapshotResponse

type CreateRouteSnapshotResponse struct {
	SnapshotID    string `json:"snapshot_id"`
	SnapshotHash  string `json:"snapshot_hash"`
	GeometryHash  string `json:"geometry_hash"`
	SourceVersion int64  `json:"source_route_version"`
}

type DiveInfo

type DiveInfo struct {
	Settings *model.DiveSettings `json:"settings,omitempty"`
	Gases    []model.DiveGas     `json:"gases"`
	Po2Stats *model.DivePo2Stats `json:"po2_stats,omitempty"`
}

DiveInfo bundles settings + gases for a single response.

type ForkRouteRequest

type ForkRouteRequest struct {
	Name               string `json:"name"`
	SourceRouteVersion int64  `json:"source_route_version"`
	OperationID        string `json:"operation_id"`
}

type ForkRouteResponse

type ForkRouteResponse struct {
	RouteID        string `json:"route_id"`
	RelationshipID string `json:"relationship_id"`
}

type FusionCandidate

type FusionCandidate struct {
	ID                 string    `json:"id"`
	Title              string    `json:"title"`
	StartedAt          time.Time `json:"started_at"`
	DistanceM          float64   `json:"distance_m"`
	SportType          string    `json:"sport_type"`
	SubType            *string   `json:"sub_type"`
	IsElectric         bool      `json:"is_electric"`
	IsIndoor           bool      `json:"is_indoor"`
	IsVirtual          bool      `json:"is_virtual"`
	HasHR              bool      `json:"has_hr"`
	HasPower           bool      `json:"has_power"`
	HasGPS             bool      `json:"has_gps"`
	HasTemperature     bool      `json:"has_temperature"`
	HasCadence         bool      `json:"has_cadence"`
	HasAltitude        bool      `json:"has_altitude"`
	OverlapPct         float64   `json:"overlap_pct"`
	Timezone           *string   `json:"timezone,omitempty"`
	SourceDevice       *string   `json:"source_device,omitempty"`
	Source             *string   `json:"source,omitempty"`
	OriginalFileFormat *string   `json:"original_file_format,omitempty"`
}

FusionCandidate beschreibt eine Aktivität die als Fusionsquelle in Frage kommt.

type FusionMergeRequest

type FusionMergeRequest struct {
	Sources         []string              `json:"sources"`   // all equal, min 2, max 6
	FieldMap        map[string]string     `json:"field_map"` // "hr" → "uuid-B", "gps" → "fuse"
	GPSFusionConfig *GPSFusionConfig      `json:"gps_fusion_config,omitempty"`
	Sport           *FusionSportSelection `json:"sport,omitempty"`
	TitleSource     *string               `json:"title_source,omitempty"`
	TagsMerge       bool                  `json:"tags_merge"`
	TagsSource      *string               `json:"tags_source,omitempty"`
	GearSource      *string               `json:"gear_source,omitempty"`
	FitLevel        bool                  `json:"fit_level"`
	GroupID         *string               `json:"group_id,omitempty"` // if fusing from a recording group
}

FusionMergeRequest describes the body for POST /api/v1/fusion-merge (no target). All sources are equal — the unified timeline is built from ALL timestamps.

type FusionQueueEntry

type FusionQueueEntry struct {
	ActivityIDA         string  `json:"activity_id_a"`
	ActivityIDB         string  `json:"activity_id_b"`
	TitleA              string  `json:"title_a"`
	TitleB              string  `json:"title_b"`
	StartedAtA          string  `json:"started_at_a"`
	StartedAtB          string  `json:"started_at_b"`
	SportTypeA          string  `json:"sport_type_a"`
	SportTypeB          string  `json:"sport_type_b"`
	SubTypeA            string  `json:"sub_type_a"`
	SubTypeB            string  `json:"sub_type_b"`
	IsElectricA         bool    `json:"is_electric_a"`
	IsElectricB         bool    `json:"is_electric_b"`
	IsIndoorA           bool    `json:"is_indoor_a"`
	IsIndoorB           bool    `json:"is_indoor_b"`
	IsVirtualA          bool    `json:"is_virtual_a"`
	IsVirtualB          bool    `json:"is_virtual_b"`
	DistanceMa          float64 `json:"distance_m_a"`
	DistanceMb          float64 `json:"distance_m_b"`
	OverlapPercent      float64 `json:"overlap_percent"`
	FieldRichnessA      int     `json:"field_richness_a"`
	FieldRichnessB      int     `json:"field_richness_b"`
	TimeDiffSec         float64 `json:"time_diff_sec"`
	SourceDeviceA       string  `json:"source_device_a"`
	SourceA             string  `json:"source_a"`
	OriginalFileFormatA string  `json:"original_file_format_a"`
	SourceDeviceB       string  `json:"source_device_b"`
	SourceB             string  `json:"source_b"`
	OriginalFileFormatB string  `json:"original_file_format_b"`
	GroupIDA            *string `json:"group_id_a,omitempty"`
	GroupIDB            *string `json:"group_id_b,omitempty"`
}

FusionQueueEntry ist ein angereichter FusionCandidate für die API. sport_type_a und sport_type_b können unterschiedlich sein — z.B. wenn dieselbe Wanderung auf einem Radcomputer als "cycling" und auf einem GPSmap als "hiking" geloggt wurde. In diesem Fall wählt der Nutzer im DuplicateReviewPanel welchen Sport-Typ er behalten will.

type FusionSportSelection

type FusionSportSelection struct {
	SportType  string `json:"sport_type"`
	SubType    string `json:"sub_type,omitempty"`
	IsElectric *bool  `json:"is_electric,omitempty"`
	IsIndoor   *bool  `json:"is_indoor,omitempty"`
	IsVirtual  *bool  `json:"is_virtual,omitempty"`
}

type GPSFusionConfig

type GPSFusionConfig struct {
	// PrimarySource: Aktivitäts-ID, deren GPS-Daten bei Zeitüberlappung bevorzugt werden.
	// Leer = Target-Aktivität ist Primary.
	PrimarySource string `json:"primary_source,omitempty"`

	// ExtendTrack: Zeitpunkte aus Quellen übernehmen, die außerhalb des
	// Target-Zeitfensters liegen (Track verlängern statt nur füllen).
	ExtendTrack bool `json:"extend_track,omitempty"`
}

GPSFusionConfig konfiguriert die GPS-Fusion für einen Merge-Vorgang. Wird von MergeActivity (fusion_merge.go) befüllt und an fuseGPSTracks übergeben.

type Handler

type Handler struct {
	DB          *database.DB
	Confirm     *agent.ConfirmationManager
	Agent       *agent.Agent
	LLMResolver LLMRoleResolver

	// Segregated publisher interfaces. Production wires all six fields to the
	// same *event.Publisher; tests can substitute a FakeInvalidator (or other
	// fake) per-method. Each may be nil when NATS is not configured — call
	// sites must guard with a nil-check before publishing.
	AnalyticsInvalidator      event.AnalyticsInvalidator
	ActivityUploadedPublisher event.ActivityUploadedPublisher
	Stage2ReadyPublisher      event.Stage2ReadyPublisher
	DocumentUploadedPublisher event.DocumentUploadedPublisher
	PhotoEmbedPublisher       event.PhotoEmbedPublisher
	EventConn                 event.EventConn

	ShadowFS           shadowfs.ShadowFS            // nil when not yet initialized
	ShadowFSConfig     *shadowfs.ShadowFSConfig     // nil when shadow FS is not configured
	WatcherManager     *shadowfs.WatcherManager     // nil when per-user hotfolder is not configured
	Config             *HandlerConfig               // LLM/LiteLLM configuration
	ImportService      *importer.Service            // shared post-processing pipeline
	AIBrokerHTTPClient *http.Client                 // optional test override; production uses SSRF-protected client
	GraphManager       *routing.GraphManager        // nil when routing graph is not configured
	CoverageBaseDir    string                       // base directory for FCOV coverage files
	CoverageReader     *coverage.DiskCoverageReader // nil when coverage binaries are not available
	PNGCache           *coverage.PNGCache           // LRU cache for rendered PNG tiles

	DEMSvc dem.Servicer // lazy mmap'd HGT service; nil when not configured

	PMC         *pmc.Service         // TSS/PMC recomputation service
	BestEfforts *bestefforts.Service // best effort computation service

	Settings *settings.SettingsStore // server settings store (DB-backed, cached)
	Mailer   *auth.Mailer            // for admin test-email endpoint

	StartupMaintenanceReady *atomic.Bool

	// TxFactory is an optional override for transaction creation. When nil,
	// beginTx falls through to DB.Pool.Begin().
	//
	// Thread-safety: this field is NOT thread-safe. Production code never
	// writes it after Handler construction. Tests that set TxFactory must
	// use a dedicated Handler instance (not a shared one) to avoid races
	// with parallel goroutines. See activity_set_virtual_test.go for the
	// expected pattern.
	TxFactory TxFactory

	// Stage1Deps overrides the external dependencies (WeatherFetcher,
	// ReverseGeocoder) used by RunStage1 in CreateManualActivity. When nil,
	// production defaults are used (real HTTP weather + PostGIS geocoder).
	// Set only in integration tests to avoid network calls.
	Stage1Deps          *enrichment.Stage1Deps
	StorageProbe        systemStatusStorageProbe
	GDPRArtifactCounter gdprartifacts.IncompleteMultipartCounter
	// contains filtered or unexported fields
}

func (*Handler) AcknowledgeLifts

func (h *Handler) AcknowledgeLifts(w http.ResponseWriter, r *http.Request)

AcknowledgeLifts handles POST /api/v1/activities/{id}/lifts/acknowledge.

Sets activities.lifts_acknowledged_at = NOW() without touching candidates. The X-close path of the review modal calls this so the modal does not auto-trigger again on the next visit (User-Quote 2026-05-08 #6).

Phase 6.3.1.2 bullet 3.

func (*Handler) ActivateBikeSetup

func (h *Handler) ActivateBikeSetup(w http.ResponseWriter, r *http.Request)

func (*Handler) ActivityTrackSummary

func (h *Handler) ActivityTrackSummary(w http.ResponseWriter, r *http.Request)

ActivityTrackSummary returns the consolidated per-track enrichment summary for one activity.

func (*Handler) AddActivityGearAssignment

func (h *Handler) AddActivityGearAssignment(w http.ResponseWriter, r *http.Request)

func (*Handler) AddBestEffortExclusion

func (h *Handler) AddBestEffortExclusion(w http.ResponseWriter, r *http.Request)

AddBestEffortExclusion marks an activity's best effort (or all efforts via "__all__") as excluded from ranking for the authenticated user.

Body: {"activity_id": "...", "category": "dist_5km"}

POST /api/v1/best-effort-exclusions

func (*Handler) AddGovernanceBinding

func (h *Handler) AddGovernanceBinding(w http.ResponseWriter, r *http.Request)

func (*Handler) AddGovernanceRole

func (h *Handler) AddGovernanceRole(w http.ResponseWriter, r *http.Request)

func (*Handler) AddGroupMember

func (h *Handler) AddGroupMember(w http.ResponseWriter, r *http.Request)

AddGroupMember godoc POST /api/v1/recording-groups/{id}/members Body: {"activity_id": "uuid"} Idempotent: returns 200 OK if the activity is already a member of this group. Returns 409 Conflict if the activity already belongs to a different group.

func (*Handler) AddRouteTag

func (h *Handler) AddRouteTag(w http.ResponseWriter, r *http.Request)

AddRouteTag adds a single tag to a route. POST /api/v1/routes/{id}/tags/{tagId}

func (*Handler) AddSegmentFavorite

func (h *Handler) AddSegmentFavorite(w http.ResponseWriter, r *http.Request)

AddSegmentFavorite adds a segment to the user's favorites.

func (*Handler) AddTargetCollectionMember

func (h *Handler) AddTargetCollectionMember(w http.ResponseWriter, r *http.Request)

func (*Handler) AddTargetReportStatus

func (h *Handler) AddTargetReportStatus(w http.ResponseWriter, r *http.Request)

func (*Handler) AdminBackfillActivityEnrichments

func (h *Handler) AdminBackfillActivityEnrichments(w http.ResponseWriter, r *http.Request)

AdminBackfillActivityEnrichments starts the global activity track enrichment backfill. The admin middleware performs the role check; this handler only requires the authenticated admin ID for background job ownership.

POST /api/v1/admin/backfill-activity-enrichments?dry_run=true&force=true

func (*Handler) AdminGetEmailTemplate

func (h *Handler) AdminGetEmailTemplate(w http.ResponseWriter, r *http.Request)

func (*Handler) AdminGetSettings

func (h *Handler) AdminGetSettings(w http.ResponseWriter, r *http.Request)

AdminGetSettings handles GET /api/v1/admin/settings. Returns all settings grouped by category; encrypted values are masked as "***". Also includes a "_meta" envelope with read-only server-side context that is env-only (not settable via PUT /settings).

func (*Handler) AdminListEmailTemplates

func (h *Handler) AdminListEmailTemplates(w http.ResponseWriter, r *http.Request)

func (*Handler) AdminPreviewEmailTemplate

func (h *Handler) AdminPreviewEmailTemplate(w http.ResponseWriter, r *http.Request)

func (*Handler) AdminPutEmailTemplate

func (h *Handler) AdminPutEmailTemplate(w http.ResponseWriter, r *http.Request)

func (*Handler) AdminPutSettings

func (h *Handler) AdminPutSettings(w http.ResponseWriter, r *http.Request)

AdminPutSettings handles PUT /api/v1/admin/settings. Accepts a map[string]*string where nil = delete (revert to default) and "***" = no change.

func (*Handler) AdminReloadSettings

func (h *Handler) AdminReloadSettings(w http.ResponseWriter, r *http.Request)

AdminReloadSettings handles POST /api/v1/admin/settings/reload. Forces a cache reload from the database (useful when DB was modified externally).

func (*Handler) AdminResetEmailTemplate

func (h *Handler) AdminResetEmailTemplate(w http.ResponseWriter, r *http.Request)

func (*Handler) AdminTestEmail

func (h *Handler) AdminTestEmail(w http.ResponseWriter, r *http.Request)

AdminTestEmail handles POST /api/v1/admin/settings/test-email. Sends a test email to the requesting admin to verify SMTP configuration. Rate-limited to 1 request per minute per admin to prevent SMTP flooding.

func (*Handler) AdminTestEmailTemplate

func (h *Handler) AdminTestEmailTemplate(w http.ResponseWriter, r *http.Request)

func (*Handler) AdminToggleUserHotfolder

func (h *Handler) AdminToggleUserHotfolder(w http.ResponseWriter, r *http.Request)

AdminToggleUserHotfolder handles POST /api/v1/admin/users/{id}/hotfolder. Enables or disables the per-user hotfolder watcher. Request body: {"enabled": true|false}

func (*Handler) ApplyActivityEdits

func (h *Handler) ApplyActivityEdits(w http.ResponseWriter, r *http.Request)

ApplyActivityEdits applies the saved edits (HR spike smooth, GPS outlier smooth, trim) to the activity streams and triggers a recompute.

POST /api/v1/activities/{id}/edits/apply

func (*Handler) ApplyRules

func (h *Handler) ApplyRules(w http.ResponseWriter, r *http.Request)

func (*Handler) ApplyRulesToActivity

func (h *Handler) ApplyRulesToActivity(ctx context.Context, activityID, userID string, allRules []model.AutomationRule, triggerEvent string) error

ApplyRulesToActivity applies automation rules and refreshes any linked local activity file path because rule actions can change schema-relevant fields.

func (*Handler) ApplySingleRule

func (h *Handler) ApplySingleRule(w http.ResponseWriter, r *http.Request)

ApplySingleRule evaluates a single rule against all activities for the user. POST /api/v1/automation-rules/{id}/apply?dry_run=true|false

func (*Handler) AssignActivitiesToTag

func (h *Handler) AssignActivitiesToTag(w http.ResponseWriter, r *http.Request)

AssignActivitiesToTag manually assigns a list of activities to a tag. POST /api/v1/tags/{id}/assign-activities Body: {"activity_ids": ["id1", "id2"]}

func (*Handler) AssignImportQueueItem

func (h *Handler) AssignImportQueueItem(w http.ResponseWriter, r *http.Request)

AssignImportQueueItem handles POST /api/v1/import-queue/{id}/assign Assigns a queued image_no_match item to an activity via the ImportService pipeline. Body: {"activity_id": "uuid"}

func (*Handler) BackfillDeveloperApps

func (h *Handler) BackfillDeveloperApps(w http.ResponseWriter, r *http.Request)

BackfillDeveloperApps backfills activity_developer_apps for all activities that have developer field definitions with known application IDs but no corresponding entry in activity_developer_apps yet.

POST /api/v1/admin/backfill-developer-apps

func (*Handler) BackfillDeveloperFieldColumns

func (h *Handler) BackfillDeveloperFieldColumns(w http.ResponseWriter, r *http.Request)

BackfillDeveloperFieldColumns promotes Suunto developer-field values from activity_streams.developer_fields JSONB into dedicated activity columns.

Stream columns (radar_vehicle_count, radar_nearest_range_m, etc.) cannot be backfilled via direct SQL UPDATE because activity_streams uses TimescaleDB columnar compression; UPDATEs on compressed chunks are not supported. For those columns, a full recompute from the original FIT file is needed.

POST /api/v1/admin/backfill-developer-field-columns Optional query param: ?user_id= to restrict to a single user.

func (*Handler) BackfillEnrichment

func (h *Handler) BackfillEnrichment(w http.ResponseWriter, r *http.Request)

BackfillEnrichment re-runs stage1 enrichment for all activities.

func (*Handler) BackfillHeatmap

func (h *Handler) BackfillHeatmap(w http.ResponseWriter, r *http.Request)

func (*Handler) BackfillHeatmapPoints

func (h *Handler) BackfillHeatmapPoints(w http.ResponseWriter, r *http.Request)

func (*Handler) BackfillMetaSidecars

func (h *Handler) BackfillMetaSidecars(w http.ResponseWriter, r *http.Request)

BackfillMetaSidecars generates missing .meta companions for:

  • Phase 1: activity files that don't have a sidecar yet (has_sidecar=false)
  • Phase 2: photo files with manually assigned locations (has_sidecar=false)

Runs as a background job with 4 concurrent workers.

POST /api/v1/admin/storage/backfill-meta

func (*Handler) BackfillSunriseSunset

func (h *Handler) BackfillSunriseSunset(w http.ResponseWriter, r *http.Request)

BackfillSunriseSunset calculates sunrise/sunset times for all activities. For events that occur during an activity, it queries the closest GPS stream point to refine the calculation based on the track position at that time.

func (*Handler) BackfillThumbnails

func (h *Handler) BackfillThumbnails(w http.ResponseWriter, r *http.Request)

BackfillThumbnails generates missing thumbnails for all activity photos whose original media file is stored in ShadowFS but that have no 'thumb' derivative. It runs globally across all users (admin-only endpoint).

POST /api/v1/admin/storage/backfill-thumbnails

func (*Handler) BackfillTimezones

func (h *Handler) BackfillTimezones(w http.ResponseWriter, r *http.Request)

BackfillTimezones sets the IANA timezone for all GPS activities that don't have one yet, and upgrades fixed-offset timezones (e.g. "+01:00") to proper IANA names where GPS is available.

func (*Handler) BackfillWeather

func (h *Handler) BackfillWeather(w http.ResponseWriter, r *http.Request)

BackfillWeather fetches weather data for all activities missing it (background job).

func (*Handler) BatchUpdateAllRegions

func (h *Handler) BatchUpdateAllRegions(w http.ResponseWriter, r *http.Request)

BatchUpdateAllRegions triggers an incremental update for all imported regions. PBFs are patched individually, then reimported via per-region RunPerRegion. POST /api/v1/admin/osm-regions/update-all

func (*Handler) BuildBRouterSegments

func (h *Handler) BuildBRouterSegments(w http.ResponseWriter, r *http.Request)

BuildBRouterSegments builds BRouter rd5 segment files from local PBF data. This is a standalone endpoint — does NOT trigger OSM reimport or enrichment. POST /admin/routing/build-brouter-segments

func (*Handler) BuildCoverage

func (h *Handler) BuildCoverage(w http.ResponseWriter, r *http.Request)

func (*Handler) BulkSortMapLayers

func (h *Handler) BulkSortMapLayers(w http.ResponseWriter, r *http.Request)

BulkSortMapLayers — admin only. Body: [{id, sort_order}, ...]. Retries up to 3× on serialization failure (Postgres SQLSTATE 40001) because BulkSortMapLayers runs under SERIALIZABLE isolation.

func (*Handler) BulkSortUserMapLayers

func (h *Handler) BulkSortUserMapLayers(w http.ResponseWriter, r *http.Request)

BulkSortUserMapLayers reorders the authenticated user's layers. Retries up to 3× on SERIALIZABLE serialization failure (SQLSTATE 40001).

func (*Handler) CancelOSMImport

func (h *Handler) CancelOSMImport(w http.ResponseWriter, r *http.Request)

CancelOSMImport bricht einen laufenden OSM-Import ab. POST /api/v1/admin/osm-import/cancel/{jobID}

func (*Handler) ChatRoutes

func (h *Handler) ChatRoutes() http.Handler

ChatRoutes returns a chi.Router with all chat-related routes.

func (*Handler) ClearTagCoverImage

func (h *Handler) ClearTagCoverImage(w http.ResponseWriter, r *http.Request)

ClearTagCoverImage löscht das Cover-Bild und den Thumbnail eines Tags. Das verknüpfte Activity-Photo bleibt unangetastet. DELETE /api/v1/tags/{id}/cover-image

func (*Handler) ConfirmAllLiftCandidates

func (h *Handler) ConfirmAllLiftCandidates(w http.ResponseWriter, r *http.Request)

ConfirmAllLiftCandidates handles POST /api/v1/activities/{id}/lifts/confirm-all.

Bulk-confirms every unconfirmed, non-rejected lift candidate of the activity, sets lifts_acknowledged_at to NOW(), and recomputes lift_* aggregates atomically inside the DB layer. Idempotent.

Phase 6.3.1.2 bullet 1.

func (*Handler) ConfirmChatToolCall

func (h *Handler) ConfirmChatToolCall(w http.ResponseWriter, r *http.Request)

func (*Handler) ConfirmLiftCandidate

func (h *Handler) ConfirmLiftCandidate(w http.ResponseWriter, r *http.Request)

ConfirmLiftCandidate marks a lift candidate as confirmed. POST /api/v1/activities/{id}/lift-candidates/{cid}/confirm

func (*Handler) CorrectActivityDistance

func (h *Handler) CorrectActivityDistance(w http.ResponseWriter, r *http.Request)

CorrectActivityDistance recalculates the activity's distance from its GPS track using the Haversine formula over all stored stream points. Updates distance_m in the activities table. Runs as a background job.

POST /api/v1/activities/{id}/correct-distance

Response: 202 Accepted { "job_id": "<uuid>", "status": "started" }

func (*Handler) CorrectActivityElevation

func (h *Handler) CorrectActivityElevation(w http.ResponseWriter, r *http.Request)

CorrectActivityElevation applies DEM-based elevation correction to an existing activity in the background.

POST /api/v1/activities/{id}/correct-elevation

Response: 202 Accepted { "job_id": "<uuid>", "status": "started" }

The job queries dem.get_elevation() from the PostGIS DEM raster for every GPS-bearing stream point, updates altitude_m in activity_streams where the DEM value deviates by less than 50 m from the recorded value, and sets activities.elevation_source = 'dem'.

func (*Handler) CountImportQueue

func (h *Handler) CountImportQueue(w http.ResponseWriter, r *http.Request)

CountImportQueue handles GET /api/v1/import-queue/count

func (*Handler) CoverageBuildInfo

func (h *Handler) CoverageBuildInfo(w http.ResponseWriter, r *http.Request)

CoverageBuildInfo returns the coverage build timestamp for cache-busting.

func (*Handler) CoverageTile

func (h *Handler) CoverageTile(w http.ResponseWriter, r *http.Request)

func (*Handler) CreateBikeSetup

func (h *Handler) CreateBikeSetup(w http.ResponseWriter, r *http.Request)

func (*Handler) CreateChainRotation

func (h *Handler) CreateChainRotation(w http.ResponseWriter, r *http.Request)

func (*Handler) CreateChatSession

func (h *Handler) CreateChatSession(w http.ResponseWriter, r *http.Request)

CreateChatSession handles POST /api/v1/chat/sessions

func (*Handler) CreateComponent

func (h *Handler) CreateComponent(w http.ResponseWriter, r *http.Request)

func (*Handler) CreateCustomMap

func (h *Handler) CreateCustomMap(w http.ResponseWriter, r *http.Request)

func (*Handler) CreateCustomMapMarker

func (h *Handler) CreateCustomMapMarker(w http.ResponseWriter, r *http.Request)

func (*Handler) CreateGear

func (h *Handler) CreateGear(w http.ResponseWriter, r *http.Request)

func (*Handler) CreateGovernanceScope

func (h *Handler) CreateGovernanceScope(w http.ResponseWriter, r *http.Request)

func (*Handler) CreateHealthBloodPressure

func (h *Handler) CreateHealthBloodPressure(w http.ResponseWriter, r *http.Request)

POST /health/blood-pressure — manual blood pressure entry creation

func (*Handler) CreateHealthWeightEntry

func (h *Handler) CreateHealthWeightEntry(w http.ResponseWriter, r *http.Request)

POST /health/weight — manual weight entry creation

func (*Handler) CreateManualActivity

func (h *Handler) CreateManualActivity(w http.ResponseWriter, r *http.Request)

CreateManualActivity handles POST /api/v1/activities/manual.

func (*Handler) CreateMapLayer

func (h *Handler) CreateMapLayer(w http.ResponseWriter, r *http.Request)

CreateMapLayer — admin only. Decodes a MapLayer from the request body, enforces server-controlled fields, validates source-kind-specific constraints, sanitizes attribution, then inserts.

func (*Handler) CreateObjectAccessScope

func (h *Handler) CreateObjectAccessScope(w http.ResponseWriter, r *http.Request)

func (*Handler) CreateOrExtendCommuteRule

func (h *Handler) CreateOrExtendCommuteRule(w http.ResponseWriter, r *http.Request)

func (*Handler) CreatePrivacyZone

func (h *Handler) CreatePrivacyZone(w http.ResponseWriter, r *http.Request)

func (*Handler) CreateRoute

func (h *Handler) CreateRoute(w http.ResponseWriter, r *http.Request)

CreateRoute creates a new planned route for the authenticated user. POST /api/v1/routes

func (*Handler) CreateRouteCourseRelationship

func (h *Handler) CreateRouteCourseRelationship(w http.ResponseWriter, r *http.Request)

func (*Handler) CreateRoutePublicationPrivacyReview

func (h *Handler) CreateRoutePublicationPrivacyReview(w http.ResponseWriter, r *http.Request)

func (*Handler) CreateRouteRelationship

func (h *Handler) CreateRouteRelationship(w http.ResponseWriter, r *http.Request)

func (*Handler) CreateRouteSnapshot

func (h *Handler) CreateRouteSnapshot(w http.ResponseWriter, r *http.Request)

func (*Handler) CreateRule

func (h *Handler) CreateRule(w http.ResponseWriter, r *http.Request)

func (*Handler) CreateSegment

func (h *Handler) CreateSegment(w http.ResponseWriter, r *http.Request)

func (*Handler) CreateServiceEvent

func (h *Handler) CreateServiceEvent(w http.ResponseWriter, r *http.Request)

func (*Handler) CreateServiceInterval

func (h *Handler) CreateServiceInterval(w http.ResponseWriter, r *http.Request)

func (*Handler) CreateTag

func (h *Handler) CreateTag(w http.ResponseWriter, r *http.Request)

CreateTag creates a new tag for the authenticated user.

func (*Handler) CreateTargetCollection

func (h *Handler) CreateTargetCollection(w http.ResponseWriter, r *http.Request)

func (*Handler) CreateTargetPlace

func (h *Handler) CreateTargetPlace(w http.ResponseWriter, r *http.Request)

func (*Handler) CreateTargetRegion

func (h *Handler) CreateTargetRegion(w http.ResponseWriter, r *http.Request)

func (*Handler) CreateTargetReport

func (h *Handler) CreateTargetReport(w http.ResponseWriter, r *http.Request)

func (*Handler) CreateTargetUserContent

func (h *Handler) CreateTargetUserContent(w http.ResponseWriter, r *http.Request)

func (*Handler) CreateUserMapLayer

func (h *Handler) CreateUserMapLayer(w http.ResponseWriter, r *http.Request)

CreateUserMapLayer creates a custom layer for the authenticated user. Enforces: per-user row limit, URL validation with private-IP block, attribution sanitization.

func (*Handler) DeleteActivities

func (h *Handler) DeleteActivities(w http.ResponseWriter, r *http.Request)

DeleteActivities bulk-deletes multiple activities owned by the authenticated user.

DELETE /api/v1/activities Body: {"ids": ["uuid1", "uuid2", ...]}

func (*Handler) DeleteActivity

func (h *Handler) DeleteActivity(w http.ResponseWriter, r *http.Request)

func (*Handler) DeleteActivityEdits

func (h *Handler) DeleteActivityEdits(w http.ResponseWriter, r *http.Request)

DeleteActivityEdits removes the edit sidecar and clears the has_edits flag.

DELETE /api/v1/activities/{id}/edits

func (*Handler) DeleteActivityPhoto

func (h *Handler) DeleteActivityPhoto(w http.ResponseWriter, r *http.Request)

func (*Handler) DeleteAllNotifications

func (h *Handler) DeleteAllNotifications(w http.ResponseWriter, r *http.Request)

DeleteAllNotifications löscht alle Benachrichtigungen des authentifizierten Users.

DELETE /api/v1/notifications

func (*Handler) DeleteBikeSetup

func (h *Handler) DeleteBikeSetup(w http.ResponseWriter, r *http.Request)

func (*Handler) DeleteChainRotation

func (h *Handler) DeleteChainRotation(w http.ResponseWriter, r *http.Request)

func (*Handler) DeleteChatSession

func (h *Handler) DeleteChatSession(w http.ResponseWriter, r *http.Request)

DeleteChatSession handles DELETE /api/v1/chat/sessions/{id}

func (*Handler) DeleteComponent

func (h *Handler) DeleteComponent(w http.ResponseWriter, r *http.Request)

func (*Handler) DeleteCustomMap

func (h *Handler) DeleteCustomMap(w http.ResponseWriter, r *http.Request)

func (*Handler) DeleteCustomMapMarker

func (h *Handler) DeleteCustomMapMarker(w http.ResponseWriter, r *http.Request)

func (*Handler) DeleteDevice

func (h *Handler) DeleteDevice(w http.ResponseWriter, r *http.Request)

DeleteDevice removes a registered device token.

DELETE /api/v1/devices/{id}

func (*Handler) DeleteGear

func (h *Handler) DeleteGear(w http.ResponseWriter, r *http.Request)

func (*Handler) DeleteGearDocument

func (h *Handler) DeleteGearDocument(w http.ResponseWriter, r *http.Request)

func (*Handler) DeleteHardware

func (h *Handler) DeleteHardware(w http.ResponseWriter, r *http.Request)

DeleteHardware handles DELETE /api/v1/hardware/{id}

func (*Handler) DeleteImportQueueItem

func (h *Handler) DeleteImportQueueItem(w http.ResponseWriter, r *http.Request)

DeleteImportQueueItem handles DELETE /api/v1/import-queue/{id}

func (*Handler) DeleteLiftDetectionConfig

func (h *Handler) DeleteLiftDetectionConfig(w http.ResponseWriter, r *http.Request)

DeleteLiftDetectionConfig handles DELETE /api/v1/activities/{id}/lifts/detection-config. Resets both per-activity override columns to NULL so the pipeline uses the user-level setting again. Idempotent — deleting already-null overrides → 204.

func (*Handler) DeleteMapLayer

func (h *Handler) DeleteMapLayer(w http.ResponseWriter, r *http.Request)

DeleteMapLayer — admin only.

func (*Handler) DeleteNotification

func (h *Handler) DeleteNotification(w http.ResponseWriter, r *http.Request)

DeleteNotification löscht eine einzelne Benachrichtigung des authentifizierten Users.

DELETE /api/v1/notifications/{id}

func (*Handler) DeleteOSMRegion

func (h *Handler) DeleteOSMRegion(w http.ResponseWriter, r *http.Request)

DeleteOSMRegion removes a region: deletes OSM data, tracking entry, and PBF files. DELETE /api/v1/admin/osm-regions/{regionID}

func (*Handler) DeleteOrphanTag

func (h *Handler) DeleteOrphanTag(w http.ResponseWriter, r *http.Request)

DeleteOrphanTag removes an orphan tag name (not in tags table) from all activities.tags TEXT[]. DELETE /api/v1/tags/orphan body: {"name": "tagname"}

func (*Handler) DeletePrivacyZone

func (h *Handler) DeletePrivacyZone(w http.ResponseWriter, r *http.Request)

func (*Handler) DeleteRoute

func (h *Handler) DeleteRoute(w http.ResponseWriter, r *http.Request)

DeleteRoute removes a planned route, enforcing ownership. DELETE /api/v1/routes/{id}

func (*Handler) DeleteRoutePhoto

func (h *Handler) DeleteRoutePhoto(w http.ResponseWriter, r *http.Request)

DeleteRoutePhoto handles DELETE /api/v1/routes/{id}/photos/{photoId}

func (*Handler) DeleteRule

func (h *Handler) DeleteRule(w http.ResponseWriter, r *http.Request)

func (*Handler) DeleteSegment

func (h *Handler) DeleteSegment(w http.ResponseWriter, r *http.Request)

func (*Handler) DeleteSegments

func (h *Handler) DeleteSegments(w http.ResponseWriter, r *http.Request)

DeleteSegments bulk-deletes multiple segments owned by the authenticated user.

DELETE /api/v1/segments Body: {"ids": ["uuid1", "uuid2", ...]} OSM-imported lift segments are silently skipped (not counted in the response).

func (*Handler) DeleteServiceEvent

func (h *Handler) DeleteServiceEvent(w http.ResponseWriter, r *http.Request)

func (*Handler) DeleteServiceInterval

func (h *Handler) DeleteServiceInterval(w http.ResponseWriter, r *http.Request)

func (*Handler) DeleteTag

func (h *Handler) DeleteTag(w http.ResponseWriter, r *http.Request)

DeleteTag deletes a tag.

func (*Handler) DeleteTagZone

func (h *Handler) DeleteTagZone(w http.ResponseWriter, r *http.Request)

DeleteTagZone removes the zone from a tag. DELETE /api/v1/tag-zones/{tagId}

func (*Handler) DeleteTargetCollection

func (h *Handler) DeleteTargetCollection(w http.ResponseWriter, r *http.Request)

func (*Handler) DeleteTargetCollectionMember

func (h *Handler) DeleteTargetCollectionMember(w http.ResponseWriter, r *http.Request)

func (*Handler) DeleteTargetPlace

func (h *Handler) DeleteTargetPlace(w http.ResponseWriter, r *http.Request)

func (*Handler) DeleteTargetRegion

func (h *Handler) DeleteTargetRegion(w http.ResponseWriter, r *http.Request)

func (*Handler) DeleteTargetUserContent

func (h *Handler) DeleteTargetUserContent(w http.ResponseWriter, r *http.Request)

func (*Handler) DeleteUserMapLayer

func (h *Handler) DeleteUserMapLayer(w http.ResponseWriter, r *http.Request)

DeleteUserMapLayer deletes a layer owned by the authenticated user. User isolation enforced at DB layer via WHERE user_id=$1 AND id=$2.

func (*Handler) DetectGPSOutliersHandler

func (h *Handler) DetectGPSOutliersHandler(w http.ResponseWriter, r *http.Request)

DetectGPSOutliersHandler detects GPS outliers in an activity's stream data.

POST /api/v1/activities/{id}/edits/detect-gps-outliers

func (*Handler) DetectGarminModules

func (h *Handler) DetectGarminModules(w http.ResponseWriter, r *http.Request)

DetectGarminModules probes a Garmin export directory and returns available import modules.

POST /api/v1/import/garmin/detect Body: {"source_path": "/path/to/garmin/export"}

func (*Handler) DetectHRSpikesHandler

func (h *Handler) DetectHRSpikesHandler(w http.ResponseWriter, r *http.Request)

DetectHRSpikesHandler detects heart rate spikes in an activity's stream data.

POST /api/v1/activities/{id}/edits/detect-hr-spikes

func (*Handler) DetectMaxHR

func (h *Handler) DetectMaxHR(w http.ResponseWriter, r *http.Request)

DetectMaxHR calculates the 90th percentile max HR from historical activity data and updates max_hr_detected_bpm if the result is higher than the current value.

func (*Handler) DetectStravaModules

func (h *Handler) DetectStravaModules(w http.ResponseWriter, r *http.Request)

DetectStravaModules probes a Strava export directory and returns available import modules.

POST /api/v1/import/strava/detect Body: {"source_path": "/path/to/strava/export"}

func (*Handler) DismissFusionCandidate

func (h *Handler) DismissFusionCandidate(w http.ResponseWriter, r *http.Request)

DismissFusionCandidate markiert ein Fusion-Kandidatenpaar als ignoriert, sodass es nicht mehr in der Queue auftaucht.

POST /fusion-candidates/dismiss Body: {"activity_id_a": "<uuid>", "activity_id_b": "<uuid>"}

Die kanonische Reihenfolge (activity_id_a < activity_id_b) wird server-seitig normalisiert. Idempotent: ein bereits dismisstes Paar liefert ebenfalls 204.

func (*Handler) DissolveRecordingGroup

func (h *Handler) DissolveRecordingGroup(w http.ResponseWriter, r *http.Request)

DissolveRecordingGroup godoc POST /api/v1/recording-groups/{id}/dissolve

func (*Handler) DownloadBRouterSegments

func (h *Handler) DownloadBRouterSegments(w http.ResponseWriter, r *http.Request)

DownloadBRouterSegments downloads pre-built BRouter rd5 segments from brouter.de. POST /admin/routing/download-brouter-segments

func (*Handler) DownloadDEM

func (h *Handler) DownloadDEM(w http.ResponseWriter, r *http.Request)

DownloadDEM triggers a background DEM download for specified or all imported regions. POST /api/v1/admin/dem/download

func (*Handler) DownloadGearDocument

func (h *Handler) DownloadGearDocument(w http.ResponseWriter, r *http.Request)

func (*Handler) DuplicateRoute

func (h *Handler) DuplicateRoute(w http.ResponseWriter, r *http.Request)

DuplicateRoute creates a copy of a planned route owned by the authenticated user. POST /api/v1/routes/{id}/duplicate

func (*Handler) ElevationProfile

func (h *Handler) ElevationProfile(w http.ResponseWriter, r *http.Request)

func (*Handler) EnrichmentSuggest

func (h *Handler) EnrichmentSuggest(w http.ResponseWriter, r *http.Request)

EnrichmentSuggest returns aggregated OSM surface/sac/mtb suggestions for a segment LineString.

POST /api/v1/segments/enrichment-suggest Body: SegmentEnrichmentRequest Response: enrichment.SegmentEnrichmentResponse

DoS-Hardening: two layers of defense before any Viterbi/PostGIS call:

  1. http.MaxBytesReader → 1 MB body cap.
  2. maxCoordinates check after decode → protects against [[0,0]]-repetition.

func (*Handler) ExecuteFileRename

func (h *Handler) ExecuteFileRename(w http.ResponseWriter, r *http.Request)

ExecuteFileRename renames all active files of the given type to paths computed from the current (already-saved) schema template. Uses a pg_advisory_lock on a pinned connection and registers a background_job entry for ActiveJobsIndicator visibility and crash-safety. POST /api/v1/settings/file-schemas/{type}/rename-execute

func (*Handler) ExecuteRename

func (h *Handler) ExecuteRename(w http.ResponseWriter, r *http.Request)

ExecuteRename is a deleted legacy handler. File renaming is now managed entirely through the FileStore/ShadowFS layer.

func (*Handler) ExportActivitiesZip

func (h *Handler) ExportActivitiesZip(w http.ResponseWriter, r *http.Request)

ExportActivitiesZip exports multiple activities as a single ZIP archive.

POST /api/v1/activities/export-zip Body: {"ids": ["uuid1", ...], "format": "original"|"gpx"|"tcx"|"fit"}

If the request body is absent or ids is empty/omitted, ALL activities for the authenticated user are exported. Callers that want a subset must provide ids. This "no body = full export" behavior is intentional for a one-click user-data export.

func (*Handler) ExportActivity

func (h *Handler) ExportActivity(w http.ResponseWriter, r *http.Request)

ExportActivity exports an activity in the requested format.

func (*Handler) ExportRouteGPX

func (h *Handler) ExportRouteGPX(w http.ResponseWriter, r *http.Request)

ExportRouteGPX handles GET /api/v1/routes/{id}/gpx Returns a GPX file for the requested planned route. Ownership is enforced via GetRoute (WHERE user_id = $2).

func (*Handler) ExportSegment

func (h *Handler) ExportSegment(w http.ResponseWriter, r *http.Request)

ExportSegment handles GET /api/v1/segments/{id}/export?format=gpx|geojson

func (*Handler) ExportSegments

func (h *Handler) ExportSegments(w http.ResponseWriter, r *http.Request)

ExportSegments handles POST /api/v1/segments/export Body: {"ids": [...], "format": "gpx"|"geojson"} Returns a ZIP archive containing one file per segment.

func (*Handler) ForkRoute

func (h *Handler) ForkRoute(w http.ResponseWriter, r *http.Request)

func (*Handler) FusionMerge

func (h *Handler) FusionMerge(w http.ResponseWriter, r *http.Request)

FusionMerge implements the unified-timeline merge without a target activity. POST /api/v1/fusion-merge

func (*Handler) GeocodingBackfill

func (h *Handler) GeocodingBackfill(w http.ResponseWriter, r *http.Request)

GeocodingBackfill runs reverse geocoding for all activities that have a start_location but no start_city yet. Requires the user to have enabled an external fallback provider (PostGIS-only instances will already have been resolved at import time, so the gap can only be filled by the fallback).

POST /api/v1/enrichment/geocoding-backfill

func (*Handler) GeocodingGap

func (h *Handler) GeocodingGap(w http.ResponseWriter, r *http.Request)

GeocodingGap reports how many activities currently have no start_city and could benefit from a reverse-geocoding backfill.

GET /api/v1/enrichment/geocoding-gap

func (*Handler) GetActivity

func (h *Handler) GetActivity(w http.ResponseWriter, r *http.Request)

func (*Handler) GetActivityBestEfforts

func (h *Handler) GetActivityBestEfforts(w http.ResponseWriter, r *http.Request)

GetActivityBestEfforts returns all best effort records for a single activity, each annotated with the user's all-time rank (top-3 only), plus near-miss entries for efforts that almost beat the user's current personal record.

Response shape:

{
  "best_efforts": [...],
  "near_misses":  [...]
}

GET /api/v1/activities/{id}/best-efforts

func (*Handler) GetActivityClimbs

func (h *Handler) GetActivityClimbs(w http.ResponseWriter, r *http.Request)

GetActivityClimbs returns detected climbs for an activity.

func (*Handler) GetActivityDetectedLaps

func (h *Handler) GetActivityDetectedLaps(w http.ResponseWriter, r *http.Request)

func (*Handler) GetActivityDeveloperFields

func (h *Handler) GetActivityDeveloperFields(w http.ResponseWriter, r *http.Request)

func (*Handler) GetActivityDiveInfo

func (h *Handler) GetActivityDiveInfo(w http.ResponseWriter, r *http.Request)

GetActivityDiveInfo returns the dive settings and gas mixes for an activity. GET /api/v1/activities/{id}/dive

func (*Handler) GetActivityEdits

func (h *Handler) GetActivityEdits(w http.ResponseWriter, r *http.Request)

GetActivityEdits loads the edit sidecar for an activity from S3.

GET /api/v1/activities/{id}/edits

func (*Handler) GetActivityHealthContext

func (h *Handler) GetActivityHealthContext(w http.ResponseWriter, r *http.Request)

GetActivityHealthContext returns health context (sleep, HRV, resting HR) for the night/morning before the given activity. GET /api/v1/activities/{id}/health-context

func (*Handler) GetActivityIntervals

func (h *Handler) GetActivityIntervals(w http.ResponseWriter, r *http.Request)

GetActivityIntervals returns detected training intervals for an activity. GET /api/v1/activities/{id}/intervals

func (*Handler) GetActivitySegmentEfforts

func (h *Handler) GetActivitySegmentEfforts(w http.ResponseWriter, r *http.Request)

func (*Handler) GetActivityStops

func (h *Handler) GetActivityStops(w http.ResponseWriter, r *http.Request)

GetActivityStops returns detected stops for an activity.

func (*Handler) GetAdminLibrarySettings

func (h *Handler) GetAdminLibrarySettings(w http.ResponseWriter, r *http.Request)

GetAdminLibrarySettings returns all library.* settings for admin consumption. GET /api/v1/admin/library-settings

func (*Handler) GetAdminMapLayers

func (h *Handler) GetAdminMapLayers(w http.ResponseWriter, r *http.Request)

GetAdminMapLayers — admin only. Returns ALL layers including disabled ones for management purposes. Returns { layers: [...], system_visibility: { key: {...}, ... } }.

func (*Handler) GetAdminSystemStatus

func (h *Handler) GetAdminSystemStatus(w http.ResponseWriter, r *http.Request)

func (*Handler) GetAdminSystemStatusExport

func (h *Handler) GetAdminSystemStatusExport(w http.ResponseWriter, r *http.Request)

func (*Handler) GetAdminUserLibraryDir

func (h *Handler) GetAdminUserLibraryDir(w http.ResponseWriter, r *http.Request)

GetAdminUserLibraryDir returns the raw library_dir_name for a target user. GET /api/v1/admin/users/{id}/library-dir

func (*Handler) GetBestEffortExclusions

func (h *Handler) GetBestEffortExclusions(w http.ResponseWriter, r *http.Request)

GetBestEffortExclusions returns all exclusion records for the authenticated user, newest first.

GET /api/v1/best-effort-exclusions

func (*Handler) GetBestEffortRecords

func (h *Handler) GetBestEffortRecords(w http.ResponseWriter, r *http.Request)

GetBestEffortRecords returns the top-N personal records for the authenticated user, optionally filtered by sport group and/or year.

Query params:

  • sport_group: e.g. "running", "cycling" (optional)
  • year: e.g. "2026" — filters to [Jan 1, Jan 1 next year) (optional)

GET /api/v1/personal-records (best-efforts flavour, distinct from analytics personal records)

func (*Handler) GetCapabilities

func (h *Handler) GetCapabilities(w http.ResponseWriter, r *http.Request)

GetCapabilities probes the infrastructure and returns which routing and geo features are available at runtime (Lite vs. Full mode). GET /api/v1/capabilities

func (*Handler) GetChainRotation

func (h *Handler) GetChainRotation(w http.ResponseWriter, r *http.Request)

func (*Handler) GetChatMessages

func (h *Handler) GetChatMessages(w http.ResponseWriter, r *http.Request)

GetChatMessages handles GET /api/v1/chat/sessions/{id}/messages

func (*Handler) GetChatPreferences

func (h *Handler) GetChatPreferences(w http.ResponseWriter, r *http.Request)

GetChatPreferences handles GET /api/v1/chat/preferences.

func (*Handler) GetChatProfileDefaults

func (h *Handler) GetChatProfileDefaults(w http.ResponseWriter, r *http.Request)

GetChatProfileDefaults returns the backend chat profile defaults used by the Go agent.

GET /api/v1/settings/ml/chat-profile-defaults

func (*Handler) GetChildActivities

func (h *Handler) GetChildActivities(w http.ResponseWriter, r *http.Request)

GetChildActivities returns all child activities for a multisport parent. GET /api/v1/activities/{id}/children

func (*Handler) GetClimbProEvents

func (h *Handler) GetClimbProEvents(w http.ResponseWriter, r *http.Request)

func (*Handler) GetCombinedTrack

func (h *Handler) GetCombinedTrack(w http.ResponseWriter, r *http.Request)

GetCombinedTrack returns a GeoJSON FeatureCollection with one Feature per child activity track. GET /api/v1/activities/{id}/combined-track

func (*Handler) GetComponentHistory

func (h *Handler) GetComponentHistory(w http.ResponseWriter, r *http.Request)

func (*Handler) GetDEMRegionStatus

func (h *Handler) GetDEMRegionStatus(w http.ResponseWriter, r *http.Request)

GetDEMRegionStatus returns DEM coverage per imported region + disk space info. GET /api/v1/admin/dem/status

func (*Handler) GetDEMStatus

func (h *Handler) GetDEMStatus(w http.ResponseWriter, r *http.Request)

GetDEMStatus returns available DEM data sources and tile counts. GET /api/v1/admin/dem-status

func (*Handler) GetEnrichmentSettings

func (h *Handler) GetEnrichmentSettings(w http.ResponseWriter, r *http.Request)

GetEnrichmentSettings returns the user's enrichment settings.

func (*Handler) GetEnrichmentStatus

func (h *Handler) GetEnrichmentStatus(w http.ResponseWriter, r *http.Request)

GetEnrichmentStatus returns per-module enrichment status for an activity.

func (*Handler) GetFileSchemas

func (h *Handler) GetFileSchemas(w http.ResponseWriter, r *http.Request)

GetFileSchemas returns all per-type file schemas for the current user. GET /api/v1/settings/file-schemas

func (*Handler) GetFileSchemasContext

func (h *Handler) GetFileSchemasContext(w http.ResponseWriter, r *http.Request)

GetFileSchemasContext returns editable/backend/admin_managed_reason metadata for the file-schemas settings UI. Separated from GET /file-schemas to keep that endpoint's top-level array shape backward-compatible. GET /api/v1/settings/file-schemas-context

NOTE: Route needs registration: GET /settings/file-schemas-context

func (*Handler) GetFusionCandidates

func (h *Handler) GetFusionCandidates(w http.ResponseWriter, r *http.Request)

GetFusionCandidates godoc GET /api/v1/activities/{id}/fusion-candidates Returns activities that are temporally close, making them potential candidates for stream fusion. Includes stream availability flags and temporal overlap percentage.

func (*Handler) GetFusionQueue

func (h *Handler) GetFusionQueue(w http.ResponseWriter, r *http.Request)

GetFusionQueue gibt Tier-2 Fusionskandidaten zurück. GET /api/v1/fusion-queue?limit=20

func (*Handler) GetGarminImportJob

func (h *Handler) GetGarminImportJob(w http.ResponseWriter, r *http.Request)

GetGarminImportJob returns the current status of an import job.

GET /api/v1/import/garmin/jobs/{id}

func (*Handler) GetGear

func (h *Handler) GetGear(w http.ResponseWriter, r *http.Request)

func (*Handler) GetGearCosts

func (h *Handler) GetGearCosts(w http.ResponseWriter, r *http.Request)

GetGearCosts returns the cost summary for a gear item.

func (*Handler) GetGearDuplicateCandidates

func (h *Handler) GetGearDuplicateCandidates(w http.ResponseWriter, r *http.Request)

GetGearDuplicateCandidates godoc GET /api/v1/gear/duplicate-candidates

func (*Handler) GetGearSchema

func (h *Handler) GetGearSchema(w http.ResponseWriter, r *http.Request)

GetGearSchema returns the effective gear schema for the authenticated user.

The effective schema is the generated base schema overlaid with the user's own customizations from gear_schema_definitions. The response includes an ETag derived from both the base schema content and the user-override content, so the client cache is invalidated on either change.

Supports If-None-Match for conditional GET (RFC 7232).

func (*Handler) GetGearSummary

func (h *Handler) GetGearSummary(w http.ResponseWriter, r *http.Request)

GetGearSummary returns aggregated usage statistics across a user's gear.

func (*Handler) GetGeoTags

func (h *Handler) GetGeoTags(w http.ResponseWriter, r *http.Request)

GetGeoTags returns geo tags for an activity.

func (*Handler) GetGlobalTopSegmentEfforts

func (h *Handler) GetGlobalTopSegmentEfforts(w http.ResponseWriter, r *http.Request)

GetGlobalTopSegmentEfforts returns all segment efforts where the authenticated user is ranked in the global top 10 (rank_overall <= 10).

func (*Handler) GetGovernanceScope

func (h *Handler) GetGovernanceScope(w http.ResponseWriter, r *http.Request)

func (*Handler) GetHardware

func (h *Handler) GetHardware(w http.ResponseWriter, r *http.Request)

GetHardware handles GET /api/v1/hardware/{id}

func (*Handler) GetHealthBloodPressure

func (h *Handler) GetHealthBloodPressure(w http.ResponseWriter, r *http.Request)

func (*Handler) GetHealthDailySummaries

func (h *Handler) GetHealthDailySummaries(w http.ResponseWriter, r *http.Request)

func (*Handler) GetHealthFitnessAge

func (h *Handler) GetHealthFitnessAge(w http.ResponseWriter, r *http.Request)

func (*Handler) GetHealthHRV

func (h *Handler) GetHealthHRV(w http.ResponseWriter, r *http.Request)

func (*Handler) GetHealthMonitoring

func (h *Handler) GetHealthMonitoring(w http.ResponseWriter, r *http.Request)

GetHealthMonitoring returns health monitoring samples with auto-bucketing. GET /api/v1/health/monitoring?from=RFC3339&to=RFC3339&bucket=1m|15m|1h|1d

func (*Handler) GetHealthMonitoringDaily

func (h *Handler) GetHealthMonitoringDaily(w http.ResponseWriter, r *http.Request)

GetHealthMonitoringDaily returns daily aggregated monitoring data from Continuous Aggregate. GET /api/v1/health/monitoring/daily?from=2006-01-02&to=2006-01-02

func (*Handler) GetHealthOverview

func (h *Handler) GetHealthOverview(w http.ResponseWriter, r *http.Request)

GET /health/overview

func (*Handler) GetHealthReadiness

func (h *Handler) GetHealthReadiness(w http.ResponseWriter, r *http.Request)

func (*Handler) GetHealthSleep

func (h *Handler) GetHealthSleep(w http.ResponseWriter, r *http.Request)

func (*Handler) GetHealthSpO2

func (h *Handler) GetHealthSpO2(w http.ResponseWriter, r *http.Request)

func (*Handler) GetHealthStress

func (h *Handler) GetHealthStress(w http.ResponseWriter, r *http.Request)

func (*Handler) GetHealthTrainingLoad

func (h *Handler) GetHealthTrainingLoad(w http.ResponseWriter, r *http.Request)

func (*Handler) GetHealthWeight

func (h *Handler) GetHealthWeight(w http.ResponseWriter, r *http.Request)

func (*Handler) GetHeatmapSettings

func (h *Handler) GetHeatmapSettings(w http.ResponseWriter, r *http.Request)

func (*Handler) GetImportQueueItemPreview

func (h *Handler) GetImportQueueItemPreview(w http.ResponseWriter, r *http.Request)

GetImportQueueItemPreview handles GET /api/v1/import-queue/{id}/preview It streams the queued original file for manual review, falling back to the thumbnail.

func (*Handler) GetImportQueueItemThumb

func (h *Handler) GetImportQueueItemThumb(w http.ResponseWriter, r *http.Request)

GetImportQueueItemThumb handles GET /api/v1/import-queue/{id}/thumb Returns the thumbnail image for a queued item stored in ShadowFS or S3.

func (*Handler) GetImportSettings

func (h *Handler) GetImportSettings(w http.ResponseWriter, r *http.Request)

GetImportSettings godoc GET /api/v1/settings/import

func (*Handler) GetImports

func (h *Handler) GetImports(w http.ResponseWriter, r *http.Request)

GetImports godoc GET /api/v1/imports Query params: status (comma-separated), source_platform, import_type, limit, offset import_type: activity | route | workout | monitoring | photo | video | document | gear | segment | archive | unknown (= IS NULL)

func (*Handler) GetKomootImportJob

func (h *Handler) GetKomootImportJob(w http.ResponseWriter, r *http.Request)

GetKomootImportJob returns the status of a Komoot import job.

GET /api/v1/import/komoot/jobs/{id}

func (*Handler) GetLLMConfig

func (h *Handler) GetLLMConfig(w http.ResponseWriter, r *http.Request)

GetLLMConfig returns the full LLM system configuration.

func (*Handler) GetLLMModels

func (h *Handler) GetLLMModels(w http.ResponseWriter, r *http.Request)

GetLLMModels returns available models for a backend (only Ollama supports auto-discovery).

func (*Handler) GetLLMUserConfig

func (h *Handler) GetLLMUserConfig(w http.ResponseWriter, r *http.Request)

GetLLMUserConfig returns user overrides.

func (*Handler) GetLaps

func (h *Handler) GetLaps(w http.ResponseWriter, r *http.Request)

func (*Handler) GetLengths

func (h *Handler) GetLengths(w http.ResponseWriter, r *http.Request)

func (*Handler) GetLibraryDir

func (h *Handler) GetLibraryDir(w http.ResponseWriter, r *http.Request)

GetLibraryDir returns the user's library directory name plus admin-model metadata. GET /api/v1/settings/library-dir

func (*Handler) GetLiftCandidates

func (h *Handler) GetLiftCandidates(w http.ResponseWriter, r *http.Request)

GetLiftCandidates returns unified lift ride candidates for an activity. GET /api/v1/activities/{id}/lifts/candidates

func (*Handler) GetLiftDetectionConfig

func (h *Handler) GetLiftDetectionConfig(w http.ResponseWriter, r *http.Request)

GetLiftDetectionConfig handles GET /api/v1/activities/{id}/lifts/detection-config. Returns the effective resolved options, the raw user-level setting, and the per-activity override pointers. User B accessing User A's activity → 404.

func (*Handler) GetMLSystemSettings

func (h *Handler) GetMLSystemSettings(w http.ResponseWriter, r *http.Request)

GetMLSystemSettings returns ML system-wide settings (nightly jobs config).

GET /api/v1/settings/ml

func (*Handler) GetMLUserSettings

func (h *Handler) GetMLUserSettings(w http.ResponseWriter, r *http.Request)

GetMLUserSettings returns ML user preferences (notification toggles).

GET /api/v1/settings/ml/user

func (*Handler) GetMaintenanceAlerts

func (h *Handler) GetMaintenanceAlerts(w http.ResponseWriter, r *http.Request)

func (*Handler) GetMapLayers

func (h *Handler) GetMapLayers(w http.ResponseWriter, r *http.Request)

GetMapLayers — public (any authenticated user). Returns only enabled layers so users never see disabled URLs. Returns { layers: [...], system_visibility: { key: {...}, ... } }.

func (*Handler) GetMe

func (h *Handler) GetMe(w http.ResponseWriter, r *http.Request)

GetMe returns the authenticated user's full profile. GET /api/v1/me

func (*Handler) GetOSMDiskUsage

func (h *Handler) GetOSMDiskUsage(w http.ResponseWriter, r *http.Request)

GetOSMDiskUsage returns estimated disk usage of OSM tables. Uses direct query instead of view for robustness (view may not exist or have permission issues). GET /api/v1/admin/osm-disk-usage

func (*Handler) GetOSMRegionStatus

func (h *Handler) GetOSMRegionStatus(w http.ResponseWriter, r *http.Request)

GetOSMRegionStatus returns all tracked import regions with metadata. GET /api/v1/admin/osm-regions/status

func (*Handler) GetOSMSchedule

func (h *Handler) GetOSMSchedule(w http.ResponseWriter, r *http.Request)

GetOSMSchedule returns current scheduler settings shared across all imported regions. GET /api/v1/admin/osm-schedule

func (*Handler) GetOSMStatus

func (h *Handler) GetOSMStatus(w http.ResponseWriter, r *http.Request)

GetOSMStatus returns counts from OSM tables. GET /api/v1/admin/osm-status

func (*Handler) GetPMC

func (h *Handler) GetPMC(w http.ResponseWriter, r *http.Request)

func (*Handler) GetPRHistory

func (h *Handler) GetPRHistory(w http.ResponseWriter, r *http.Request)

GetPRHistory returns the full chronological progression of best efforts for a (user, category, sport_group) triple — up to 200 entries — each annotated with its all-time performance rank. Intended for the PR Progression Timeline UI.

Query params:

  • category: e.g. "dist_5km" (required)
  • sport_group: e.g. "running" (required)

GET /api/v1/personal-records/history

func (*Handler) GetPowerCurve

func (h *Handler) GetPowerCurve(w http.ResponseWriter, r *http.Request)

GetPowerCurve returns the best-effort power duration curve for a user.

func (*Handler) GetProfile

func (h *Handler) GetProfile(w http.ResponseWriter, r *http.Request)

GetProfile returns the user's physiological profile.

func (*Handler) GetRecordingGroup

func (h *Handler) GetRecordingGroup(w http.ResponseWriter, r *http.Request)

GetRecordingGroup godoc GET /api/v1/recording-groups/{id}

func (*Handler) GetRecordingGroupMembers

func (h *Handler) GetRecordingGroupMembers(w http.ResponseWriter, r *http.Request)

GetRecordingGroupMembers godoc GET /api/v1/recording-groups/{id}/members

func (*Handler) GetRecordingGroups

func (h *Handler) GetRecordingGroups(w http.ResponseWriter, r *http.Request)

GetRecordingGroups godoc GET /api/v1/recording-groups?limit=50&offset=0

func (*Handler) GetRoute

func (h *Handler) GetRoute(w http.ResponseWriter, r *http.Request)

GetRoute fetches a single planned route by ID, enforcing ownership. GET /api/v1/routes/{id}

func (*Handler) GetRunningFormBaseline

func (h *Handler) GetRunningFormBaseline(w http.ResponseWriter, r *http.Request)

GetRunningFormBaseline returns 90-day median running form metrics.

GET /api/v1/running-form/baseline

func (*Handler) GetRunningFormTrend

func (h *Handler) GetRunningFormTrend(w http.ResponseWriter, r *http.Request)

GetRunningFormTrend returns per-activity running form values as a time series.

GET /api/v1/running-form/trend Optional query params: from=2025-01-01&to=2026-04-04

func (*Handler) GetSegment

func (h *Handler) GetSegment(w http.ResponseWriter, r *http.Request)

func (*Handler) GetSegmentFavoriteStatus

func (h *Handler) GetSegmentFavoriteStatus(w http.ResponseWriter, r *http.Request)

GetSegmentFavoriteStatus checks if a segment is favorited by the user.

func (*Handler) GetSegmentLeaderboard

func (h *Handler) GetSegmentLeaderboard(w http.ResponseWriter, r *http.Request)

func (*Handler) GetSegmentPRs

func (h *Handler) GetSegmentPRs(w http.ResponseWriter, r *http.Request)

GetSegmentPRs returns all segment efforts where the authenticated user achieved personal rank 1–3.

func (*Handler) GetSegmentPerformances

func (h *Handler) GetSegmentPerformances(w http.ResponseWriter, r *http.Request)

GetSegmentPerformances retrieves all efforts on a segment from the user's activities.

func (*Handler) GetSegmentVersionRevision

func (h *Handler) GetSegmentVersionRevision(w http.ResponseWriter, r *http.Request)

func (*Handler) GetSensors

func (h *Handler) GetSensors(w http.ResponseWriter, r *http.Request)

func (*Handler) GetSettingsLimits

func (h *Handler) GetSettingsLimits(w http.ResponseWriter, r *http.Request)

GetSettingsLimits returns a whitelist of user-relevant system-wide limits so normal users can read limits without hitting the admin endpoint. GET /api/v1/settings/limits — requires jwtAuth only (not admin).

func (*Handler) GetShadowFSSettings

func (h *Handler) GetShadowFSSettings(w http.ResponseWriter, r *http.Request)

GetShadowFSSettings is a deleted legacy handler. Callers should use GetShadowFSSettingsCompat instead (reads from user_file_schemas). This stub remains so existing route wiring does not break at compile time.

func (*Handler) GetShadowFSSettingsCompat deprecated

func (h *Handler) GetShadowFSSettingsCompat(w http.ResponseWriter, r *http.Request)

GetShadowFSSettingsCompat is a backward-compatibility shim for GET /settings/shadow-fs. It reads the activity schema from user_file_schemas (written by migration 000232) and returns it in the legacy ShadowFSSettingsResponse shape so existing clients continue to work.

Deprecated: use GET /settings/file-schemas instead.

func (*Handler) GetSplits

func (h *Handler) GetSplits(w http.ResponseWriter, r *http.Request)

func (*Handler) GetSportTypeStats

func (h *Handler) GetSportTypeStats(w http.ResponseWriter, r *http.Request)

GetSportTypeStats returns activity counts grouped by sport_type for the authenticated user. GET /api/v1/activities/sport-type-stats[?from=YYYY-MM-DD&to=YYYY-MM-DD]

func (*Handler) GetSportsTaxonomy

func (h *Handler) GetSportsTaxonomy(w http.ResponseWriter, r *http.Request)

func (*Handler) GetStravaImportJob

func (h *Handler) GetStravaImportJob(w http.ResponseWriter, r *http.Request)

GetStravaImportJob returns the current status of an import job.

GET /api/v1/import/strava/jobs/{id}

func (*Handler) GetStreams

func (h *Handler) GetStreams(w http.ResponseWriter, r *http.Request)

func (*Handler) GetSummitActivities

func (h *Handler) GetSummitActivities(w http.ResponseWriter, r *http.Request)

GetSummitActivities returns all activities linked to a summit. URL param: osm_id (int) — falls back to query param "name" when osm_id is 0.

func (*Handler) GetSummitActivitiesByName

func (h *Handler) GetSummitActivitiesByName(w http.ResponseWriter, r *http.Request)

GetSummitActivitiesByName returns all activities linked to a summit by name. Query param: name (required)

func (*Handler) GetSummitCollection

func (h *Handler) GetSummitCollection(w http.ResponseWriter, r *http.Request)

GetSummitCollection returns all summits visited by the authenticated user, grouped by osm_id with visit statistics. Optional query parameters: country (comma-separated ISO codes), min_elevation, max_elevation, year.

func (*Handler) GetSystemLayerKeys

func (h *Handler) GetSystemLayerKeys(w http.ResponseWriter, r *http.Request)

GetSystemLayerKeys — public (no auth required). Returns the 14 SystemLayerDescriptors that are the code source-of-truth. Mounted on the top-level router (no jwtAuth middleware) in main.go.

func (*Handler) GetTagActivities

func (h *Handler) GetTagActivities(w http.ResponseWriter, r *http.Request)

GetTagActivities returns a list of activities that have the given tag.

func (*Handler) GetTagZone

func (h *Handler) GetTagZone(w http.ResponseWriter, r *http.Request)

GetTagZone returns the zone for a tag. GET /api/v1/tag-zones/{tagId}

func (*Handler) GetTargetCollection

func (h *Handler) GetTargetCollection(w http.ResponseWriter, r *http.Request)

func (*Handler) GetTargetOverlaySummary

func (h *Handler) GetTargetOverlaySummary(w http.ResponseWriter, r *http.Request)

func (*Handler) GetTargetPlace

func (h *Handler) GetTargetPlace(w http.ResponseWriter, r *http.Request)

func (*Handler) GetTargetRegion

func (h *Handler) GetTargetRegion(w http.ResponseWriter, r *http.Request)

func (*Handler) GetTargetReport

func (h *Handler) GetTargetReport(w http.ResponseWriter, r *http.Request)

func (*Handler) GetTerraIncognitaCoverage

func (h *Handler) GetTerraIncognitaCoverage(w http.ResponseWriter, r *http.Request)

GetTerraIncognitaCoverage returns 5 concentric GeoJSON ring polygons for gradient fog-of-war rendering and the total explored area in km². Results are served from terra_incognita_cache (keyed by user + heatmap line count + radius) to avoid the ~30 s PostGIS computation on every request.

func (*Handler) GetTrainingZonesAll

func (h *Handler) GetTrainingZonesAll(w http.ResponseWriter, r *http.Request)

GetTrainingZonesAll returns all training zones for the authenticated user, grouped by sport. The sport list is the union of sports that already have stored zones and distinct fit_sport_id values present in the user's activities.

GET /api/v1/training-zones

func (*Handler) GetUnreadCount

func (h *Handler) GetUnreadCount(w http.ResponseWriter, r *http.Request)

GetUnreadCount returns just the unread notification count.

GET /api/v1/notifications/unread-count

func (*Handler) GetUserMapLayers

func (h *Handler) GetUserMapLayers(w http.ResponseWriter, r *http.Request)

GetUserMapLayers returns the authenticated user's custom tile-URL layers.

func (*Handler) GetUserReadinessPreferences

func (h *Handler) GetUserReadinessPreferences(w http.ResponseWriter, r *http.Request)

GetUserReadinessPreferences returns the current preference (preferred_source is null when no manual override is stored).

func (*Handler) GetVfEncodingCoverage

func (h *Handler) GetVfEncodingCoverage(w http.ResponseWriter, r *http.Request)

GetVfEncodingCoverage scans all routing tiles and classifies via_ferrata edges.

GET /api/v1/admin/routing/vf-encoding-coverage

Query parameters:

  • limit=N Scan at most N tiles (default 100, max 1000). When more tiles exist, truncated=true is set in the response.

The handler honours ctx.Done(): it stops scanning mid-way and returns a partial result with truncated=true if the request context is cancelled.

func (*Handler) GetVisitedPOIs

func (h *Handler) GetVisitedPOIs(w http.ResponseWriter, r *http.Request)

GetVisitedPOIs returns GeoJSON FeatureCollection of visited POIs. GET /api/v1/geo/visited-pois

func (*Handler) GetWeatherSettings

func (h *Handler) GetWeatherSettings(w http.ResponseWriter, r *http.Request)

GetWeatherSettings returns the user's weather preferences.

func (*Handler) GetZoneDistribution

func (h *Handler) GetZoneDistribution(w http.ResponseWriter, r *http.Request)

GetZoneDistribution returns the HR and power zone distribution for an activity.

func (*Handler) GroupActivities

func (h *Handler) GroupActivities(w http.ResponseWriter, r *http.Request)

GroupActivities creates a multisport parent activity and links the given activities to it. POST /api/v1/activities/group Body: { "activity_ids": ["uuid1", "uuid2", ...], "title": "Mein Triathlon" }

func (*Handler) HandleAIBrokerChat

func (h *Handler) HandleAIBrokerChat(w http.ResponseWriter, r *http.Request)

HandleAIBrokerChat is an internal ML-service endpoint. It accepts only the requested Skadia role, task type, and message payload; provider URLs and credentials stay inside the API service.

func (*Handler) HandleAIBrokerRoleStatus

func (h *Handler) HandleAIBrokerRoleStatus(w http.ResponseWriter, r *http.Request)

HandleAIBrokerRoleStatus reports internal ML role readiness without leaking provider URLs, model names, or API keys.

func (*Handler) HandleCreateProfile

func (h *Handler) HandleCreateProfile(w http.ResponseWriter, r *http.Request)

HandleCreateProfile creates a new user-owned routing profile. POST /routing/profiles

func (*Handler) HandleDeleteBrouterProfile

func (h *Handler) HandleDeleteBrouterProfile(w http.ResponseWriter, r *http.Request)

HandleDeleteBrouterProfile deletes a .brf profile file from the BRouter profiles directory. DELETE /admin/routing/brouter-profiles/{name}

func (*Handler) HandleDeleteProfile

func (h *Handler) HandleDeleteProfile(w http.ResponseWriter, r *http.Request)

HandleDeleteProfile deletes a user-owned routing profile. DELETE /routing/profiles/{id}

func (*Handler) HandleDocumentProcessedMessage

func (h *Handler) HandleDocumentProcessedMessage(ctx context.Context, data []byte) error

func (*Handler) HandleExportProfile

func (h *Handler) HandleExportProfile(w http.ResponseWriter, r *http.Request)

HandleExportProfile exports a single routing profile as a JSON download. GET /routing/profiles/{id}/export

func (*Handler) HandleGetActivityEvents

func (h *Handler) HandleGetActivityEvents(w http.ResponseWriter, r *http.Request)

HandleGetActivityEvents returns all events for an activity. GET /api/v1/activities/{id}/events

func (*Handler) HandleGetActivityHrData

func (h *Handler) HandleGetActivityHrData(w http.ResponseWriter, r *http.Request)

HandleGetActivityHrData returns high-resolution HR data for an activity. GET /api/v1/activities/{id}/hr-data

func (*Handler) HandleGetActivityJumps

func (h *Handler) HandleGetActivityJumps(w http.ResponseWriter, r *http.Request)

HandleGetActivityJumps returns all jump records for an activity. GET /api/v1/activities/{id}/jumps

func (*Handler) HandleGetActivitySegmentLaps

func (h *Handler) HandleGetActivitySegmentLaps(w http.ResponseWriter, r *http.Request)

HandleGetActivitySegmentLaps returns segment lap data for an activity. GET /api/v1/activities/{id}/segment-laps

func (*Handler) HandleGetActivitySets

func (h *Handler) HandleGetActivitySets(w http.ResponseWriter, r *http.Request)

HandleGetActivitySets returns all exercise sets for an activity. GET /api/v1/activities/{id}/sets

func (*Handler) HandleGetProfileDefaults

func (h *Handler) HandleGetProfileDefaults(w http.ResponseWriter, r *http.Request)

HandleGetProfileDefaults returns the full default ProfileConfig for a profile. GET /api/v1/routing/profiles/{name}

func (*Handler) HandleGetWorkout

func (h *Handler) HandleGetWorkout(w http.ResponseWriter, r *http.Request)

HandleGetWorkout returns a single workout with its steps. GET /api/v1/workouts/{id}

func (*Handler) HandleImportProfiles

func (h *Handler) HandleImportProfiles(w http.ResponseWriter, r *http.Request)

HandleImportProfiles imports one or more profiles from a multipart form with .json files. POST /routing/profiles/import

func (*Handler) HandleListProfiles

func (h *Handler) HandleListProfiles(w http.ResponseWriter, r *http.Request)

HandleListProfiles returns the combined list of system, user/filesystem, and BRouter profiles. GET /routing/profiles

func (*Handler) HandleListWorkouts

func (h *Handler) HandleListWorkouts(w http.ResponseWriter, r *http.Request)

HandleListWorkouts returns all workouts for the authenticated user. GET /api/v1/workouts

func (*Handler) HandleMLCallback

func (h *Handler) HandleMLCallback(w http.ResponseWriter, r *http.Request)

HandleMLCallback handles callbacks from the ML service for remote operation. POST /api/v1/internal/ml-callback Auth: X-ML-Callback-Secret header must match ML_CALLBACK_SECRET env var

func (*Handler) HandleUpdateProfile

func (h *Handler) HandleUpdateProfile(w http.ResponseWriter, r *http.Request)

HandleUpdateProfile updates an existing user-owned routing profile. PUT /routing/profiles/{id}

func (*Handler) HandleUploadBrouterProfile

func (h *Handler) HandleUploadBrouterProfile(w http.ResponseWriter, r *http.Request)

HandleUploadBrouterProfile uploads a .brf profile file to the BRouter profiles directory. POST /admin/routing/brouter-profiles

func (*Handler) Healthz

func (h *Handler) Healthz(w http.ResponseWriter, r *http.Request)

Healthz returns a human-readable status overview. Always 200 — use for dashboards, not probes.

func (*Handler) ImportDEM

func (h *Handler) ImportDEM(w http.ResponseWriter, r *http.Request)

ImportDEM startet einen Background-Job der nach einem manuellen raster2pgsql-Import ANALYZE + VACUUM auf der dem.raster_tiles-Tabelle ausführt und optional einen PostGIS-Raster-Overview erstellt.

POST /api/v1/dem/import

Optional request body (all fields optional):

{
  "source": "eea10"        // Label für Log-Ausgaben
}

Response: 202 Accepted { "job_id": "<uuid>" }

Typischer Workflow:

  1. scripts/dem-import.sh <source> <dir> → lädt GeoTIFFs per raster2pgsql in DB
  2. POST /api/v1/dem/import → ANALYZE + Overview-Raster im Hintergrund

func (*Handler) ImportFromURL

func (h *Handler) ImportFromURL(w http.ResponseWriter, r *http.Request)

ImportFromURL importiert eine Aktivitätsdatei von einer externen URL. POST /api/v1/import/from-url Body: { "url": "https://example.com/activity.fit" }

func (*Handler) ImportLiftsFromOSM

func (h *Handler) ImportLiftsFromOSM(w http.ResponseWriter, r *http.Request)

ImportLiftsFromOSM creates lift segments from osm_lifts. POST /api/v1/segments/import-lifts

func (*Handler) ImportSegment

func (h *Handler) ImportSegment(w http.ResponseWriter, r *http.Request)

ImportSegment handles POST /api/v1/segments/import for exported checkpoint courses.

func (*Handler) IngestHealthData

func (h *Handler) IngestHealthData(w http.ResponseWriter, r *http.Request)

IngestHealthData accepts health data payloads from the mobile app and stores them using the same UPSERT logic as the Garmin importer.

POST /api/v1/import/health-data

func (*Handler) Isochrone

func (h *Handler) Isochrone(w http.ResponseWriter, r *http.Request)

Isochrone computes reachable areas from a start point using UnifiedRouter.FindReachableWithCoords (Dijkstra with cost budget). One concave (alpha-shape) polygon is returned for each requested time band. POIs within the outermost band are optionally returned when poi_types is set.

POST /api/v1/isochrone

func (*Handler) KomootCredentialsStatus

func (h *Handler) KomootCredentialsStatus(w http.ResponseWriter, r *http.Request)

KomootCredentialsStatus checks if stored Komoot credentials exist for the user.

GET /api/v1/import/komoot/credentials

func (*Handler) KomootDeleteCredentials

func (h *Handler) KomootDeleteCredentials(w http.ResponseWriter, r *http.Request)

KomootDeleteCredentials removes stored Komoot credentials.

DELETE /api/v1/import/komoot/credentials

func (*Handler) KomootLogin

func (h *Handler) KomootLogin(w http.ResponseWriter, r *http.Request)

KomootLogin validates Komoot credentials against the API and saves them encrypted.

POST /api/v1/import/komoot/login

func (*Handler) KomootScan

func (h *Handler) KomootScan(w http.ResponseWriter, r *http.Request)

KomootScan re-scans Komoot tours using stored credentials.

POST /api/v1/import/komoot/scan

func (*Handler) KomootStartImport

func (h *Handler) KomootStartImport(w http.ResponseWriter, r *http.Request)

KomootStartImport starts an async Komoot import job.

POST /api/v1/import/komoot/start

func (*Handler) ListActiveJobs

func (h *Handler) ListActiveJobs(w http.ResponseWriter, r *http.Request)

ListActiveJobs gibt laufende Background-Jobs + Anzahl pending Enrichments zurück.

GET /api/v1/jobs/active

func (*Handler) ListActivities

func (h *Handler) ListActivities(w http.ResponseWriter, r *http.Request)

func (*Handler) ListActivityPhotos

func (h *Handler) ListActivityPhotos(w http.ResponseWriter, r *http.Request)

func (*Handler) ListBikeSetups

func (h *Handler) ListBikeSetups(w http.ResponseWriter, r *http.Request)

func (*Handler) ListChatSessions

func (h *Handler) ListChatSessions(w http.ResponseWriter, r *http.Request)

ListChatSessions handles GET /api/v1/chat/sessions

func (*Handler) ListComponentDocuments

func (h *Handler) ListComponentDocuments(w http.ResponseWriter, r *http.Request)

ListComponentDocuments returns all documents for a gear component.

func (*Handler) ListComponentPresets

func (h *Handler) ListComponentPresets(w http.ResponseWriter, r *http.Request)

func (*Handler) ListCustomMapMarkers

func (h *Handler) ListCustomMapMarkers(w http.ResponseWriter, r *http.Request)

func (*Handler) ListCustomMaps

func (h *Handler) ListCustomMaps(w http.ResponseWriter, r *http.Request)

func (*Handler) ListDevices

func (h *Handler) ListDevices(w http.ResponseWriter, r *http.Request)

ListDevices returns all registered device tokens for the authenticated user.

GET /api/v1/devices

func (*Handler) ListGear

func (h *Handler) ListGear(w http.ResponseWriter, r *http.Request)

func (*Handler) ListGearDocuments

func (h *Handler) ListGearDocuments(w http.ResponseWriter, r *http.Request)

func (*Handler) ListGovernanceEffectiveTargets

func (h *Handler) ListGovernanceEffectiveTargets(w http.ResponseWriter, r *http.Request)

func (*Handler) ListGovernanceScopes

func (h *Handler) ListGovernanceScopes(w http.ResponseWriter, r *http.Request)

func (*Handler) ListHardware

func (h *Handler) ListHardware(w http.ResponseWriter, r *http.Request)

ListHardware handles GET /api/v1/hardware?type=recorder|sensor

func (*Handler) ListHardwareActivities

func (h *Handler) ListHardwareActivities(w http.ResponseWriter, r *http.Request)

ListHardwareActivities handles GET /api/v1/hardware/{id}/activities?after=cursor

func (*Handler) ListImportQueue

func (h *Handler) ListImportQueue(w http.ResponseWriter, r *http.Request)

ListImportQueue handles GET /api/v1/import-queue Query param: status=pending|resolved|dismissed|all (default: pending)

func (*Handler) ListMapPhotos

func (h *Handler) ListMapPhotos(w http.ResponseWriter, r *http.Request)

func (*Handler) ListNotifications

func (h *Handler) ListNotifications(w http.ResponseWriter, r *http.Request)

ListNotifications returns recent notifications for the authenticated user.

GET /api/v1/notifications?limit=50

func (*Handler) ListOSMRegions

func (h *Handler) ListOSMRegions(w http.ResponseWriter, r *http.Request)

ListOSMRegions gibt den Regions-Katalog zurück. GET /api/v1/admin/osm-regions

func (*Handler) ListObjectAccessScopes

func (h *Handler) ListObjectAccessScopes(w http.ResponseWriter, r *http.Request)

func (*Handler) ListPendingReviewGear

func (h *Handler) ListPendingReviewGear(w http.ResponseWriter, r *http.Request)

ListPendingReviewGear returns gear items awaiting user review.

func (*Handler) ListPhotoFeed

func (h *Handler) ListPhotoFeed(w http.ResponseWriter, r *http.Request)

ListPhotoFeed — GET /api/v1/photos?sort=date|added&page=1

func (*Handler) ListPrivacyZones

func (h *Handler) ListPrivacyZones(w http.ResponseWriter, r *http.Request)

func (*Handler) ListRoutePublications

func (h *Handler) ListRoutePublications(w http.ResponseWriter, r *http.Request)

func (*Handler) ListRoutes

func (h *Handler) ListRoutes(w http.ResponseWriter, r *http.Request)

ListRoutes returns paginated planned routes for the authenticated user. GET /api/v1/routes

func (*Handler) ListRules

func (h *Handler) ListRules(w http.ResponseWriter, r *http.Request)

func (*Handler) ListSegmentVersionRevisions

func (h *Handler) ListSegmentVersionRevisions(w http.ResponseWriter, r *http.Request)

func (*Handler) ListSegments

func (h *Handler) ListSegments(w http.ResponseWriter, r *http.Request)

func (*Handler) ListServiceEvents

func (h *Handler) ListServiceEvents(w http.ResponseWriter, r *http.Request)

func (*Handler) ListServiceIntervals

func (h *Handler) ListServiceIntervals(w http.ResponseWriter, r *http.Request)

func (*Handler) ListTags

func (h *Handler) ListTags(w http.ResponseWriter, r *http.Request)

ListTags returns all tags for the authenticated user.

func (*Handler) ListTargetCollections

func (h *Handler) ListTargetCollections(w http.ResponseWriter, r *http.Request)

func (*Handler) ListTargetObservations

func (h *Handler) ListTargetObservations(w http.ResponseWriter, r *http.Request)

func (*Handler) ListTargetPlaces

func (h *Handler) ListTargetPlaces(w http.ResponseWriter, r *http.Request)

func (*Handler) ListTargetRegions

func (h *Handler) ListTargetRegions(w http.ResponseWriter, r *http.Request)

func (*Handler) ListTargetReports

func (h *Handler) ListTargetReports(w http.ResponseWriter, r *http.Request)

func (*Handler) ListTargetUserContent

func (h *Handler) ListTargetUserContent(w http.ResponseWriter, r *http.Request)

func (*Handler) Livez

func (h *Handler) Livez(w http.ResponseWriter, r *http.Request)

Livez returns 200 if the process is alive. Does NOT check external dependencies. Used as K8s livenessProbe — checking DB here would cause restart cascades.

func (*Handler) MarkAllNotificationsRead

func (h *Handler) MarkAllNotificationsRead(w http.ResponseWriter, r *http.Request)

MarkAllNotificationsRead marks all notifications as read for the authenticated user.

POST /api/v1/notifications/read-all

func (*Handler) MarkNotificationRead

func (h *Handler) MarkNotificationRead(w http.ResponseWriter, r *http.Request)

MarkNotificationRead marks a single notification as read.

POST /api/v1/notifications/{id}/read

func (*Handler) MaybeTriggerFusionQueueCompute

func (h *Handler) MaybeTriggerFusionQueueCompute(ctx context.Context, userID string)

MaybeTriggerFusionQueueCompute startet einen Fusion-Queue-Recompute im Hintergrund, wenn der Cache stale ist und kein Job läuft. Wird nach Imports und vom GET-Endpoint aufgerufen.

func (*Handler) MergeGear

func (h *Handler) MergeGear(w http.ResponseWriter, r *http.Request)

MergeGear godoc POST /api/v1/gear/{id}/merge Body: {"merge_from_id": "uuid"}

func (*Handler) MergeHardware

func (h *Handler) MergeHardware(w http.ResponseWriter, r *http.Request)

MergeHardware handles POST /api/v1/hardware/merge

func (*Handler) MergeTag

func (h *Handler) MergeTag(w http.ResponseWriter, r *http.Request)

MergeTag merges a source tag into a target tag. POST /api/v1/tags/{id}/merge body: { "source_tag_id": "uuid" } The tag identified by {id} survives; source_tag_id is deleted.

func (*Handler) PatchActivityPhotoLocation

func (h *Handler) PatchActivityPhotoLocation(w http.ResponseWriter, r *http.Request)

PatchActivityPhotoLocation handles PATCH /api/v1/activities/{id}/photos/{photoID}. Updates the GPS position and/or timestamp of a photo. When location_source is 'manual' a meta-sidecar JSON is written to S3 so the original file remains untouched.

func (*Handler) PatchGearReview

func (h *Handler) PatchGearReview(w http.ResponseWriter, r *http.Request)

PatchGearReview updates the review status of a gear item.

func (*Handler) PatchHardware

func (h *Handler) PatchHardware(w http.ResponseWriter, r *http.Request)

PatchHardware handles PATCH /api/v1/hardware/{id}

func (*Handler) PatchImportQueueItem

func (h *Handler) PatchImportQueueItem(w http.ResponseWriter, r *http.Request)

PatchImportQueueItem handles PATCH /api/v1/import-queue/{id} Body: {"status": "resolved"|"dismissed"|"pending"}

func (*Handler) PatchLLMConfigEvalWaiver

func (h *Handler) PatchLLMConfigEvalWaiver(w http.ResponseWriter, r *http.Request)

PatchLLMConfigEvalWaiver records an explicit admin waiver for a role eval gate.

func (*Handler) PatchLLMUserConfig

func (h *Handler) PatchLLMUserConfig(w http.ResponseWriter, r *http.Request)

PatchLLMUserConfig saves the model override for a user.

func (*Handler) PatchMapLayer

func (h *Handler) PatchMapLayer(w http.ResponseWriter, r *http.Request)

PatchMapLayer — admin only. Honors X-If-Match-Updated-At for concurrent-edit detection. For url_template / style_url changes the header is REQUIRED (428 otherwise).

func (*Handler) PatchProfile

func (h *Handler) PatchProfile(w http.ResponseWriter, r *http.Request)

PatchProfile updates the user's physiological profile.

func (*Handler) PatchSplit

func (h *Handler) PatchSplit(w http.ResponseWriter, r *http.Request)

PatchSplit updates mutable split fields (currently only climbing_grade). PATCH /api/v1/activities/{id}/splits/{splitId}

func (*Handler) PatchSystemLayerVisibility

func (h *Handler) PatchSystemLayerVisibility(w http.ResponseWriter, r *http.Request)

PatchSystemLayerVisibility — admin only. PATCH /admin/system-layer-visibility/{key} Accepted fields: enabled, default_enabled_for_new_users, default_opacity, label_override. Unknown fields → 400. Unknown key → 404.

func (*Handler) PatchUserMapLayer

func (h *Handler) PatchUserMapLayer(w http.ResponseWriter, r *http.Request)

PatchUserMapLayer patches a layer owned by the authenticated user. User isolation is enforced at the DB layer via WHERE user_id=$1 AND id=$2.

func (*Handler) PostAdminRenameBulk

func (h *Handler) PostAdminRenameBulk(w http.ResponseWriter, r *http.Request)

PostAdminRenameBulk kicks off a rename for all active users for one fileType.

Design (v2):

  • Cluster-wide serialization via pg_try_advisory_lock on a constant key — avoids requiring a new background_jobs row with NULL user_id (column is NOT NULL) and avoids 23505 races on a dedicated unique index.
  • Batch-JOIN against users ⟕ user_file_schemas to pre-resolve ALL user snapshots in one round-trip (scales to 100k users).
  • Per-user shadowfs_rename rows are inserted iteratively in the background goroutine. 23505 from idx_bg_jobs_one_running = user has a running per-user job already (user-triggered or from a previous bulk); skip — do not block the rest of the bulk run.

POST /api/v1/admin/rename-execute-bulk Body: {"file_type": "activity"}

func (*Handler) PostAdminRenameUser

func (h *Handler) PostAdminRenameUser(w http.ResponseWriter, r *http.Request)

PostAdminRenameUser triggers a per-user+type rename executed in the background. The foreground resolves schema + library-dir snapshots so that admin mid-job changes do not affect an already-started rename job. Relies on the unique partial index idx_bg_jobs_one_running (user_id, job_type) WHERE status='running' — duplicate INSERT returns 23505 → 200 + rename_in_progress. POST /api/v1/admin/users/{id}/files/{type}/rename-execute

func (*Handler) PostLLMConfigEval

func (h *Handler) PostLLMConfigEval(w http.ResponseWriter, r *http.Request)

func (*Handler) PreviewFileRename

func (h *Handler) PreviewFileRename(w http.ResponseWriter, r *http.Request)

PreviewFileRename computes which active files would be moved when the user's schema template for a given type is changed. POST /api/v1/settings/file-schemas/{type}/rename-preview

func (*Handler) PreviewRename

func (h *Handler) PreviewRename(w http.ResponseWriter, r *http.Request)

PreviewRename is a deleted legacy handler. File renaming is now managed entirely through the FileStore/ShadowFS layer.

func (*Handler) ProxyMartinTile

func (h *Handler) ProxyMartinTile(w http.ResponseWriter, r *http.Request)

ProxyMartinTile reverse-proxies tile requests to the internal Martin tile server. It enforces per-user isolation by always overriding the user_id query parameter with the authenticated user's ID for every proxied source.

GET /api/v1/tiles/*

func (*Handler) PublishRouteCourse

func (h *Handler) PublishRouteCourse(w http.ResponseWriter, r *http.Request)

func (*Handler) PublishSegment

func (h *Handler) PublishSegment(w http.ResponseWriter, r *http.Request)

PublishSegment transitions a non-public segment to public and queues durable recalculation activities so all users' matching activities are processed.

func (*Handler) PutActivityEdits

func (h *Handler) PutActivityEdits(w http.ResponseWriter, r *http.Request)

PutActivityEdits saves the edit sidecar for an activity to S3.

PUT /api/v1/activities/{id}/edits

func (*Handler) PutChatPreferences

func (h *Handler) PutChatPreferences(w http.ResponseWriter, r *http.Request)

PutChatPreferences handles PUT /api/v1/chat/preferences.

func (*Handler) PutLLMConfigRole

func (h *Handler) PutLLMConfigRole(w http.ResponseWriter, r *http.Request)

PutLLMConfigRole saves the configuration for a model role.

func (*Handler) PutLiftDetectionConfig

func (h *Handler) PutLiftDetectionConfig(w http.ResponseWriter, r *http.Request)

PutLiftDetectionConfig handles PUT /api/v1/activities/{id}/lifts/detection-config. Persists per-activity override values. Partial body accepted: omit a key to leave its column unchanged (COALESCE in DB layer). Does not trigger re-enrichment.

func (*Handler) PutMLSystemSetting

func (h *Handler) PutMLSystemSetting(w http.ResponseWriter, r *http.Request)

PutMLSystemSetting updates a single ML system setting.

PUT /api/v1/settings/ml/{key}

func (*Handler) PutMLUserSetting

func (h *Handler) PutMLUserSetting(w http.ResponseWriter, r *http.Request)

PutMLUserSetting upserts a single ML user setting.

PUT /api/v1/settings/ml/user/{key}

func (*Handler) PutUserReadinessPreferences

func (h *Handler) PutUserReadinessPreferences(w http.ResponseWriter, r *http.Request)

PutUserReadinessPreferences stores a preference and triggers analytics invalidation. The publisher is best-effort here: even if invalidation fails, the write succeeded and the user gets a 200 confirming their choice.

func (*Handler) QueryCoverage

func (h *Handler) QueryCoverage(w http.ResponseWriter, r *http.Request)

func (*Handler) QueryElevation

func (h *Handler) QueryElevation(w http.ResponseWriter, r *http.Request)

QueryElevation returns DEM elevations for a list of [lng, lat] coordinate pairs.

POST /api/v1/elevation/query

{
  "points": [[lng, lat], ...]
}

Response:

{
  "elevations": [float64, ...],
  "source":     "srtm30" | "copernicus30" | "none" | ...
}

func (*Handler) ReEnrichActivities

func (h *Handler) ReEnrichActivities(w http.ResponseWriter, r *http.Request)

ReEnrichActivities re-runs selected enrichment modules for the user's activities without downloading or re-parsing original files from S3.

POST /api/v1/activities/re-enrich

func (*Handler) Readyz

func (h *Handler) Readyz(w http.ResponseWriter, r *http.Request)

Readyz returns 200 if the service can handle traffic, 503 otherwise. Checks DB connectivity and NATS (if configured).

func (*Handler) RebuildLandmarks

func (h *Handler) RebuildLandmarks(w http.ResponseWriter, r *http.Request)

RebuildLandmarks runs ONLY the landmark preprocessing on existing tiles. Does NOT rebuild tiles. Use when tiles exist but landmarks need recomputing. POST /admin/routing/rebuild-landmarks

func (*Handler) RebuildRoutingTiles

func (h *Handler) RebuildRoutingTiles(w http.ResponseWriter, r *http.Request)

RebuildRoutingTiles triggers a background tile build from PostGIS data. POST /admin/routing/rebuild-tiles

func (*Handler) RecalcAllUsersBestEfforts

func (h *Handler) RecalcAllUsersBestEfforts(w http.ResponseWriter, r *http.Request)

RecalcAllUsersBestEfforts starts a background job that recomputes best efforts for every user. Uses a fixed "system" user ID as job guard so only one run can be active at a time regardless of which admin triggers it.

POST /api/v1/admin/best-efforts/recalc-all

func (*Handler) RecalcBestEfforts

func (h *Handler) RecalcBestEfforts(w http.ResponseWriter, r *http.Request)

RecalcBestEfforts starts a background job that deletes all best effort records for the user and recomputes them from scratch in chronological order. Uses InsertBackgroundJobGuarded to prevent duplicate runs.

POST /api/v1/best-efforts/recalc

func (*Handler) RecalculateActivityLifts

func (h *Handler) RecalculateActivityLifts(w http.ResponseWriter, r *http.Request)

RecalculateActivityLifts handles POST /api/v1/activities/{id}/lifts/recalculate-all.

Hard-resets lift detection state for one activity (User-Quote 2026-05-08 #5): DELETE all candidates (including rejected), DELETE coupled lift segment_efforts, clear lifts_acknowledged_at, then trigger re-enrichment from a pre-created background job (F2 fix: async dispatch prevents client-disconnect from leaving the activity in a half-reset state and avoids blocking long enrichment runs).

F25 fix: non-lift-eligible sports (running, cycling indoor, etc.) are rejected with 400 NOT_LIFT_ELIGIBLE before any DELETE — the reset would be a no-op anyway, and returning early avoids unnecessary DB churn.

The post-commit re-enrichment respects user_enrichment_settings.enable_lift_detection — a user who has lift detection disabled gets the reset only, no re-detection.

Disjoint from the user-wide POST /api/v1/lifts/recalculate-all (RecalculateLifts) which only clears the rejected_by_user flag across all of the user's activities and does not delete or re-enrich.

Phase 6.3.1.2 bullet 4.

func (*Handler) RecalculateLifts

func (h *Handler) RecalculateLifts(w http.ResponseWriter, r *http.Request)

RecalculateLifts handles POST /api/v1/lifts/recalculate-all.

Clears the rejected_by_user flag on every lift candidate that belongs to the authenticated user so that candidates hidden by a previous soft-reject become visible again on the next enrichment pass.

This endpoint only resets the soft-reject flag; it does not trigger re-enrichment or recompute aggregate statistics. Lift aggregates are refreshed on the next regular enrichment pass for each affected activity.

Response on success:

200 {"cleared_count": N, "affected_activities": M}

Returns 204 when no rows were in rejected state.

func (*Handler) RecomputeActivity

func (h *Handler) RecomputeActivity(w http.ResponseWriter, r *http.Request)

RecomputeActivity re-downloads the original file from S3, parses it again, and replaces all derived data (streams, track, sensors, metrics). User-defined fields (title, description, tags, visibility) are preserved.

POST /api/v1/activities/{id}/recompute

func (*Handler) RecomputeAllActivities

func (h *Handler) RecomputeAllActivities(w http.ResponseWriter, r *http.Request)

RecomputeAllActivities starts an async background job that reprocesses all activities for the authenticated user. Returns immediately with 202 Accepted.

POST /api/v1/activities/recompute-all

func (*Handler) RecomputeAllTSS

func (h *Handler) RecomputeAllTSS(w http.ResponseWriter, r *http.Request)

RecomputeAllTSS starts an async background job that recomputes TSS for all activities of the authenticated user, then recalculates the PMC. Returns 202 Accepted with {"status":"started","job_id":"..."} immediately, or 200 OK with {"status":"already_running"} when a job is already in progress.

POST /api/v1/admin/recompute-tss

func (*Handler) RecomputeEffortsForActivity

func (h *Handler) RecomputeEffortsForActivity(ctx context.Context, activityID, userID string) error

RecomputeEffortsForActivity re-matches a single activity against all spatially overlapping segments in one pass. Used by postCommitFusionCleanup after a fusion-merge.

Complexity:

Previously O(N²): collectSegmentEfforts was called once per overlapping
segment (N calls, each loading the full GPS stream and running the spatial
pre-filter).

Now O(N): collectSegmentEfforts is called exactly once for the activity.
The returned efforts are grouped by segment_id in-memory, then each
segment gets a dedicated transactional DELETE + batch-INSERT of only its
slice of efforts.

Table: activity_tracks.bbox (GEOMETRY Polygon 4326, Mig 000004). segment_versions.track (GEOMETRY LineString 4326, Mig 000032 GIST-indexed).

Per-segment failures are logged as warnings and do not abort the loop — partial coverage is acceptable since the user can trigger a manual recompute.

func (*Handler) RecomputePhotoEmbeddings

func (h *Handler) RecomputePhotoEmbeddings(w http.ResponseWriter, r *http.Request)

RecomputePhotoEmbeddings re-queues CLIP embedding computation for all user photos that currently have no embedding stored (clip_embedding IS NULL). Runs as a background job; returns 202 Accepted immediately.

func (*Handler) RecomputePhotoPreviews

func (h *Handler) RecomputePhotoPreviews(w http.ResponseWriter, r *http.Request)

RecomputePhotoPreviews regenerates preview images for all user photos via ShadowFS. For each photo without a preview in user_files, the original is downloaded, resized, and stored as file_type='preview' with parent_type='photo'.

func (*Handler) RecomputePhotoThumbnails

func (h *Handler) RecomputePhotoThumbnails(w http.ResponseWriter, r *http.Request)

RecomputePhotoThumbnails regenerates thumbnail images for all user photos via ShadowFS. For each photo without a thumb in user_files, the original is downloaded, resized to 200px, and stored as file_type='thumb' with parent_type='photo'.

func (*Handler) RecomputeSegment

func (h *Handler) RecomputeSegment(w http.ResponseWriter, r *http.Request)

RecomputeSegment queues durable recalculation activities for the caller's GPS activities against a single segment.

func (*Handler) RecomputeStatus

func (h *Handler) RecomputeStatus(w http.ResponseWriter, r *http.Request)

RecomputeStatus returns the current state of the async recompute-all job. NOTE: The state is global (process-wide) — not scoped per user. In a single-admin deployment this is intentional. Multi-admin setups would need per-user job state, tracked via background_jobs table instead.

GET /api/v1/activities/recompute-status

func (*Handler) RegisterDevice

func (h *Handler) RegisterDevice(w http.ResponseWriter, r *http.Request)

RegisterDevice registers or updates an FCM/APNs device token.

POST /api/v1/devices/register

func (*Handler) RejectAllLiftCandidates

func (h *Handler) RejectAllLiftCandidates(w http.ResponseWriter, r *http.Request)

RejectAllLiftCandidates handles POST /api/v1/activities/{id}/lifts/reject-all.

Bulk-soft-rejects every unconfirmed, non-rejected lift candidate of the activity, sets lifts_acknowledged_at to NOW(), and recomputes aggregates atomically. Idempotent.

Phase 6.3.1.2 bullet 2.

func (*Handler) RejectLiftCandidate

func (h *Handler) RejectLiftCandidate(w http.ResponseWriter, r *http.Request)

RejectLiftCandidate deletes a lift candidate and recomputes activity lift stats. DELETE /api/v1/activities/{id}/lift-candidates/{cid}

func (*Handler) RemoveActivityGearAssignment

func (h *Handler) RemoveActivityGearAssignment(w http.ResponseWriter, r *http.Request)

func (*Handler) RemoveBestEffortExclusion

func (h *Handler) RemoveBestEffortExclusion(w http.ResponseWriter, r *http.Request)

RemoveBestEffortExclusion removes an exclusion so the effort is ranked again.

DELETE /api/v1/best-effort-exclusions/{activityID}/{category}

func (*Handler) RemoveComponent

func (h *Handler) RemoveComponent(w http.ResponseWriter, r *http.Request)

func (*Handler) RemoveRouteTag

func (h *Handler) RemoveRouteTag(w http.ResponseWriter, r *http.Request)

RemoveRouteTag removes a single tag from a route. DELETE /api/v1/routes/{id}/tags/{tagId}

func (*Handler) RemoveSegmentFavorite

func (h *Handler) RemoveSegmentFavorite(w http.ResponseWriter, r *http.Request)

RemoveSegmentFavorite removes a segment from the user's favorites.

func (*Handler) ReorderRoutePhotos

func (h *Handler) ReorderRoutePhotos(w http.ResponseWriter, r *http.Request)

ReorderRoutePhotos handles PUT /api/v1/routes/{id}/photos/reorder

func (*Handler) ResetActivityDistance

func (h *Handler) ResetActivityDistance(w http.ResponseWriter, r *http.Request)

ResetActivityDistance restores the original parsed distance by re-parsing the source file (streams + metrics scope). This undoes any distance correction and reverts to the device-recorded or parser-calculated distance.

POST /api/v1/activities/{id}/reset-distance

Response: 202 Accepted { "job_id": "<uuid>", "status": "started" }

func (*Handler) ResetActivityElevation

func (h *Handler) ResetActivityElevation(w http.ResponseWriter, r *http.Request)

ResetActivityElevation restores the GPS-recorded altitude values for an activity by re-parsing the original file (streams + metrics scope) and then explicitly setting elevation_source = 'gps'. Runs as a background job.

POST /api/v1/activities/{id}/reset-elevation

Response: 202 Accepted { "job_id": "<uuid>", "status": "started" }

func (*Handler) ResolvePDFQueueItem

func (h *Handler) ResolvePDFQueueItem(w http.ResponseWriter, r *http.Request)

ResolvePDFQueueItem handles POST /api/v1/import-queue/{id}/resolve-pdf. It turns a queued PDF into a gear document and resolves the linked import log.

func (*Handler) ResolveSportConflict

func (h *Handler) ResolveSportConflict(w http.ResponseWriter, r *http.Request)

ResolveSportConflict handles PATCH /api/v1/activities/{id}/resolve-sport-conflict

func (*Handler) RestoreLiftCandidate

func (h *Handler) RestoreLiftCandidate(w http.ResponseWriter, r *http.Request)

RestoreLiftCandidate handles POST /api/v1/lift-candidates/{id}/restore.

Clears the rejected_by_user flag on a soft-rejected candidate and returns it to the review queue with confirmed=false.

Owner check is enforced inside the DB layer via a JOIN to activities. 0-row UPDATE (wrong owner, already-non-rejected, missing row) → 404.

regression case. Phase 6.3.1.2 bullet 5.

func (*Handler) ResumeInterruptedJobs

func (h *Handler) ResumeInterruptedJobs(ctx context.Context)

ResumeInterruptedJobs inspects all background_jobs with status='running' (leftovers from the previous server process) and either resumes or cancels them. Must be called after the Handler is fully initialised.

func (*Handler) RetryOSMEnrichment

func (h *Handler) RetryOSMEnrichment(w http.ResponseWriter, r *http.Request)

RetryOSMEnrichment re-runs only the enrichment + splitting phases (6+7) on existing osm_ways. POST /api/v1/admin/osm-import/retry-enrichment

func (*Handler) ReverseSegment

func (h *Handler) ReverseSegment(w http.ResponseWriter, r *http.Request)

ReverseSegment creates a reversed active segment version atomically. Existing efforts remain frozen on the version and direction that originally matched. Owner OR admin may reverse.

POST /api/v1/segments/{id}/reverse → 200 {"efforts_updated": 0} | 400 | 403 | 404 | 500

func (*Handler) RevertFusion

func (h *Handler) RevertFusion(w http.ResponseWriter, r *http.Request)

RevertFusion löscht eine Fusion-Ergebnis-Aktivität und stellt die Quellen wieder her. POST /api/v1/activities/{id}/revert-fusion

func (*Handler) RollbackSegmentVersionRevision

func (h *Handler) RollbackSegmentVersionRevision(w http.ResponseWriter, r *http.Request)

func (*Handler) RouteCalculate

func (h *Handler) RouteCalculate(w http.ResponseWriter, r *http.Request)

RouteCalculate computes a route using the UnifiedRouter (TiledGraphReader). Returns 503 if no routing engine is available.

func (*Handler) RouteDraftFromActivity

func (h *Handler) RouteDraftFromActivity(w http.ResponseWriter, r *http.Request)

RouteDraftFromActivity returns a planned-route payload derived from an activity's GPS track without saving it to the database. GET /api/v1/routes/from-activity/{activityId}/draft

func (*Handler) RouteFromActivity

func (h *Handler) RouteFromActivity(w http.ResponseWriter, r *http.Request)

RouteFromActivity creates a new planned route from an existing activity's GPS track. POST /api/v1/routes/from-activity/{activityId}

func (*Handler) RouteFromActivityWaypoints

func (h *Handler) RouteFromActivityWaypoints(w http.ResponseWriter, r *http.Request)

RouteFromActivityWaypoints returns waypoints derived from an activity's GPS track WITHOUT saving a route to the database. Used by "Open in Route Planner" button. GET /api/v1/routes/from-activity/{activityId}/waypoints

func (*Handler) RouteViabrouter

func (h *Handler) RouteViabrouter(w http.ResponseWriter, r *http.Request)

RouteViabrouter proxies a routing request to the BRouter service and returns a GeoJSON FeatureCollection. If BRouter is unavailable or returns an error, a structured JSON error with a "status" field is returned.

POST /api/v1/routing/route

func (*Handler) RoutingGraphStatus

func (h *Handler) RoutingGraphStatus(w http.ResponseWriter, r *http.Request)

RoutingGraphStatus returns the current routing status based on tile availability.

func (*Handler) ScanGarminExport

func (h *Handler) ScanGarminExport(w http.ResponseWriter, r *http.Request)

ScanGarminExport analyses a Garmin export directory without writing to the DB. For each import module it returns how many items were found, how many already exist in the database, and how many would be new imports.

POST /api/v1/import/garmin/scan Body: {"source_path": "/path/to/garmin/export"}

func (*Handler) ScanStravaExport

func (h *Handler) ScanStravaExport(w http.ResponseWriter, r *http.Request)

ScanStravaExport analyses a Strava export directory without writing to the DB. For each import module it returns how many items were found, how many already exist in the database, and how many would be new imports.

POST /api/v1/import/strava/scan Body: {"source_path": "/path/to/strava/export"}

func (*Handler) SearchAddress

func (h *Handler) SearchAddress(w http.ResponseWriter, r *http.Request)

SearchAddress searches imported map data and reports optional external lookup status. GET /api/v1/geo/search?q=...&type=street|place|all&limit=20&lat=1.2&lng=3.4&external=1

func (*Handler) SearchPOIs

func (h *Handler) SearchPOIs(w http.ResponseWriter, r *http.Request)

SearchPOIs handles GET /api/v1/search/pois?q=...&types=...&limit=... Searches peaks and amenities using pg_trgm similarity matching.

func (*Handler) SearchPhotos

func (h *Handler) SearchPhotos(w http.ResponseWriter, r *http.Request)

SearchPhotos — GET /api/v1/photos/search?q=text&limit=20

func (*Handler) SeedDefaultTags

func (h *Handler) SeedDefaultTags(w http.ResponseWriter, r *http.Request)

SeedDefaultTags creates the default autotag definitions if they don't exist.

Phase 4b.A.3: the system tags + automation rules (Rundtour, E-Bike, Pendeln, Indoor, virtuell, Frost, Nacht) are seeded by automation.EnsureSystemArtifacts — call ordered FIRST so the rule INSERTs can resolve their tag IDs. SeedSystemDefaults still handles weather tags + recommended rules + the migrate-existing step.

func (*Handler) SendChatMessage

func (h *Handler) SendChatMessage(w http.ResponseWriter, r *http.Request)

SendChatMessage handles POST /api/v1/chat/sessions/{id}/send It persists the user message, streams the ML response via SSE, and persists the assistant reply when done.

func (*Handler) ServeFile

func (h *Handler) ServeFile(w http.ResponseWriter, r *http.Request)

ServeFile serves a file from the ShadowFS. GET /api/v1/files/{fileID}

Unlike serveResolvedFile (used for photos/media), this handler intentionally proxies sensitive application/* types (FIT, GPX, PDF) server-side so the presigned S3 URL is never exposed to the browser.

func (*Handler) ServeMedia

func (h *Handler) ServeMedia(w http.ResponseWriter, r *http.Request)

ServeMedia proxies authenticated media requests to storage. GET /api/v1/media/{bucket}/{key...} Used for tag cover images, custom map marker images, and other user-uploaded media. Prefers ShadowFS (user_files lookup) and falls back to direct S3 for pre-migration files. Access is restricted to skadia-media bucket with key-prefix ownership validation.

func (*Handler) ServeTagCover

func (h *Handler) ServeTagCover(w http.ResponseWriter, r *http.Request)

ServeTagCover serves the tag cover image (thumbnail preferred, original fallback). GET /api/v1/tags/{id}/cover

func (*Handler) SetCacheBudget

func (h *Handler) SetCacheBudget(w http.ResponseWriter, r *http.Request)

SetCacheBudget adjusts the tile cache budget at runtime. PUT /admin/routing/cache-budget Admin-Berechtigung wird durch JWT-Middleware sichergestellt.

func (*Handler) SetOSMSchedule

func (h *Handler) SetOSMSchedule(w http.ResponseWriter, r *http.Request)

SetOSMSchedule configures the automatic update schedule for all imported regions. POST /api/v1/admin/osm-schedule Body: { "interval": "weekly"|"monthly"|"manual", "weekday": 1, "day": 1, "hour": 3 }

func (*Handler) SetRecordingGroupPrimary

func (h *Handler) SetRecordingGroupPrimary(w http.ResponseWriter, r *http.Request)

SetRecordingGroupPrimary godoc PATCH /api/v1/recording-groups/{id}/primary Body: {"activity_id": "uuid"}

func (*Handler) SetRecordingGroupSportType

func (h *Handler) SetRecordingGroupSportType(w http.ResponseWriter, r *http.Request)

SetRecordingGroupSportType godoc PATCH /api/v1/recording-groups/{id}/sport-type Body: {"sport_type": "cycling", "sub_type": "mountain", "is_electric": true}

func (*Handler) SetRouteTags

func (h *Handler) SetRouteTags(w http.ResponseWriter, r *http.Request)

SetRouteTags replaces all tags on a route with the provided set (PUT semantics). PUT /api/v1/routes/{id}/tags Body: {"tag_ids": ["uuid1", "uuid2"]}

func (*Handler) SplitActivity

func (h *Handler) SplitActivity(w http.ResponseWriter, r *http.Request)

SplitActivity splits an activity at a given timestamp. A new activity record is created for the second part; both activities share streams (the split boundary is stored in edit metadata).

POST /api/v1/activities/{id}/split Body: { "split_at": "RFC3339" }

func (*Handler) StartDeviceOrphanCleanupScheduler

func (h *Handler) StartDeviceOrphanCleanupScheduler(ctx context.Context)

StartDeviceOrphanCleanupScheduler launches a background goroutine that runs a one-time cleanup of devices that are no longer referenced by any activity or activity sensor. The cleanup runs once, 1 hour after startup. Devices only become orphaned when activities are deleted, which is rare, so a periodic ticker is not needed. Call once from main after the handler is initialised.

func (*Handler) StartGarminImport

func (h *Handler) StartGarminImport(w http.ResponseWriter, r *http.Request)

StartGarminImport starts an async Garmin export import job.

POST /api/v1/import/garmin/start Body: {"source_path": "/path/to/garmin/export", "modules": ["activities", "profile"]}

func (*Handler) StartOSMImport

func (h *Handler) StartOSMImport(w http.ResponseWriter, r *http.Request)

StartOSMImport startet einen asynchronen OSM-Import-Job. POST /api/v1/admin/osm-import/start

func (*Handler) StartOSMUpdateScheduler

func (h *Handler) StartOSMUpdateScheduler(ctx context.Context)

StartOSMUpdateScheduler launches a background goroutine that checks every hour whether any imported region has an automatic update due and triggers it. Call once from main after the handler is initialised.

func (*Handler) StartPhashBackfill

func (h *Handler) StartPhashBackfill(w http.ResponseWriter, r *http.Request)

StartPhashBackfill computes pHash for all photos of the authenticated user that currently have no pHash stored.

POST /api/v1/admin/phash-backfill

func (*Handler) StartReconciliation

func (h *Handler) StartReconciliation(w http.ResponseWriter, r *http.Request)

StartReconciliation triggers a one-off shadowfs reconciliation job that checks consistency between the filesystem and the user_files registry.

POST /api/v1/admin/storage/reconcile

func (*Handler) StartStravaImport

func (h *Handler) StartStravaImport(w http.ResponseWriter, r *http.Request)

StartStravaImport starts an async Strava export import job.

POST /api/v1/import/strava/start Body: {"source_path": "/path/to/strava/export", "modules": ["activities", "profile"]}

func (*Handler) StorageDashboard

func (h *Handler) StorageDashboard(w http.ResponseWriter, r *http.Request)

StorageDashboard returns an overview of the unified ShadowFS state.

GET /api/v1/admin/storage

func (*Handler) StreamActivityPhotoFull

func (h *Handler) StreamActivityPhotoFull(w http.ResponseWriter, r *http.Request)

func (*Handler) StreamActivityPhotoPreview

func (h *Handler) StreamActivityPhotoPreview(w http.ResponseWriter, r *http.Request)

func (*Handler) StreamActivityPhotoThumb

func (h *Handler) StreamActivityPhotoThumb(w http.ResponseWriter, r *http.Request)

func (*Handler) StreamJobProgress

func (h *Handler) StreamJobProgress(w http.ResponseWriter, r *http.Request)

StreamJobProgress streams enrichment progress via Server-Sent Events.

GET /api/v1/jobs/stream

func (*Handler) StreamRoutePhotoFull

func (h *Handler) StreamRoutePhotoFull(w http.ResponseWriter, r *http.Request)

func (*Handler) StreamRoutePhotoPreview

func (h *Handler) StreamRoutePhotoPreview(w http.ResponseWriter, r *http.Request)

func (*Handler) StreamRoutePhotoThumb

func (h *Handler) StreamRoutePhotoThumb(w http.ResponseWriter, r *http.Request)

func (*Handler) SuggestRoutes

func (h *Handler) SuggestRoutes(w http.ResponseWriter, r *http.Request)

SuggestRoutes generates round-trip route suggestions starting from the given point. The algorithm:

  1. Build a CostConfig for the requested BadRouter profile.
  2. Run Pareto FindReachableWithCoords to discover nodes reachable within half the target distance budget (outward leg).
  3. Filter by distance band and select candidates via BuildCandidatePool + PickDiverseIndices.
  4. For each variant, build a round-trip with quality gate in parallel.
  5. Classify loop quality, compute overlap, and return enriched suggestions.

If the tiled graph is not loaded, the endpoint returns 503.

POST /api/v1/routes/suggest

func (*Handler) SuggestRoutesForUser

func (h *Handler) SuggestRoutesForUser(ctx context.Context, userID string, req RouteSuggestionRequest) (RouteSuggestionResponse, error)

SuggestRoutesForUser runs the route suggestion engine without an HTTP request. It is used by the HTTP handler and by internal callers such as agent tools.

func (*Handler) SwapChain

func (h *Handler) SwapChain(w http.ResponseWriter, r *http.Request)

func (*Handler) SwapComponent

func (h *Handler) SwapComponent(w http.ResponseWriter, r *http.Request)

SwapComponent atomically retires an existing component and assigns a new one, recording a swap event in gear_component_swap_events.

func (*Handler) SyncTagLocation

func (h *Handler) SyncTagLocation(w http.ResponseWriter, r *http.Request)

SyncTagLocation sets location_lat/location_lng on all activities linked to this tag, using the tag's own lat/lng coordinates. Only acts when the tag has a location set. POST /api/v1/tags/{id}/sync-location

func (*Handler) TestLLMConnection

func (h *Handler) TestLLMConnection(w http.ResponseWriter, r *http.Request)

TestLLMConnection tests connectivity to an LLM backend.

func (*Handler) TransferComponent

func (h *Handler) TransferComponent(w http.ResponseWriter, r *http.Request)

func (*Handler) TriggerOSMRegionUpdate

func (h *Handler) TriggerOSMRegionUpdate(w http.ResponseWriter, r *http.Request)

TriggerOSMRegionUpdate triggers an immediate incremental update for a single region. POST /api/v1/admin/osm-regions/{regionID}/update

func (*Handler) TrimActivity

func (h *Handler) TrimActivity(w http.ResponseWriter, r *http.Request)

TrimActivity logically trims the start and/or end of an activity. The original streams are not deleted; the activity metadata is updated to reflect the new time window and recalculated metrics.

POST /api/v1/activities/{id}/trim Body: { "trim_start": "RFC3339", "trim_end": "RFC3339" }

func (*Handler) UnassignActivitiesFromTag

func (h *Handler) UnassignActivitiesFromTag(w http.ResponseWriter, r *http.Request)

UnassignActivitiesFromTag removes a list of activities from a tag. DELETE /api/v1/tags/{id}/activities Body: {"activity_ids": ["id1", "id2"]}

func (*Handler) UnconfirmLiftCandidate

func (h *Handler) UnconfirmLiftCandidate(w http.ResponseWriter, r *http.Request)

UnconfirmLiftCandidate reverts a candidate's confirmed flag to false. PATCH /api/v1/activities/{id}/lift-candidates/{cid}/unconfirm

Restricted to NOT rejected_by_user — see database.UnconfirmLiftCandidate for the rationale. The handler probes the row first and returns a structured RFC7807 problem+json with a `code` field:

  • 409 ALREADY_REJECTED — candidate has rejected_by_user=true
  • 404 NOT_FOUND — missing, wrong activity, or wrong owner (collapsed to one status to avoid existence/owner leak)

func (*Handler) UngroupActivities

func (h *Handler) UngroupActivities(w http.ResponseWriter, r *http.Request)

UngroupActivities dissolves a multisport group: clears parent_id on all children, then deletes the parent. DELETE /api/v1/activities/{id}/group

func (*Handler) UpdateActivity

func (h *Handler) UpdateActivity(w http.ResponseWriter, r *http.Request)

func (*Handler) UpdateAdminLibrarySettings

func (h *Handler) UpdateAdminLibrarySettings(w http.ResponseWriter, r *http.Request)

UpdateAdminLibrarySettings replaces all 7 library.* keys atomically. PUT /api/v1/admin/library-settings

func (*Handler) UpdateAdminUserLibraryDir

func (h *Handler) UpdateAdminUserLibraryDir(w http.ResponseWriter, r *http.Request)

UpdateAdminUserLibraryDir sets library_dir_name for a target user. PUT /api/v1/admin/users/{id}/library-dir Body: {"library_dir_name": null | "my-library"} Conventions: 23505 → 200 + {"status":"library_dir_name_taken"}

UUID clash → 200 + {"status":"library_dir_name_clashes_with_user_uuid"}

func (*Handler) UpdateBikeSetup

func (h *Handler) UpdateBikeSetup(w http.ResponseWriter, r *http.Request)

func (*Handler) UpdateChainRotation

func (h *Handler) UpdateChainRotation(w http.ResponseWriter, r *http.Request)

func (*Handler) UpdateComponent

func (h *Handler) UpdateComponent(w http.ResponseWriter, r *http.Request)

func (*Handler) UpdateComponentStatus

func (h *Handler) UpdateComponentStatus(w http.ResponseWriter, r *http.Request)

func (*Handler) UpdateCustomMap

func (h *Handler) UpdateCustomMap(w http.ResponseWriter, r *http.Request)

func (*Handler) UpdateCustomMapMarker

func (h *Handler) UpdateCustomMapMarker(w http.ResponseWriter, r *http.Request)

func (*Handler) UpdateEnrichmentSettings

func (h *Handler) UpdateEnrichmentSettings(w http.ResponseWriter, r *http.Request)

UpdateEnrichmentSettings updates the user's enrichment settings.

func (*Handler) UpdateFileSchema

func (h *Handler) UpdateFileSchema(w http.ResponseWriter, r *http.Request)

UpdateFileSchema sets a custom schema template for a specific file type. PUT /api/v1/settings/file-schemas/{type}

func (*Handler) UpdateGear

func (h *Handler) UpdateGear(w http.ResponseWriter, r *http.Request)

func (*Handler) UpdateGearStatus

func (h *Handler) UpdateGearStatus(w http.ResponseWriter, r *http.Request)

func (*Handler) UpdateGovernanceBinding

func (h *Handler) UpdateGovernanceBinding(w http.ResponseWriter, r *http.Request)

func (*Handler) UpdateGovernanceRole

func (h *Handler) UpdateGovernanceRole(w http.ResponseWriter, r *http.Request)

func (*Handler) UpdateGovernanceScope

func (h *Handler) UpdateGovernanceScope(w http.ResponseWriter, r *http.Request)

func (*Handler) UpdateHeatmapSettings

func (h *Handler) UpdateHeatmapSettings(w http.ResponseWriter, r *http.Request)

func (*Handler) UpdateImportSettings

func (h *Handler) UpdateImportSettings(w http.ResponseWriter, r *http.Request)

UpdateImportSettings godoc PUT /api/v1/settings/import

func (*Handler) UpdateLibraryDir

func (h *Handler) UpdateLibraryDir(w http.ResponseWriter, r *http.Request)

UpdateLibraryDir sets the user's library directory name. PUT /api/v1/settings/library-dir

func (*Handler) UpdateMe

func (h *Handler) UpdateMe(w http.ResponseWriter, r *http.Request)

UpdateMe handles PATCH /api/v1/me — updates account details and user preferences.

func (*Handler) UpdateObjectAccessScope

func (h *Handler) UpdateObjectAccessScope(w http.ResponseWriter, r *http.Request)

func (*Handler) UpdatePrivacyZone

func (h *Handler) UpdatePrivacyZone(w http.ResponseWriter, r *http.Request)

func (*Handler) UpdateRoute

func (h *Handler) UpdateRoute(w http.ResponseWriter, r *http.Request)

UpdateRoute applies a partial update to a planned route, enforcing ownership. PATCH /api/v1/routes/{id}

Semantics: fields present in the JSON body overwrite the DB value (including explicit null for nullable columns). Fields absent from the body are preserved. See patchableRouteFields for the allow-list.

func (*Handler) UpdateRule

func (h *Handler) UpdateRule(w http.ResponseWriter, r *http.Request)

func (*Handler) UpdateSegment

func (h *Handler) UpdateSegment(w http.ResponseWriter, r *http.Request)

func (*Handler) UpdateSegmentVersionGeometry

func (h *Handler) UpdateSegmentVersionGeometry(w http.ResponseWriter, r *http.Request)

func (*Handler) UpdateSegmentVersionValidity

func (h *Handler) UpdateSegmentVersionValidity(w http.ResponseWriter, r *http.Request)

func (*Handler) UpdateServiceInterval

func (h *Handler) UpdateServiceInterval(w http.ResponseWriter, r *http.Request)

func (*Handler) UpdateShadowFSSettings

func (h *Handler) UpdateShadowFSSettings(w http.ResponseWriter, r *http.Request)

UpdateShadowFSSettings is a deleted legacy handler. Callers should use UpdateShadowFSSettingsCompat instead (writes to user_file_schemas). This stub remains so existing route wiring does not break at compile time.

func (*Handler) UpdateShadowFSSettingsCompat deprecated

func (h *Handler) UpdateShadowFSSettingsCompat(w http.ResponseWriter, r *http.Request)

UpdateShadowFSSettingsCompat is a backward-compatibility shim for PUT /settings/shadow-fs. Schema changes are written directly to user_file_schemas for the "activity" file type. HotfolderEnabled is still managed via the users table (no ShadowFS equivalent).

Deprecated: use PUT /settings/file-schemas/activity instead.

func (*Handler) UpdateTag

func (h *Handler) UpdateTag(w http.ResponseWriter, r *http.Request)

UpdateTag updates an existing tag.

func (*Handler) UpdateTargetCollection

func (h *Handler) UpdateTargetCollection(w http.ResponseWriter, r *http.Request)

func (*Handler) UpdateTargetCollectionMember

func (h *Handler) UpdateTargetCollectionMember(w http.ResponseWriter, r *http.Request)

func (*Handler) UpdateTargetPlace

func (h *Handler) UpdateTargetPlace(w http.ResponseWriter, r *http.Request)

func (*Handler) UpdateTargetRegion

func (h *Handler) UpdateTargetRegion(w http.ResponseWriter, r *http.Request)

func (*Handler) UpdateTrainingZones

func (h *Handler) UpdateTrainingZones(w http.ResponseWriter, r *http.Request)

UpdateTrainingZones saves training zone data for a single sport/sub_sport/zone_type with source_type = 'manual_override' (highest priority, always wins).

PUT /api/v1/training-zones

func (*Handler) UpdateWeatherSettings

func (h *Handler) UpdateWeatherSettings(w http.ResponseWriter, r *http.Request)

UpdateWeatherSettings updates the user's weather preferences.

func (*Handler) Upload

func (h *Handler) Upload(w http.ResponseWriter, r *http.Request)

func (*Handler) UploadActivityPhoto

func (h *Handler) UploadActivityPhoto(w http.ResponseWriter, r *http.Request)

func (*Handler) UploadComponentDocument

func (h *Handler) UploadComponentDocument(w http.ResponseWriter, r *http.Request)

UploadComponentDocument uploads a photo/document for a specific gear component.

func (*Handler) UploadCustomMapMarkerImage

func (h *Handler) UploadCustomMapMarkerImage(w http.ResponseWriter, r *http.Request)

UploadCustomMapMarkerImage uploads an image for a custom map marker. Supports multipart file upload (field "file") or JSON body with { "image_url": "..." }. POST /api/v1/custom-maps/{mapID}/markers/{markerID}/image

func (*Handler) UploadFromFile

func (h *Handler) UploadFromFile(ctx context.Context, userID, filePath string) error

UploadFromFile imports a single activity file from disk on behalf of userID. This is used by the hotfolder watcher.

func (*Handler) UploadGarminZIP

func (h *Handler) UploadGarminZIP(w http.ResponseWriter, r *http.Request)

UploadGarminZIP accepts a multipart ZIP upload of a Garmin GDPR export, extracts it to a temporary directory and immediately starts an async import job for all modules.

POST /api/v1/import/garmin/upload-zip Body: multipart/form-data { file: <export.zip> }

func (*Handler) UploadGarminZIPScan

func (h *Handler) UploadGarminZIPScan(w http.ResponseWriter, r *http.Request)

UploadGarminZIPScan accepts a ZIP upload, extracts it to a temp directory, runs a scan (without starting the import) and returns the scan result + the temp dir path so the frontend can show the config wizard before the actual import begins.

POST /api/v1/import/garmin/upload-zip-scan Body: multipart/form-data { file: <export.zip> }

func (*Handler) UploadGearDocument

func (h *Handler) UploadGearDocument(w http.ResponseWriter, r *http.Request)

func (*Handler) UploadMedia

func (h *Handler) UploadMedia(w http.ResponseWriter, r *http.Request)

UploadMedia handles POST /api/v1/upload/media Accepts multipart/form-data with field "files" (multiple allowed). Images are EXIF-parsed and auto-assigned to activities when possible. Files that cannot be auto-assigned go into the import_queue.

func (*Handler) UploadRoutePhoto

func (h *Handler) UploadRoutePhoto(w http.ResponseWriter, r *http.Request)

UploadRoutePhoto handles POST /api/v1/routes/{id}/photos Accepts a multipart form with a "photo" field, generates thumbnails, uploads to ShadowFS.

func (*Handler) UploadStravaZIP

func (h *Handler) UploadStravaZIP(w http.ResponseWriter, r *http.Request)

UploadStravaZIP accepts a multipart ZIP upload of a Strava data export, extracts it to a temporary directory and immediately starts an async import job for all modules.

POST /api/v1/import/strava/upload-zip Body: multipart/form-data { file: <export.zip> }

func (*Handler) UploadStravaZIPScan

func (h *Handler) UploadStravaZIPScan(w http.ResponseWriter, r *http.Request)

UploadStravaZIPScan accepts a ZIP upload, extracts it to a temp directory, runs a scan (without starting the import) and returns the scan result + the temp dir path so the frontend can show the config wizard before the actual import begins.

POST /api/v1/import/strava/upload-zip-scan Body: multipart/form-data { file: <export.zip> }

func (*Handler) UploadTagCoverImage

func (h *Handler) UploadTagCoverImage(w http.ResponseWriter, r *http.Request)

UploadTagCoverImage uploads a cover image for a tag (location pin). Supports multipart file upload (field "file") or JSON body with { "activity_photo_url": "..." }. POST /api/v1/tags/{id}/cover-image

func (*Handler) UpsertTagZone

func (h *Handler) UpsertTagZone(w http.ResponseWriter, r *http.Request)

UpsertTagZone creates or replaces the zone for a tag. PUT /api/v1/tag-zones/{tagId}

type HandlerConfig

type HandlerConfig struct {
	DeploymentProfile           string
	DeploymentProfileSource     string
	NatsURL                     string
	APIReplicaCount             int
	SharedStorage               string
	StorageMode                 string
	LiteLLMConfigPath           string
	LiteLLMInternalURL          string
	LLMEncryptionKey            string
	LLMBaseURL                  string
	LLMAPIKey                   string
	LLMModel                    string
	LLMContextWindow            int
	MLServiceURL                string        // e.g. http://ml:8000
	MLCallbackSecret            string        // ML_CALLBACK_SECRET env var
	MLLLMBrokerToken            string        // ML_LLM_BROKER_TOKEN or ML_CALLBACK_SECRET fallback
	BRouterURL                  string        // e.g. http://brouter:17777
	KomootEncryptionKey         string        // 32-byte hex for AES-256-GCM Komoot password encryption
	OAuthEncryptionKey          string        // 32-byte hex for OAuth token encryption
	RoutingGraphPath            string        // path to binary graph file
	RoutingTimeout              time.Duration // request timeout for route calculations (default 30s)
	BadRouterProfilesKnown      bool
	BadRouterProfilesConfigured bool
	BadRouterProfilesAvailable  bool
	BadRouterProfilesReason     string
	MaxUploadSize               int64 // maximum upload size in bytes
	// Data directories
	OSMDataDir         string // directory for OSM PBF files (default /data/osm)
	DEMDataDir         string // directory for DEM/HGT files (default /data/dem)
	BRouterSegmentsDir string // directory for BRouter segment files (default /data/brouter/segments)
	BRouterProfilesDir string // directory for BRouter profile files (default /data/brouter/profiles)
	TilesDir           string // directory for offline PMTiles map files (default /data/tiles)
	// ImportAllowedDirs lists extra directories beyond /tmp that are trusted for
	// source-path imports (Garmin, Strava). Populated from IMPORT_ALLOWED_DIRS.
	ImportAllowedDirs []string

	// ShadowFS — used by rename handlers and admin dashboard
	LibraryBasePath   string // e.g. /data/library (local backend only)
	StorageBackend    string // "local" or "s3"
	KeepVersions      int    // 0=delete, 1=keep last, -1=keep all
	SyncEnabled       bool
	LocalBE           shadowfs.StorageBackend // nil when no local backend
	S3BE              shadowfs.StorageBackend // nil when no S3 backend
	BucketFn          func(string) string     // fileType → S3 bucket
	FrontendStaticDir string                  // directory serving production frontend assets
}

type IsochronePOI

type IsochronePOI struct {
	Name    string  `json:"name"`
	Type    string  `json:"type"`
	Lat     float64 `json:"lat"`
	Lng     float64 `json:"lng"`
	OsmID   int64   `json:"osm_id,omitempty"`
	Minutes float64 `json:"minutes"`
}

IsochronePOI is a point of interest found within the outermost isochrone.

type IsochronePolygon

type IsochronePolygon struct {
	Minutes  int             `json:"minutes"`
	Geometry json.RawMessage `json:"geometry"` // GeoJSON Polygon
}

IsochronePolygon is one isochrone band in the response.

type IsochroneRequest

type IsochroneRequest struct {
	// Start is a [longitude, latitude] coordinate pair.
	Start [2]float64 `json:"start"`
	// Minutes lists the travel-time thresholds (in minutes) for each isochrone
	// band, e.g. [60, 120, 180].  At least one value is required.
	Minutes []int `json:"minutes"`
	// Profile selects the routing cost function, e.g. "hiking", "mtb", "road".
	Profile string `json:"profile"`
	// POITypes is an optional list of OSM categories to discover within the
	// reachable area, e.g. ["summit", "gastronomy", "accommodation"].
	POITypes []string `json:"poi_types"`
}

IsochroneRequest describes the POST body for /api/v1/isochrone.

type IsochroneResponse

type IsochroneResponse struct {
	Polygons  []IsochronePolygon `json:"polygons"`
	POIs      []IsochronePOI     `json:"pois"`
	Truncated bool               `json:"truncated"`
}

IsochroneResponse is the JSON body returned by POST /api/v1/isochrone.

type KomootLoginRequest

type KomootLoginRequest struct {
	Email           string `json:"email"`
	Password        string `json:"password"`
	SaveCredentials bool   `json:"save_credentials"` // false = only use for this session, don't store
}

type KomootScanResult

type KomootScanResult struct {
	TourCount      int  `json:"tour_count"`
	RecordedCount  int  `json:"recorded_count"`
	PlannedCount   int  `json:"planned_count"`
	NewCount       int  `json:"new_count"` // tours not yet imported
	HasCredentials bool `json:"has_credentials"`
}

type KomootStartRequest

type KomootStartRequest struct {
	ImportRecorded         bool                   `json:"import_recorded"`
	ImportPlanned          bool                   `json:"import_planned"`
	ImportPhotos           bool                   `json:"import_photos"`
	TourIDs                []int64                `json:"tour_ids,omitempty"` // empty = all new
	Options                importer.ImportOptions `json:"options"`
	DeleteCredentialsAfter bool                   `json:"delete_credentials_after"` // delete stored credentials after import completes
}

type LLMRoleResolver

type LLMRoleResolver interface {
	Resolve(ctx context.Context, userID, role string) (agent.LLMConfig, error)
}

type LabeledRoute

type LabeledRoute struct {
	Candidate       RouteCandidate
	VariantID       string
	VariantLabelKey string
	Descriptors     []VariantDescriptor
}

LabeledRoute is a RouteCandidate plus its assigned variant_id and descriptors.

Produced by assignLabels in the V2 route-suggestion pipeline.

type LandscapePreferences

type LandscapePreferences struct {
	Forest     int `json:"forest"`      // 0-10
	Water      int `json:"water"`       // 0-10 (uses RiverProximity including lakes)
	Protected  int `json:"protected"`   // 0-10
	LowNoise   int `json:"low_noise"`   // 0-10
	LowTraffic int `json:"low_traffic"` // 0-10
}

LandscapePreferences holds per-dimension landscape preference weights (0–10).

type LoopQuality

type LoopQuality string

LoopQuality classifies a round-trip suggestion by how "loop-like" it is.

const (
	LoopQualityLoop        LoopQuality = "loop"
	LoopQualityPartialLoop LoopQuality = "partial_loop"
	LoopQualityOutAndBack  LoopQuality = "out_and_back"
)

type MLCallbackPayload

type MLCallbackPayload struct {
	Type   string          `json:"type"` // "enrichment", "analytics", "chat"
	UserID string          `json:"user_id"`
	TaskID string          `json:"task_id"`
	Status string          `json:"status"` // "done", "error"
	Result json.RawMessage `json:"result,omitempty"`
	Error  string          `json:"error,omitempty"`
}

MLCallbackPayload is the payload from the ML service.

type ObjectAccessLinkTokenValue

type ObjectAccessLinkTokenValue struct {
	GrantID string `json:"grant_id"`
	Token   string `json:"token"`
}

type ObjectAccessScopeResponse

type ObjectAccessScopeResponse struct {
	Scope      model.ObjectAccessScope      `json:"scope"`
	LinkTokens []ObjectAccessLinkTokenValue `json:"link_tokens,omitempty"`
}

type POIResult

type POIResult struct {
	Name   string     `json:"name"`
	Type   string     `json:"type"`
	Ele    *float64   `json:"ele,omitempty"`
	LatLng [2]float64 `json:"latlng"`
	OsmID  int64      `json:"osm_id"`
}

POIResult is a point-of-interest search result returned to the client.

type PublishRouteCourseRequest

type PublishRouteCourseRequest struct {
	RouteSnapshotID              string         `json:"route_snapshot_id,omitempty"`
	SnapshotID                   string         `json:"snapshot_id"`
	SourceRouteVersion           int64          `json:"source_route_version"`
	PublishPolicy                string         `json:"publish_policy,omitempty"`
	EquivalenceDecision          string         `json:"equivalence_decision,omitempty"`
	Visibility                   string         `json:"visibility,omitempty"`
	AccessScopeID                *string        `json:"access_scope_id,omitempty"`
	CompatibilityPreset          string         `json:"compatibility_preset,omitempty"`
	TargetPublicationID          *string        `json:"target_publication_id,omitempty"`
	TargetCourseSegmentID        *string        `json:"target_course_segment_id,omitempty"`
	TargetCourseSegmentVersionID *string        `json:"target_course_segment_version_id,omitempty"`
	CompatibilityEvidence        map[string]any `json:"compatibility_evidence,omitempty"`
	OperationID                  string         `json:"operation_id"`
}

type PublishRouteCourseResponse

type PublishRouteCourseResponse struct {
	PublicationID          string `json:"publication_id"`
	CourseSegmentID        string `json:"course_segment_id"`
	CourseSegmentVersionID string `json:"course_segment_version_id"`
}

type ResponsePassedSummit

type ResponsePassedSummit struct {
	OsmID    int64    `json:"osm_id"`
	Name     string   `json:"name"`
	EleM     *float64 `json:"ele_m,omitempty"`
	Category string   `json:"category,omitempty"`
	DistM    float64  `json:"dist_m"`
	Lat      float64  `json:"lat"`
	Lng      float64  `json:"lng"`
}

ResponsePassedSummit is a handler-internal DTO for database.PassedSummit. It avoids serialising DB types directly (DTO-layer isolation).

type RoundTripOptions

type RoundTripOptions struct {
	MaxAvgFlatDifference  float32
	MaxAvgFlatVsBaseline  float32
	MaxLeg2DistanceFactor float32
	OverlapCheckThreshold float64
	SoftPenaltyFactor     float32
}

RoundTripOptions configures the quality-gate thresholds.

func DefaultRoundTripOptions

func DefaultRoundTripOptions() RoundTripOptions

DefaultRoundTripOptions returns the tested defaults.

type RoundTripResult

type RoundTripResult struct {
	Leg1        *routing.RouteResult
	Leg2        *routing.RouteResult
	LoopQuality LoopQuality
	OverlapPct  float64
	AvgFlatLeg1 float32
	AvgFlatLeg2 float32
}

RoundTripResult is the outcome of one variant's round-trip construction.

type RouteCandidate

type RouteCandidate struct {
	NodeID uint64
	ViaLat float64
	ViaLon float64

	Leg1   *routing.RouteResult
	Leg2   *routing.RouteResult
	Merged *routing.RouteResult

	DistanceM float64
	AscentM   float64
	DescentM  float64
	AvgFlat   float32

	// Distance-weighted averages across merged route.
	// 0 means "either no coverage data OR actually zero value" — see HasCoverage.
	MergedForestPct      float32
	MergedRiverProximity float32
	MergedProtectedArea  float32
	MergedNoiseScore     float32
	MergedTrafficClass   float32

	// HasCoverage is true iff at least one coverage layer (forest, water,
	// protected area, noise, or traffic) returned a real measurement for
	// this route. False means all Merged* values are zero only because no
	// coverage data exists — not because the route scored zero on merit.
	HasCoverage bool

	LoopQuality LoopQuality
	OverlapPct  float64

	// F26: Outdoor-descriptor aggregation over merged.EdgeHandles.
	// MaxMtbScale/MaxSacScale/MaxViaFerrataScale hold per-route extrema.
	// MaxMtbScale is the composite: max(mtb_scale, mtb:scale:uphill) per edge,
	// then max across the whole route. See aggregateLegOutdoor below.
	MaxMtbScale          uint8   `json:"-"`
	MaxSacScale          uint8   `json:"-"`
	MaxViaFerrataScale   uint8   `json:"-"`
	HasViaFerrataHighway bool    `json:"-"`
	MaxTrailVisibility   uint8   `json:"-"`
	IntermediatePctTv    float32 `json:"-"`

	TaggedPctMtb float32 `json:"-"`
	TaggedPctSac float32 `json:"-"`
	TaggedPctVia float32 `json:"-"`
	TaggedPctTv  float32 `json:"-"`
}

RouteCandidate is a fully-constructed round-trip with merged metrics.

type RouteRequest

type RouteRequest struct {
	// Waypoints is a list of [latitude, longitude] pairs (at least 2).
	// Frontend convention is latlng; the proxy swaps to lonlat for BRouter.
	Waypoints [][2]float64 `json:"waypoints"`
	// Profile is the BRouter routing profile name (e.g. "hiking-mountain", "trekking").
	Profile string `json:"profile"`
	// Alternatives is the BRouter alternative-route index (0 = primary).
	Alternatives int `json:"alternatives"`
	// ProfileParams are optional key=value overrides for BRouter profile variables.
	// Passed as profile:key=value query parameters to BRouter.
	// Example: {"SAC_scale_limit": "2", "consider_elevation": "true"}
	ProfileParams map[string]string `json:"profile_params,omitempty"`
}

RouteRequest is the request body for POST /api/v1/routing/route.

type RouteSuggestion

type RouteSuggestion struct {
	Variant  int        `json:"variant"`
	Profile  string     `json:"profile"`
	ViaPoint [2]float64 `json:"via_point"`

	VariantID       string `json:"variant_id"`
	VariantLabelKey string `json:"variant_label_key"`

	DistanceM float64 `json:"distance_m"`
	AscentM   float64 `json:"ascent_m"`
	DescentM  float64 `json:"descent_m"`
	AvgFlat   float64 `json:"avg_flat"`

	LoopQuality string  `json:"loop_quality"`
	OverlapPct  float64 `json:"overlap_pct"`

	DistanceTargetAchieved bool `json:"distance_target_achieved"`
	AscentTargetAchieved   bool `json:"ascent_target_achieved"`

	GeoJSON    json.RawMessage `json:"geojson"`
	IsFallback bool            `json:"is_fallback"`

	// Descriptors are dynamic variant labels (e.g. "more forest", "less ascent").
	// Always present as an array (may be empty); never null.
	Descriptors []VariantDescriptor `json:"variant_descriptors"`

	// Route aggregates (0.0–1.0). Propagated from RouteCandidate.MergedForestPct
	// and MergedProtectedArea (computed during coverage scoring).
	ForestPct    float32 `json:"forest_pct"`
	ProtectedPct float32 `json:"protected_pct"`

	// Range-based highlights per variant. omitempty because data may be absent
	// for e.g. approach-only routes. Coord indices reference GeoJSON.features[0]
	// .geometry.coordinates within the same suggestion.
	ForestRanges    []CoordRange            `json:"forest_ranges,omitempty"`
	ProtectedRanges []CoordRange            `json:"protected_ranges,omitempty"`
	SacRanges       map[string][]CoordRange `json:"sac_ranges,omitempty"`
	MtbRanges       map[string][]CoordRange `json:"mtb_ranges,omitempty"`
	// Via-Ferrata — keys "K1".."K6".
	// VfUnknownRanges: K?-Ranges (highway=via_ferrata AND scale=NULL), Invariant I9.
	VfRanges        map[string][]CoordRange `json:"vf_ranges,omitempty"`
	VfUnknownRanges []CoordRange            `json:"vf_unknown_ranges,omitempty"`

	// Summits and passes passed within the user-configured radius.
	// Uses handler-internal DTO (ResponsePassedSummit) to avoid leaking DB types.
	SummitsPassed []ResponsePassedSummit `json:"summits_passed,omitempty"`

	// ElevationProfile is the authoritative top-level profile for the suggestion
	// (Spec §6.4 "key always present"). The same data is also exposed per-feature
	// in geojson.properties.elevation_profile for per-segment consumers. Both
	// paths intentionally carry the same data — frontend should prefer the
	// top-level field for the combined chart, per-feature for segment editing.
	// nil when no DEM data is available for the route.
	ElevationProfile *[]routing.ElevationPoint `json:"elevation_profile"`
}

RouteSuggestion is a single suggested route variant returned by SuggestRoutes.

type RouteSuggestionRequest

type RouteSuggestionRequest struct {
	// Start is [longitude, latitude] of the starting point.
	Start [2]float64 `json:"start"`
	// End is an optional fixed end point. If omitted the route returns to Start (round-trip).
	End *[2]float64 `json:"end,omitempty"`
	// RoundTrip indicates the route should return to Start. Ignored when End is set.
	RoundTrip bool `json:"round_trip"`
	// DirectionPoint is an optional point the suggested route should trend toward.
	DirectionPoint *[2]float64 `json:"direction_point,omitempty"`
	// TargetDistanceM is the desired total route length in metres.
	TargetDistanceM float64 `json:"target_distance_m"`
	// SportType is the FIT sport type (e.g. "hiking", "cycling").
	SportType string `json:"sport_type"`
	// SubType is the optional canonical FIT sport subtype.
	SubType *string `json:"sub_type,omitempty"`
	// Modifiers optionally constrain profile matching by activity flags.
	Modifiers *sport.ModifierFilters `json:"modifiers,omitempty"`
	// Profile is the routing profile (e.g. "hiking", "mtb", "gravel") or a custom profile name.
	Profile string `json:"profile"`
	// ProfileSportOverride bypasses profile-vs-sport tuple matching after an explicit frontend warning.
	ProfileSportOverride bool `json:"profile_sport_override"`
	// PreferUnknown, when true, weights the via-point selection toward
	// trail segments the user has not visited yet (Terra Incognita).
	PreferUnknown bool `json:"prefer_unknown"`
	// LandscapePrefs holds per-dimension landscape preference weights (0–10).
	LandscapePrefs *LandscapePreferences `json:"landscape_prefs,omitempty"`
	// SurfaceTendency selects a surface-type preset: "trails", "forest_roads", "paved", or "mixed".
	SurfaceTendency string `json:"surface_tendency,omitempty"`
	// MaxSacScale overrides the profile's SAC difficulty limit (0–6).
	MaxSacScale *int `json:"max_sac_scale,omitempty"`
	// MaxMtbScale overrides the profile's MTB difficulty limit (0–5).
	MaxMtbScale *int `json:"max_mtb_scale,omitempty"`
	// Ascent targeting
	TargetAscentM      *float64 `json:"target_ascent_m,omitempty"`
	AscentTolerancePct *float64 `json:"ascent_tolerance_pct,omitempty"`
	// Variant control
	MaxVariants *int `json:"max_variants,omitempty"`
}

RouteSuggestionRequest is the request body for POST /api/v1/routes/suggest.

type RouteSuggestionResponse

type RouteSuggestionResponse struct {
	Suggestions       []RouteSuggestion `json:"suggestions"`
	CoverageAvailable bool              `json:"coverage_available"`
	PartialSearch     bool              `json:"partial_search"`
}

RouteSuggestionResponse is the JSON response for POST /api/v1/routes/suggest.

type SegmentEnrichmentRequest

type SegmentEnrichmentRequest struct {
	Coordinates   [][]float64 `json:"coordinates"`
	SegType       string      `json:"seg_type"`
	DominantSport string      `json:"dominant_sport,omitempty"`
}

SegmentEnrichmentRequest is the POST body for POST /segments/enrichment-suggest.

type ShadowFSSettingsRequest

type ShadowFSSettingsRequest struct {
	Schema           string `json:"schema"`
	HotfolderEnabled *bool  `json:"hotfolder_enabled,omitempty"`
}

ShadowFSSettingsRequest is the PUT request body for updating shadow FS settings.

type ShadowFSSettingsResponse

type ShadowFSSettingsResponse struct {
	Schema           string `json:"schema"`
	HotfolderPath    string `json:"hotfolder_path"`
	HotfolderEnabled bool   `json:"hotfolder_enabled"`
}

ShadowFSSettingsResponse is the GET response for shadow FS settings.

type SportZones

type SportZones struct {
	Sport      int16                  `json:"sport"`
	SportLabel string                 `json:"sport_label"`
	Zones      map[string]*ZoneDetail `json:"zones"` // keys: "hr", "power", "speed"
}

SportZones groups all zone details for a single sport (keyed by fit_sport_id).

type TileCount

type TileCount struct {
	TileID    string `json:"tile_id"`    // e.g. "46_14" (lat_lon floor)
	UnscaledN int    `json:"unscaled_n"` // number of unscaled VF edges in this tile
}

TileCount pairs a tile ID (human-readable "LAT_LON") with a per-tile edge count.

type TrainingZonesResponse

type TrainingZonesResponse struct {
	Sports []SportZones `json:"sports"`
}

TrainingZonesResponse is the top-level response for GET /api/v1/training-zones.

type TxFactory

type TxFactory func(ctx context.Context) (pgx.Tx, error)

TxFactory is a function that begins a database transaction. It is used by beginTx to allow tests to inject a mock pgx.Tx.

type UpdateObjectAccessScopeRequest

type UpdateObjectAccessScopeRequest struct {
	RevokeScope    bool     `json:"revoke_scope,omitempty"`
	RevokeGrantIDs []string `json:"revoke_grant_ids,omitempty"`
}

type VariantDescriptor

type VariantDescriptor struct {
	Key          string  `json:"key"`
	Magnitude    float64 `json:"magnitude"`
	DisplayValue string  `json:"display_value,omitempty"`
}

VariantDescriptor describes a single distinctive feature of a route.

type VfCoverageResponse

type VfCoverageResponse struct {
	TotalViaFerrataEdges int         `json:"total_via_ferrata_edges"`
	ScaledEdges          int         `json:"scaled_edges"`
	UnscaledEdges        int         `json:"unscaled_edges"`
	CoveragePct          float32     `json:"coverage_pct"` // scaled / total; 0.0 when total==0
	TilesScanned         int         `json:"tiles_scanned"`
	Truncated            bool        `json:"truncated"` // true when ?limit < total tile count
	TopUnscaledTiles     []TileCount `json:"top_unscaled_tiles,omitempty"`
}

VfCoverageResponse is the JSON response for GET /admin/routing/vf-encoding-coverage.

It reports how many via_ferrata edges (highway==22) in the current tile set carry a non-zero vfScale (K1–K6), vs. how many are unscaled (vfScale==0, meaning the tile was built before Task 7a landed or before a tile-rebuild ran).

A pre-rebuild instance will report coverage_pct ≈ 0.001 (nearly all zeroes). A post-rebuild instance should report coverage_pct ≥ 0.95 once OSM data carries via_ferrata_scale tags (see docs/_internal/open-questions.md).

type ZoneDetail

type ZoneDetail struct {
	ZoneNames                []string  `json:"zone_names"`
	ZoneHighs                []float64 `json:"zone_highs"`
	ThresholdHeartRate       *int16    `json:"threshold_heart_rate,omitempty"`
	FunctionalThresholdPower *int32    `json:"functional_threshold_power,omitempty"`
	ThresholdSpeedMps        *float64  `json:"threshold_speed_mps,omitempty"`
	SourceType               string    `json:"source_type"`
}

ZoneDetail holds the configuration for one zone type.

Source Files

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL