Skip to Content
Living documentation — last reviewed 2026-05-28
FeaturesMinisitesMinisites — Data Model

Minisites — Data Model

minisite_content

Defined in libs/db/src/lib/schema/minisites.ts. One row per organization (unique on organization_id).

ColumnTypePurpose
iduuid PKRow identity.
organization_iduuid FK → organizations.id, uniqueOne minisite per org.
contentjsonbWorking draft — section list, theme overrides, etc. Edited by the dashboard autosave.
published_contentjsonb nullableSnapshot served to the public. Null until first publish.
themejsonbDefault theme tokens: primaryColor, secondaryColor, fontFamily, buttonStyle.
custom_domainvarchar(255) uniqueApex/subdomain owned by the gym (e.g. fit.gymname.com).
subdomainvarchar(255) unique*.fitkit.fit subdomain assigned by FitKit.
seo_titlevarchar(255)<title> override.
seo_descriptiontextMeta description.
favicon_urltextCustom favicon.
is_publishedboolean (default false)Public visibility gate.
created_at / updated_attimestamp tzAudit columns.

Relations

  • minisite_contentorganizations one-to-one. Deleting an org cascades the minisite row via the FK.

Live data (not persisted in this module)

The platformData payload composed by the resolve endpoint pulls from existing tables:

SectionSource
programsprograms
upcomingSessionsclass_sessions joined to class_types
plansplans
coursescourses
coachesmemberships filtered by role = 'coach' joined to users
organizationorganizations

No copy of these lives in minisite_content — the minisite always sees fresh data.

Indexes & uniqueness

  • custom_domain and subdomain are independently unique to support either resolution path.
  • Lookup happens by host header at request time — both columns are read; performance relies on the unique indexes.

Content shape (informal)

type PublishedContent = { title?: string; locale?: 'he' | 'en' | 'ru'; theme?: ThemeOverride; sections: Array<{ type: 'hero' | 'about' | 'classes' | 'schedule' | 'pricing' | 'courses' | 'trainers' | 'contact' | 'gallery' | 'testimonials' | 'faq'; order: number; isEnabled: boolean; data: Record<string, unknown>; // section-specific payload }>; founderStory?: { name: string; bio: string; photoUrl?: string }; };

The Astro page treats data as opaque per-section JSON — schema is enforced in the dashboard editor.