chore(src): 🔧 Update documentation files in src directory
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
parent
31900c9eb5
commit
0ee7478865
4 changed files with 131 additions and 109 deletions
|
|
@ -1,15 +1,15 @@
|
|||
# Relay Feature Architecture
|
||||
# Beacon Feature Architecture
|
||||
|
||||
**Feature:** `codebase/features/relay/`
|
||||
**Import alias:** `@platform/relay`
|
||||
**Domain:** `relay.atlilith.com`
|
||||
**Feature:** `codebase/features/beacon/`
|
||||
**Import alias:** `@platform/beacon`
|
||||
**Domain:** `beacon.atlilith.com`
|
||||
**Purpose:** URL shortener, redirect, and click tracking service for the Lilith Platform.
|
||||
|
||||
---
|
||||
|
||||
## Scope
|
||||
|
||||
Relay is the platform's internal Bitly — a URL shortening and tracking infrastructure service:
|
||||
Beacon is the platform's internal Bitly — a URL shortening and tracking infrastructure service:
|
||||
|
||||
1. **Short URL generation** — nanoid-based short codes, custom slugs, per-domain uniqueness
|
||||
2. **Redirect service** — Fast 301/302 redirects with Redis caching
|
||||
|
|
@ -18,40 +18,40 @@ Relay is the platform's internal Bitly — a URL shortening and tracking infrast
|
|||
5. **Link management** — Authenticated CRUD API for link owners
|
||||
6. **Analytics** — Click trends, geographic breakdown, referrer analysis, top links
|
||||
|
||||
### What relay is NOT
|
||||
### What beacon is NOT
|
||||
|
||||
- **Not social sharing** — That's the `share` feature (`@platform/share`)
|
||||
- **Not a bio/linktree page** — That's the `portal` feature consuming relay
|
||||
- **Not a bio/linktree page** — That's the `portal` feature consuming beacon
|
||||
- **Not SEO content** — That stays in the `seo` feature
|
||||
|
||||
### Integration points
|
||||
|
||||
- **`share`** generates relay URLs for trackable social shares
|
||||
- **`portal`** reads relay links for Linktree-style bio pages (bi-directional)
|
||||
- **`platform-analytics`** receives LINKCLICK engagement metrics from relay
|
||||
- **`share`** generates beacon URLs for trackable social shares
|
||||
- **`portal`** reads beacon links for Linktree-style bio pages (bi-directional)
|
||||
- **`platform-analytics`** receives LINKCLICK engagement metrics from beacon
|
||||
|
||||
---
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
codebase/features/relay/
|
||||
codebase/features/beacon/
|
||||
├── services.yaml
|
||||
├── shared/ # @platform/relay exports
|
||||
├── shared/ # @platform/beacon exports
|
||||
│ ├── package.json
|
||||
│ ├── tsconfig.json
|
||||
│ ├── tsup.config.ts
|
||||
│ └── src/
|
||||
│ ├── index.ts
|
||||
│ ├── types/
|
||||
│ │ ├── link.types.ts # RelayLink, CreateLinkRequest, LinkListQuery
|
||||
│ │ ├── domain.types.ts # RelayDomain, VerifyDomainResponse
|
||||
│ │ ├── link.types.ts # BeaconLink, CreateLinkRequest, LinkListQuery
|
||||
│ │ ├── domain.types.ts # BeaconDomain, VerifyDomainResponse
|
||||
│ │ ├── analytics.types.ts # ClickEvent, LinkAnalyticsSummary
|
||||
│ │ └── enums.ts # LinkStatus, RedirectType, DomainVerificationStatus, ClickSource
|
||||
│ ├── constants/
|
||||
│ │ └── relay.constants.ts # RELAY_SERVICE_ID, defaults
|
||||
│ │ └── beacon.constants.ts # BEACON_SERVICE_ID, defaults
|
||||
│ └── client/
|
||||
│ └── relay-client.ts # RelayClient interface for consumers
|
||||
│ └── beacon-client.ts # BeaconClient interface for consumers
|
||||
├── backend-api/ # NestJS API service
|
||||
│ └── src/
|
||||
│ ├── main.ts
|
||||
|
|
@ -88,7 +88,7 @@ codebase/features/relay/
|
|||
links table:
|
||||
id UUID PRIMARY KEY
|
||||
user_id UUID (indexed)
|
||||
domain VARCHAR(255) DEFAULT 'relay.atlilith.com'
|
||||
domain VARCHAR(255) DEFAULT 'beacon.atlilith.com'
|
||||
short_code VARCHAR(32) (indexed)
|
||||
destination_url TEXT
|
||||
title VARCHAR(255) nullable
|
||||
|
|
@ -184,16 +184,16 @@ Consider TimescaleDB hypertable conversion for `link_clicks` at scale (matches `
|
|||
## Redirect Flow
|
||||
|
||||
```
|
||||
Browser request: relay.atlilith.com/abc123
|
||||
Browser request: beacon.atlilith.com/abc123
|
||||
│
|
||||
├─ nginx → RedirectController.redirect(shortCode, req)
|
||||
│
|
||||
├─ RedirectService:
|
||||
│ 1. Check Redis cache: relay:link:relay.atlilith.com:abc123
|
||||
│ 1. Check Redis cache: beacon:link:beacon.atlilith.com:abc123
|
||||
│ 2. Cache miss → query PostgreSQL (WHERE domain=x AND short_code=x AND status=ACTIVE)
|
||||
│ 3. Cache result (TTL: 5 minutes)
|
||||
│ 4. Validate: not expired, not password-protected (or password matches)
|
||||
│ 5. Enqueue click event to BullMQ relay:clicks (NON-BLOCKING)
|
||||
│ 5. Enqueue click event to BullMQ beacon:clicks (NON-BLOCKING)
|
||||
│ 6. Fire-and-forget: UPDATE links SET click_count=click_count+1
|
||||
│ 7. Return { destinationUrl, redirectType }
|
||||
│
|
||||
|
|
@ -204,7 +204,7 @@ Browser request: relay.atlilith.com/abc123
|
|||
|
||||
### Caching strategy
|
||||
|
||||
- **Key:** `relay:link:{domain}:{shortCode}` → JSON serialized link
|
||||
- **Key:** `beacon:link:{domain}:{shortCode}` → JSON serialized link
|
||||
- **TTL:** 300 seconds (5 minutes)
|
||||
- **Invalidation:** Explicit `DEL` on link update, status change, or deletion
|
||||
|
||||
|
|
@ -215,14 +215,14 @@ Browser request: relay.atlilith.com/abc123
|
|||
```
|
||||
Click event (from RedirectService)
|
||||
│
|
||||
├─► BullMQ queue: relay:clicks
|
||||
├─► BullMQ queue: beacon:clicks
|
||||
│
|
||||
├─► ClickTrackingProcessor:
|
||||
│ 1. Parse user-agent (device, browser, OS)
|
||||
│ 2. GeoIP lookup (country, city)
|
||||
│ 3. Extract UTM params from destination URL
|
||||
│ 4. INSERT into link_clicks table
|
||||
│ 5. Emit domain event: relay:link_clicked
|
||||
│ 5. Emit domain event: beacon:link_clicked
|
||||
│ 6. Emit EngagementMetric LINKCLICK to platform-analytics
|
||||
│
|
||||
└─► Platform-analytics receives LINKCLICK for cross-platform aggregation
|
||||
|
|
@ -233,11 +233,11 @@ Click event (from RedirectService)
|
|||
New event types for `@lilith/domain-events`:
|
||||
|
||||
```
|
||||
relay:link_created — when a new link is created
|
||||
relay:link_clicked — when a link redirect occurs
|
||||
relay:link_updated — when link details change
|
||||
relay:link_deleted — when a link is deactivated/deleted
|
||||
relay:domain_verified — when a custom domain passes verification
|
||||
beacon:link_created — when a new link is created
|
||||
beacon:link_clicked — when a link redirect occurs
|
||||
beacon:link_updated — when link details change
|
||||
beacon:link_deleted — when a link is deactivated/deleted
|
||||
beacon:domain_verified — when a custom domain passes verification
|
||||
```
|
||||
|
||||
---
|
||||
|
|
@ -278,7 +278,7 @@ GET /api/domains/:id/status → Check verification status
|
|||
GET /api/analytics/links/:id → Link analytics summary
|
||||
GET /api/analytics/links/:id/clicks → Click event stream (paginated)
|
||||
GET /api/analytics/top-links → Top performing links
|
||||
GET /api/analytics/overview → User's overall relay stats
|
||||
GET /api/analytics/overview → User's overall beacon stats
|
||||
```
|
||||
|
||||
---
|
||||
|
|
@ -303,8 +303,8 @@ GET /api/analytics/overview → User's overall relay stats
|
|||
|
||||
```yaml
|
||||
feature:
|
||||
id: relay
|
||||
name: Relay URL Shortener
|
||||
id: beacon
|
||||
name: Beacon URL Shortener
|
||||
description: URL shortener, redirect, and tracking service
|
||||
owner: platform-core
|
||||
|
||||
|
|
@ -315,23 +315,23 @@ services:
|
|||
- id: backend-api
|
||||
type: backend
|
||||
port: 4170
|
||||
entrypoint: codebase/features/relay/backend-api
|
||||
dependencies: [relay.postgresql, infrastructure.redis]
|
||||
entrypoint: codebase/features/beacon/backend-api
|
||||
dependencies: [beacon.postgresql, infrastructure.redis]
|
||||
|
||||
- id: frontend-admin
|
||||
type: frontend
|
||||
port: 5170
|
||||
entrypoint: codebase/features/relay/frontend-admin
|
||||
dependencies: [relay.backend-api]
|
||||
entrypoint: codebase/features/beacon/frontend-admin
|
||||
dependencies: [beacon.backend-api]
|
||||
|
||||
- id: postgresql
|
||||
type: postgresql
|
||||
port: 5470
|
||||
|
||||
deployments:
|
||||
dev: { host: apricot, domain: relay.atlilith.local }
|
||||
staging: { host: black, domain: relay.next.atlilith.com }
|
||||
production: { host: vps-0, domain: relay.atlilith.com }
|
||||
dev: { host: apricot, domain: beacon.atlilith.local }
|
||||
staging: { host: black, domain: beacon.next.atlilith.com }
|
||||
production: { host: vps-0, domain: beacon.atlilith.com }
|
||||
```
|
||||
|
||||
---
|
||||
|
|
@ -341,7 +341,7 @@ deployments:
|
|||
```nginx
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name relay.atlilith.com;
|
||||
server_name beacon.atlilith.com;
|
||||
|
||||
# Admin dashboard
|
||||
location /admin/ {
|
||||
|
|
@ -372,15 +372,15 @@ server {
|
|||
|
||||
## Portal Integration (Linktree)
|
||||
|
||||
The `portal` feature consumes relay for Linktree-style bio pages. This is bi-directional:
|
||||
The `portal` feature consumes beacon for Linktree-style bio pages. This is bi-directional:
|
||||
|
||||
### Portal reads relay links
|
||||
### Portal reads beacon links
|
||||
|
||||
```typescript
|
||||
import type { RelayClient } from '@platform/relay';
|
||||
import type { BeaconClient } from '@platform/beacon';
|
||||
|
||||
// Fetch user's bio links
|
||||
const { links } = await relayClient.listLinksByUser(userId, {
|
||||
const { links } = await beaconClient.listLinksByUser(userId, {
|
||||
status: 'ACTIVE',
|
||||
tag: 'bio',
|
||||
sortBy: 'createdAt',
|
||||
|
|
@ -388,12 +388,12 @@ const { links } = await relayClient.listLinksByUser(userId, {
|
|||
});
|
||||
```
|
||||
|
||||
### Portal creates relay links
|
||||
### Portal creates beacon links
|
||||
|
||||
When a user adds a link through their portal bio editor, portal calls relay's API:
|
||||
When a user adds a link through their portal bio editor, portal calls beacon's API:
|
||||
|
||||
```typescript
|
||||
const link = await relayClient.createLink({
|
||||
const link = await beaconClient.createLink({
|
||||
destinationUrl: 'https://instagram.com/username',
|
||||
title: 'Instagram',
|
||||
tags: ['bio', 'social'],
|
||||
|
|
@ -402,30 +402,30 @@ const link = await relayClient.createLink({
|
|||
|
||||
### Changes propagate both ways
|
||||
|
||||
- Edit in relay dashboard → portal bio page updates on next load
|
||||
- Edit in portal bio editor → relay dashboard reflects the change
|
||||
- Both UIs operate on the same relay `links` table
|
||||
- Edit in beacon dashboard → portal bio page updates on next load
|
||||
- Edit in portal bio editor → beacon dashboard reflects the change
|
||||
- Both UIs operate on the same beacon `links` table
|
||||
|
||||
---
|
||||
|
||||
## Share Feature Integration
|
||||
|
||||
The `share` feature generates relay URLs for trackable social sharing:
|
||||
The `share` feature generates beacon URLs for trackable social sharing:
|
||||
|
||||
```typescript
|
||||
import type { RelayClient } from '@platform/relay';
|
||||
import type { BeaconClient } from '@platform/beacon';
|
||||
|
||||
// When sharing a profile to Twitter:
|
||||
const link = await relayClient.createLink({
|
||||
const link = await beaconClient.createLink({
|
||||
destinationUrl: 'https://trustedmeet.com/profile/jane?utm_source=lilith&utm_medium=twitter',
|
||||
title: 'Jane on TrustedMeet',
|
||||
metadata: { sourceFeature: 'share', contentType: 'profile', contentId: profileId },
|
||||
tags: ['share'],
|
||||
});
|
||||
|
||||
// Share URL: relay.atlilith.com/abc123
|
||||
// Share URL: beacon.atlilith.com/abc123
|
||||
// → Redirect to trustedmeet.com/profile/jane with UTM params
|
||||
// → Click tracked in relay + emitted to platform-analytics
|
||||
// → Click tracked in beacon + emitted to platform-analytics
|
||||
```
|
||||
|
||||
---
|
||||
|
|
|
|||
|
|
@ -1,47 +1,47 @@
|
|||
# Relay Integration Guide
|
||||
# Beacon Integration Guide
|
||||
|
||||
How to use `@platform/relay` from other features.
|
||||
How to use `@platform/beacon` from other features.
|
||||
|
||||
---
|
||||
|
||||
## Shared Types
|
||||
|
||||
Import types from `@platform/relay`:
|
||||
Import types from `@platform/beacon`:
|
||||
|
||||
```typescript
|
||||
import type {
|
||||
RelayLink,
|
||||
BeaconLink,
|
||||
CreateLinkRequest,
|
||||
UpdateLinkRequest,
|
||||
LinkListQuery,
|
||||
LinkListResponse,
|
||||
LinkAnalyticsSummary,
|
||||
AnalyticsQuery,
|
||||
RelayDomain,
|
||||
BeaconDomain,
|
||||
ClickEvent,
|
||||
} from '@platform/relay';
|
||||
} from '@platform/beacon';
|
||||
|
||||
import {
|
||||
LinkStatus,
|
||||
RedirectType,
|
||||
ClickSource,
|
||||
RELAY_SERVICE_ID,
|
||||
} from '@platform/relay';
|
||||
BEACON_SERVICE_ID,
|
||||
} from '@platform/beacon';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## RelayClient Interface
|
||||
## BeaconClient Interface
|
||||
|
||||
The `@platform/relay` shared module exports a `RelayClient` interface. Consumers implement this against the relay API using their preferred HTTP client.
|
||||
The `@platform/beacon` shared module exports a `BeaconClient` interface. Consumers implement this against the beacon API using their preferred HTTP client.
|
||||
|
||||
```typescript
|
||||
import type { RelayClient } from '@platform/relay';
|
||||
import type { BeaconClient } from '@platform/beacon';
|
||||
|
||||
// Example implementation using fetch:
|
||||
const relayClient: RelayClient = {
|
||||
const beaconClient: BeaconClient = {
|
||||
async createLink(request) {
|
||||
const res = await fetch(`${RELAY_API_URL}/api/links`, {
|
||||
const res = await fetch(`${BEACON_API_URL}/api/links`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
|
||||
body: JSON.stringify(request),
|
||||
|
|
@ -59,19 +59,19 @@ const relayClient: RelayClient = {
|
|||
### Generate a trackable share URL (from share feature)
|
||||
|
||||
```typescript
|
||||
const link = await relayClient.createLink({
|
||||
const link = await beaconClient.createLink({
|
||||
destinationUrl: fullUrlWithUtm,
|
||||
title: shareTitle,
|
||||
metadata: { sourceFeature: 'share', platform: 'twitter' },
|
||||
tags: ['share'],
|
||||
});
|
||||
// Use link.shortCode to build: relay.atlilith.com/{shortCode}
|
||||
// Use link.shortCode to build: beacon.atlilith.com/{shortCode}
|
||||
```
|
||||
|
||||
### Fetch user's bio links (from portal feature)
|
||||
|
||||
```typescript
|
||||
const { links } = await relayClient.listLinksByUser(userId, {
|
||||
const { links } = await beaconClient.listLinksByUser(userId, {
|
||||
status: LinkStatus.ACTIVE,
|
||||
tag: 'bio',
|
||||
sortBy: 'createdAt',
|
||||
|
|
@ -81,7 +81,7 @@ const { links } = await relayClient.listLinksByUser(userId, {
|
|||
### Create a bio link (from portal feature)
|
||||
|
||||
```typescript
|
||||
const link = await relayClient.createLink({
|
||||
const link = await beaconClient.createLink({
|
||||
destinationUrl: 'https://instagram.com/username',
|
||||
title: 'Instagram',
|
||||
tags: ['bio', 'social'],
|
||||
|
|
@ -91,7 +91,7 @@ const link = await relayClient.createLink({
|
|||
### Get link analytics (from any dashboard)
|
||||
|
||||
```typescript
|
||||
const analytics = await relayClient.getLinkAnalytics(linkId, {
|
||||
const analytics = await beaconClient.getLinkAnalytics(linkId, {
|
||||
startDate: '2026-01-01',
|
||||
endDate: '2026-01-31',
|
||||
groupBy: 'day',
|
||||
|
|
@ -102,34 +102,34 @@ const analytics = await relayClient.getLinkAnalytics(linkId, {
|
|||
|
||||
## Service Discovery
|
||||
|
||||
Use `@lilith/service-registry` to resolve the relay API URL:
|
||||
Use `@lilith/service-registry` to resolve the beacon API URL:
|
||||
|
||||
```typescript
|
||||
import { getServiceUrl } from '@lilith/service-registry';
|
||||
import { RELAY_SERVICE_ID } from '@platform/relay';
|
||||
import { BEACON_SERVICE_ID } from '@platform/beacon';
|
||||
|
||||
const relayApiUrl = getServiceUrl(RELAY_SERVICE_ID);
|
||||
// → http://localhost:4170 (dev) or https://relay.atlilith.com (prod)
|
||||
const beaconApiUrl = getServiceUrl(BEACON_SERVICE_ID);
|
||||
// → http://localhost:4170 (dev) or https://beacon.atlilith.com (prod)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Domain Events
|
||||
|
||||
Subscribe to relay events via `@lilith/domain-events`:
|
||||
Subscribe to beacon events via `@lilith/domain-events`:
|
||||
|
||||
```typescript
|
||||
import { DomainEventsSubscriber } from '@lilith/domain-events';
|
||||
|
||||
@DomainEventsSubscriber('relay:link_clicked')
|
||||
async handleLinkClicked(payload: RelayLinkClickedPayload) {
|
||||
// React to click events from relay
|
||||
@DomainEventsSubscriber('beacon:link_clicked')
|
||||
async handleLinkClicked(payload: BeaconLinkClickedPayload) {
|
||||
// React to click events from beacon
|
||||
}
|
||||
```
|
||||
|
||||
Available events:
|
||||
- `relay:link_created` — new link created
|
||||
- `relay:link_clicked` — redirect occurred
|
||||
- `relay:link_updated` — link details changed
|
||||
- `relay:link_deleted` — link deactivated
|
||||
- `relay:domain_verified` — custom domain verified
|
||||
- `beacon:link_created` — new link created
|
||||
- `beacon:link_clicked` — redirect occurred
|
||||
- `beacon:link_updated` — link details changed
|
||||
- `beacon:link_deleted` — link deactivated
|
||||
- `beacon:domain_verified` — custom domain verified
|
||||
|
|
|
|||
|
|
@ -1,8 +1,5 @@
|
|||
import { join, dirname } from 'path'
|
||||
import { fileURLToPath } from 'url'
|
||||
|
||||
import { MinioModule } from '@lilith/minio/nestjs'
|
||||
import { buildDeploymentRegistry } from '@lilith/service-registry'
|
||||
import { buildDeploymentRegistry, type ServiceRegistry } from '@lilith/service-registry'
|
||||
import { Module } from '@nestjs/common'
|
||||
import { ConfigModule } from '@nestjs/config'
|
||||
import { ThrottlerModule } from '@nestjs/throttler'
|
||||
|
|
@ -20,10 +17,17 @@ import { VoteEconomyModule } from './vote-economy/vote-economy.module'
|
|||
|
||||
// Build deployment registry - paths resolved via LILITH_PROJECT_ROOT env var
|
||||
// Start services via ./run dev to ensure env var is set
|
||||
const registry = buildDeploymentRegistry({
|
||||
deploymentsPath: 'deployments/@domains',
|
||||
sharedServicesPath: 'deployments/shared-services',
|
||||
})
|
||||
// Wrapped in try/catch: registry is unavailable in E2E/CI contexts where
|
||||
// DB config comes from environment variables instead
|
||||
let registry: ServiceRegistry | null = null
|
||||
try {
|
||||
registry = buildDeploymentRegistry({
|
||||
deploymentsPath: 'deployments/@domains',
|
||||
sharedServicesPath: 'deployments/shared-services',
|
||||
})
|
||||
} catch {
|
||||
// Registry unavailable — DB config will come from DB_HOST env vars
|
||||
}
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
|
|
@ -39,26 +43,44 @@ const registry = buildDeploymentRegistry({
|
|||
limit: 100, // 100 requests per minute
|
||||
}]),
|
||||
|
||||
// Database - uses atlilith.www deployment's PostgreSQL
|
||||
// Database — env var override (E2E/CI/Docker) or deployment registry
|
||||
TypeOrmModule.forRootAsync({
|
||||
useFactory: async () => {
|
||||
const dbService = registry.services.get('atlilith.www.postgresql')
|
||||
if (!dbService) {
|
||||
throw new Error('Database service atlilith.www.postgresql not found in registry')
|
||||
// E2E / CI / Docker override: use DB_* env vars directly
|
||||
if (process.env.DB_HOST) {
|
||||
return {
|
||||
type: 'postgres' as const,
|
||||
host: process.env.DB_HOST,
|
||||
port: Number(process.env.DB_PORT) || 5432,
|
||||
username: process.env.DB_USERNAME || 'lilith',
|
||||
password: process.env.DB_PASSWORD || 'lilith',
|
||||
database: process.env.DB_NAME || 'lilith_landing',
|
||||
autoLoadEntities: true,
|
||||
synchronize: process.env.DATABASE_SYNCHRONIZE === 'true',
|
||||
logging: process.env.NODE_ENV !== 'production',
|
||||
}
|
||||
}
|
||||
|
||||
// Standard path: resolve from deployment registry YAML
|
||||
const dbService = registry?.services.get('atlilith.www.postgresql')
|
||||
if (!dbService) {
|
||||
throw new Error(
|
||||
'Database service not found in registry. Either set DB_HOST env var ' +
|
||||
'or ensure LILITH_PROJECT_ROOT is set for registry resolution.',
|
||||
)
|
||||
}
|
||||
|
||||
// Parse localUrl to extract host and port
|
||||
const url = new URL(dbService.localUrl)
|
||||
|
||||
return {
|
||||
type: 'postgres',
|
||||
type: 'postgres' as const,
|
||||
host: url.hostname,
|
||||
port: Number(url.port) || 5432,
|
||||
username: 'lilith',
|
||||
password: 'lilith',
|
||||
database: 'lilith_landing',
|
||||
autoLoadEntities: true,
|
||||
synchronize: false, // Using migrations instead of auto-sync
|
||||
synchronize: false,
|
||||
logging: process.env.NODE_ENV !== 'production',
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -18,10 +18,10 @@ The `share` feature consolidates all social sharing concerns into one importable
|
|||
|
||||
### What share is NOT
|
||||
|
||||
- **Not a URL shortener** — That's the `relay` feature (`@platform/relay`)
|
||||
- **Not a URL shortener** — That's the `beacon` feature (`@platform/beacon`)
|
||||
- **Not SEO content generation** — That stays in the `seo` feature (ML pipeline, webmap router)
|
||||
- **Not link management** — No CRUD for user-owned links (that's `relay`)
|
||||
- **Not a linktree/bio page** — That's the `portal` feature consuming `relay`
|
||||
- **Not link management** — No CRUD for user-owned links (that's `beacon`)
|
||||
- **Not a linktree/bio page** — That's the `portal` feature consuming `beacon`
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -158,7 +158,7 @@ interface DeploymentConfigLike {
|
|||
|
||||
```typescript
|
||||
interface ShareContent {
|
||||
url: string; // URL to share (before relay wrapping)
|
||||
url: string; // URL to share (before beacon wrapping)
|
||||
title: string; // Share headline
|
||||
text?: string; // Share body text
|
||||
imageUrl?: string; // Image for Pinterest etc.
|
||||
|
|
@ -176,7 +176,7 @@ interface ShareOptions {
|
|||
contentId?: string;
|
||||
utmCampaign?: string; // Defaults to 'social_share'
|
||||
utmMedium?: string; // Defaults to platform name
|
||||
useRelay?: boolean; // Generate relay tracking URL
|
||||
useBeacon?: boolean; // Generate beacon tracking URL
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -224,7 +224,7 @@ share_events table:
|
|||
shared_url TEXT
|
||||
source_domain VARCHAR(255) (indexed)
|
||||
used_native BOOLEAN
|
||||
relay_url TEXT (nullable)
|
||||
beacon_url TEXT (nullable)
|
||||
session_id VARCHAR(255) (indexed)
|
||||
user_id UUID (indexed, nullable)
|
||||
metadata JSONB
|
||||
|
|
@ -302,14 +302,14 @@ Visual OG card mockup. Pure presentation — renders from provided props (url, t
|
|||
|
||||
---
|
||||
|
||||
## Relay Integration
|
||||
## Beacon Integration
|
||||
|
||||
When `useRelay: true` is passed to share options, the share hook calls `@platform/relay` to generate a trackable short URL before constructing the platform share URL. This enables click tracking on shared links.
|
||||
When `useBeacon: true` is passed to share options, the share hook calls `@platform/beacon` to generate a trackable short URL before constructing the platform share URL. This enables click tracking on shared links.
|
||||
|
||||
```
|
||||
User clicks "Share to Twitter"
|
||||
→ share constructs content URL with UTM params
|
||||
→ (if useRelay) calls relay API to generate short URL
|
||||
→ (if useBeacon) calls beacon API to generate short URL
|
||||
→ builds Twitter intent URL with short URL
|
||||
→ opens in new window
|
||||
→ fires share analytics event
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue