@linkforty/core 1.4.4 → 1.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -3,9 +3,9 @@
3
3
 
4
4
  # LinkForty Core
5
5
 
6
- **Open-source deeplink management engine with device detection and analytics**
6
+ **Open-source deeplink framework with device detection, analytics, and smart routing**
7
7
 
8
- LinkForty Core is a powerful, self-hosted deeplink management system that enables you to create, manage, and track smart links with device-specific routing, analytics, and UTM parameter support. It's the open-source foundation of the LinkForty platform.
8
+ LinkForty Core is a TypeScript deeplink framework built on Fastify + PostgreSQL. It provides the engine for creating, managing, and tracking smart links with device-specific routing, targeting rules, QR codes, deferred deep linking, and webhook integrations. Auth, UI, and user management are not included — bring your own, or use separate packages like `@linkforty/ui`.
9
9
  </div>
10
10
 
11
11
  [![npm version](https://img.shields.io/npm/v/@linkforty/core.svg)](https://www.npmjs.com/package/@linkforty/core)
@@ -13,18 +13,25 @@
13
13
  [![codecov](https://codecov.io/gh/linkforty/core/branch/main/graph/badge.svg)](https://codecov.io/gh/linkforty/core)
14
14
  [![Docker Pulls](https://img.shields.io/docker/pulls/linkforty/core)](https://hub.docker.com/r/linkforty/core)
15
15
  [![Docker Image Size](https://img.shields.io/docker/image-size/linkforty/core/latest)](https://hub.docker.com/r/linkforty/core)
16
- [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
17
-
18
- ## Features
19
-
20
- **Smart Link Routing** - Create short links with device-specific URLs for iOS, Android, and web \
21
- **Device Detection** - Automatic detection and routing based on user device \
22
- **Click Analytics** - Track clicks with geolocation, device type, platform, and more \
23
- **UTM Parameters** - Built-in support for UTM campaign tracking \
24
- **Link Expiration** - Set expiration dates for time-sensitive links \
25
- **Redis Caching** - Optional Redis support for high-performance link lookups \
26
- **PostgreSQL Storage** - Reliable data persistence with full SQL capabilities \
27
- **TypeScript** - Fully typed API for better developer experience
16
+ [![License: AGPL v3](https://img.shields.io/badge/License-AGPL_v3-blue.svg)](https://www.gnu.org/licenses/agpl-3.0)
17
+
18
+ ## Features
19
+
20
+ **Smart Link Routing** - Create short links with device-specific URLs for iOS, Android, and web \
21
+ **Device Detection** - Automatic detection and routing based on user device \
22
+ **Click Analytics** - Track clicks with geolocation, device type, platform, and more \
23
+ **UTM Parameters** - Built-in support for UTM campaign tracking \
24
+ **Targeting Rules** - Filter by country, device, and language before redirecting \
25
+ **QR Code Generation** - Generate QR codes (PNG/SVG) for any link \
26
+ **Deferred Deep Linking** - Probabilistic fingerprint matching for install attribution \
27
+ **Webhooks** - Event-driven integrations with HMAC-signed payloads and retry logic \
28
+ **OG Preview Pages** - Social media scraper detection with Open Graph meta tags \
29
+ **iOS Universal Links & Android App Links** - Serve `.well-known` files automatically \
30
+ **Link Expiration** - Set expiration dates for time-sensitive links \
31
+ **Redis Caching** - Optional Redis support for high-performance link lookups \
32
+ **PostgreSQL Storage** - Reliable data persistence with full SQL capabilities \
33
+ **TypeScript** - Fully typed API for better developer experience \
34
+ **No Auth Included** - Bring your own authentication; `userId` is optional for multi-tenant scoping
28
35
 
29
36
  ## Installation
30
37
 
@@ -81,17 +88,21 @@ docker run -d \
81
88
  ```
82
89
 
83
90
  **Features:**
84
- - Pre-built multi-architecture images (AMD64 + ARM64)
85
- - Automatic updates with version tags
86
- - Non-root user for security
87
- - Built-in health checks
88
- - Supply chain attestations (SBOM + Provenance)
91
+ - Pre-built multi-architecture images (AMD64 + ARM64)
92
+ - Automatic updates with version tags
93
+ - Non-root user for security
94
+ - Built-in health checks
95
+ - Supply chain attestations (SBOM + Provenance)
89
96
 
90
97
  See [DOCKER.md](DOCKER.md) for complete deployment guide.
91
98
 
92
99
  ## API Reference
93
100
 
94
- ### Create a Link
101
+ ### Links
102
+
103
+ #### Create a Link
104
+
105
+ `userId` is optional. When provided, the link is scoped to that user (multi-tenant mode). When omitted, the link has no owner (single-tenant mode).
95
106
 
96
107
  ```bash
97
108
  POST /api/links
@@ -101,57 +112,54 @@ Content-Type: application/json
101
112
  "userId": "user-uuid",
102
113
  "originalUrl": "https://example.com",
103
114
  "title": "My Link",
104
- "iosUrl": "myapp://product/123",
105
- "androidUrl": "myapp://product/123",
115
+ "description": "Summer campaign link",
116
+ "iosAppStoreUrl": "https://apps.apple.com/app/id123456",
117
+ "androidAppStoreUrl": "https://play.google.com/store/apps/details?id=com.example",
106
118
  "webFallbackUrl": "https://example.com/product/123",
119
+ "appScheme": "myapp",
120
+ "iosUniversalLink": "https://example.com/app/product/123",
121
+ "androidAppLink": "https://example.com/app/product/123",
122
+ "deepLinkPath": "/product/123",
123
+ "deepLinkParameters": { "ref": "campaign-1" },
107
124
  "utmParameters": {
108
125
  "source": "twitter",
109
126
  "medium": "social",
110
127
  "campaign": "summer-sale"
111
128
  },
129
+ "ogTitle": "Check out this deal",
130
+ "ogDescription": "50% off summer sale",
131
+ "ogImageUrl": "https://example.com/og-image.png",
132
+ "targetingRules": {
133
+ "countries": ["US", "CA"],
134
+ "devices": ["ios", "android"],
135
+ "languages": ["en"]
136
+ },
137
+ "attributionWindowHours": 168,
112
138
  "customCode": "summer-sale",
113
- "expiresAt": "2024-12-31T23:59:59Z"
139
+ "expiresAt": "2026-12-31T23:59:59Z"
114
140
  }
115
141
  ```
116
142
 
117
- **Response:**
118
-
119
- ```json
120
- {
121
- "id": "link-uuid",
122
- "userId": "user-uuid",
123
- "short_code": "summer-sale",
124
- "original_url": "https://example.com",
125
- "title": "My Link",
126
- "ios_url": "myapp://product/123",
127
- "android_url": "myapp://product/123",
128
- "web_fallback_url": "https://example.com/product/123",
129
- "utmParameters": {
130
- "source": "twitter",
131
- "medium": "social",
132
- "campaign": "summer-sale"
133
- },
134
- "is_active": true,
135
- "expires_at": "2024-12-31T23:59:59Z",
136
- "created_at": "2024-01-01T00:00:00Z",
137
- "updated_at": "2024-01-01T00:00:00Z",
138
- "clickCount": 0
139
- }
140
- ```
143
+ All fields except `originalUrl` are optional.
141
144
 
142
- ### Get All Links
145
+ #### Get All Links
143
146
 
144
147
  ```bash
148
+ # Single-tenant (all links)
149
+ GET /api/links
150
+
151
+ # Multi-tenant (scoped to user)
145
152
  GET /api/links?userId=user-uuid
146
153
  ```
147
154
 
148
- ### Get a Specific Link
155
+ #### Get a Specific Link
149
156
 
150
157
  ```bash
158
+ GET /api/links/:id
151
159
  GET /api/links/:id?userId=user-uuid
152
160
  ```
153
161
 
154
- ### Update a Link
162
+ #### Update a Link
155
163
 
156
164
  ```bash
157
165
  PUT /api/links/:id?userId=user-uuid
@@ -163,62 +171,99 @@ Content-Type: application/json
163
171
  }
164
172
  ```
165
173
 
166
- ### Delete a Link
174
+ #### Duplicate a Link
175
+
176
+ ```bash
177
+ POST /api/links/:id/duplicate?userId=user-uuid
178
+ ```
179
+
180
+ #### Delete a Link
167
181
 
168
182
  ```bash
169
183
  DELETE /api/links/:id?userId=user-uuid
170
184
  ```
171
185
 
172
- ### Get Analytics Overview
186
+ ### Analytics
187
+
188
+ #### Get Analytics Overview
173
189
 
174
190
  ```bash
191
+ # All links
192
+ GET /api/analytics/overview?days=30
193
+
194
+ # Scoped to user
175
195
  GET /api/analytics/overview?userId=user-uuid&days=30
176
196
  ```
177
197
 
178
- **Response:**
198
+ Returns: `totalClicks`, `uniqueClicks`, `clicksByDate`, `clicksByCountry`, `clicksByDevice`, `clicksByPlatform`, `topLinks`
179
199
 
180
- ```json
181
- {
182
- "totalClicks": 1234,
183
- "uniqueClicks": 567,
184
- "clicksByDate": [
185
- { "date": "2024-01-01", "clicks": 45 }
186
- ],
187
- "clicksByCountry": [
188
- { "countryCode": "US", "country": "United States", "clicks": 234 }
189
- ],
190
- "clicksByDevice": [
191
- { "device": "mobile", "clicks": 789 }
192
- ],
193
- "clicksByPlatform": [
194
- { "platform": "iOS", "clicks": 456 }
195
- ],
196
- "topLinks": [
197
- {
198
- "id": "link-uuid",
199
- "shortCode": "summer-sale",
200
- "title": "My Link",
201
- "originalUrl": "https://example.com",
202
- "totalClicks": 123,
203
- "uniqueClicks": 67
204
- }
205
- ]
206
- }
200
+ #### Get Link-Specific Analytics
201
+
202
+ ```bash
203
+ GET /api/analytics/links/:linkId?days=30
204
+ ```
205
+
206
+ ### Redirect
207
+
208
+ ```bash
209
+ GET /:shortCode
210
+ GET /:templateSlug/:shortCode
207
211
  ```
208
212
 
209
- ### Get Link-Specific Analytics
213
+ Automatically redirects users to the appropriate URL based on device type (iOS/Android/web), evaluates targeting rules, and tracks the click asynchronously.
214
+
215
+ ### QR Codes
210
216
 
211
217
  ```bash
212
- GET /api/analytics/links/:linkId?userId=user-uuid&days=30
218
+ GET /api/links/:id/qr?format=png&size=300
219
+ GET /api/links/:id/qr?format=svg
213
220
  ```
214
221
 
215
- ### Redirect Short Link
222
+ ### Webhooks
216
223
 
217
224
  ```bash
218
- GET /:shortCode
225
+ GET /api/webhooks?userId=user-uuid
226
+ POST /api/webhooks # Body: { name, url, events, userId? }
227
+ GET /api/webhooks/:id?userId=user-uuid
228
+ PUT /api/webhooks/:id?userId=user-uuid
229
+ DELETE /api/webhooks/:id?userId=user-uuid
230
+ POST /api/webhooks/:id/test?userId=user-uuid
231
+ ```
232
+
233
+ Events: `click_event`, `install_event`, `conversion_event`. Payloads are HMAC SHA-256 signed.
234
+
235
+ ### Mobile SDK Endpoints
236
+
237
+ ```bash
238
+ POST /api/sdk/v1/install # Report app install, get deferred deep link
239
+ GET /api/sdk/v1/attribution/:fingerprint # Debug attribution lookups
240
+ POST /api/sdk/v1/event # Track in-app conversion events
241
+ GET /api/sdk/v1/resolve/:shortCode # Resolve link to deep link data (no redirect)
242
+ GET /api/sdk/v1/health # Health check
243
+ ```
244
+
245
+ ### Debug & Testing
246
+
247
+ ```bash
248
+ POST /api/debug/simulate # Simulate a link click with custom parameters
249
+ WS /api/debug/live?userId=user-uuid # WebSocket live click event stream
250
+ GET /api/debug/user-agents # Common UA strings for testing
251
+ GET /api/debug/countries # Common countries list
252
+ GET /api/debug/languages # Common languages list
219
253
  ```
220
254
 
221
- This endpoint automatically redirects users to the appropriate URL based on their device type.
255
+ ### Well-Known Routes
256
+
257
+ ```bash
258
+ GET /.well-known/apple-app-site-association # iOS Universal Links
259
+ GET /.well-known/assetlinks.json # Android App Links
260
+ ```
261
+
262
+ ### OG Preview
263
+
264
+ ```bash
265
+ GET /:shortCode/preview # OG meta tag page for social scrapers
266
+ ```
222
267
 
223
268
  ## Configuration
224
269
 
@@ -251,39 +296,50 @@ REDIS_URL=redis://localhost:6379
251
296
  PORT=3000
252
297
  NODE_ENV=production
253
298
  CORS_ORIGIN=*
299
+
300
+ # Mobile SDK (optional — for iOS Universal Links and Android App Links)
301
+ IOS_TEAM_ID=ABC123XYZ
302
+ IOS_BUNDLE_ID=com.yourcompany.yourapp
303
+ ANDROID_PACKAGE_NAME=com.yourcompany.yourapp
304
+ ANDROID_SHA256_FINGERPRINTS=AA:BB:CC:DD:...
305
+
306
+ # Custom domain for QR code URLs (optional)
307
+ SHORTLINK_DOMAIN=yourdomain.com
254
308
  ```
255
309
 
256
310
  ## Database Schema
257
311
 
258
- ### Users Table
259
-
260
- | Column | Type | Description |
261
- |---------------|--------------|-----------------------|
262
- | id | UUID | Primary key |
263
- | email | VARCHAR(255) | Unique email |
264
- | name | VARCHAR(255) | User name |
265
- | password_hash | VARCHAR(255) | Hashed password |
266
- | created_at | TIMESTAMP | Creation timestamp |
267
- | updated_at | TIMESTAMP | Last update timestamp |
312
+ Core does not create a `users` table. Authentication and user management are the consumer's responsibility. The `user_id` column on `links` and `webhooks` is optional (nullable, no foreign key) — use it for multi-tenant scoping when your auth layer provides a user identity.
268
313
 
269
314
  ### Links Table
270
315
 
271
- | Column | Type | Description |
272
- |------------------|--------------|-----------------------|
273
- | id | UUID | Primary key |
274
- | user_id | UUID | Foreign key to users |
275
- | short_code | VARCHAR(20) | Unique short code |
276
- | original_url | TEXT | Original URL |
277
- | title | VARCHAR(255) | Link title |
278
- | ios_url | TEXT | iOS-specific URL |
279
- | android_url | TEXT | Android-specific URL |
280
- | web_fallback_url | TEXT | Web fallback URL |
281
- | utm_parameters | JSONB | UTM parameters |
282
- | targeting_rules | JSONB | Targeting rules |
283
- | is_active | BOOLEAN | Active status |
284
- | expires_at | TIMESTAMP | Expiration date |
285
- | created_at | TIMESTAMP | Creation timestamp |
286
- | updated_at | TIMESTAMP | Last update timestamp |
316
+ | Column | Type | Description |
317
+ |-------------------------|--------------|------------------------------------------|
318
+ | id | UUID | Primary key |
319
+ | user_id | UUID | Optional owner/tenant identifier |
320
+ | short_code | VARCHAR(20) | Unique short code |
321
+ | original_url | TEXT | Original URL |
322
+ | title | VARCHAR(255) | Link title |
323
+ | description | TEXT | Link description |
324
+ | ios_app_store_url | TEXT | iOS App Store URL |
325
+ | android_app_store_url | TEXT | Android Play Store URL |
326
+ | web_fallback_url | TEXT | Web fallback URL |
327
+ | app_scheme | VARCHAR(255) | URI scheme (e.g., "myapp") |
328
+ | ios_universal_link | TEXT | iOS Universal Link URL |
329
+ | android_app_link | TEXT | Android App Link URL |
330
+ | deep_link_path | TEXT | In-app destination path |
331
+ | deep_link_parameters | JSONB | Custom app parameters |
332
+ | utm_parameters | JSONB | UTM tracking parameters |
333
+ | targeting_rules | JSONB | Country/device/language targeting |
334
+ | og_title | VARCHAR(255) | Open Graph title |
335
+ | og_description | TEXT | Open Graph description |
336
+ | og_image_url | TEXT | Open Graph image URL |
337
+ | og_type | VARCHAR(50) | Open Graph type (default: "website") |
338
+ | attribution_window_hours| INTEGER | Install attribution window (default: 168)|
339
+ | is_active | BOOLEAN | Active status |
340
+ | expires_at | TIMESTAMP | Expiration date |
341
+ | created_at | TIMESTAMP | Creation timestamp |
342
+ | updated_at | TIMESTAMP | Last update timestamp |
287
343
 
288
344
  ### Click Events Table
289
345
 
@@ -294,7 +350,7 @@ CORS_ORIGIN=*
294
350
  | clicked_at | TIMESTAMP | Click timestamp |
295
351
  | ip_address | INET | User IP address |
296
352
  | user_agent | TEXT | User agent string |
297
- | device_type | VARCHAR(20) | Device type (mobile/desktop) |
353
+ | device_type | VARCHAR(20) | Device type (ios/android/web)|
298
354
  | platform | VARCHAR(20) | Platform (iOS/Android/Web) |
299
355
  | country_code | CHAR(2) | Country code |
300
356
  | country_name | VARCHAR(100) | Country name |
@@ -308,6 +364,68 @@ CORS_ORIGIN=*
308
364
  | utm_campaign | VARCHAR(255) | UTM campaign |
309
365
  | referrer | TEXT | Referrer URL |
310
366
 
367
+ ### Device Fingerprints Table
368
+
369
+ | Column | Type | Description |
370
+ |------------------|-------------|-------------------------------------|
371
+ | id | UUID | Primary key |
372
+ | click_id | UUID | Foreign key to click_events |
373
+ | fingerprint_hash | VARCHAR(64) | SHA-256 hash of fingerprint signals |
374
+ | ip_address | INET | IP address |
375
+ | user_agent | TEXT | User agent string |
376
+ | timezone | VARCHAR(100)| Timezone |
377
+ | language | VARCHAR(10) | Browser language |
378
+ | screen_width | INTEGER | Screen width |
379
+ | screen_height | INTEGER | Screen height |
380
+ | platform | VARCHAR(50) | Platform |
381
+ | platform_version | VARCHAR(50) | Platform version |
382
+ | created_at | TIMESTAMP | Creation timestamp |
383
+
384
+ ### Install Events Table
385
+
386
+ | Column | Type | Description |
387
+ |-------------------------|-------------|------------------------------------|
388
+ | id | UUID | Primary key |
389
+ | link_id | UUID | Attributed link (nullable) |
390
+ | click_id | UUID | Attributed click (nullable) |
391
+ | fingerprint_hash | VARCHAR(64) | Device fingerprint hash |
392
+ | confidence_score | DECIMAL | Match confidence (0-100) |
393
+ | installed_at | TIMESTAMP | Install timestamp |
394
+ | first_open_at | TIMESTAMP | First app open |
395
+ | deep_link_retrieved | BOOLEAN | Whether deferred link was fetched |
396
+ | deep_link_data | JSONB | Deferred deep link data |
397
+ | attribution_window_hours| INTEGER | Attribution window used (default: 168) |
398
+ | device_id | VARCHAR(255)| Optional device identifier |
399
+ | created_at | TIMESTAMP | Creation timestamp |
400
+
401
+ ### In-App Events Table
402
+
403
+ | Column | Type | Description |
404
+ |-----------------|--------------|------------------------------|
405
+ | id | UUID | Primary key |
406
+ | install_id | UUID | Foreign key to install_events|
407
+ | event_name | VARCHAR(255) | Event name |
408
+ | event_data | JSONB | Custom event properties |
409
+ | event_timestamp | TIMESTAMP | When the event occurred |
410
+ | created_at | TIMESTAMP | Creation timestamp |
411
+
412
+ ### Webhooks Table
413
+
414
+ | Column | Type | Description |
415
+ |------------|--------------|----------------------------------|
416
+ | id | UUID | Primary key |
417
+ | user_id | UUID | Optional owner/tenant identifier |
418
+ | name | VARCHAR(255) | Webhook name |
419
+ | url | TEXT | Delivery URL |
420
+ | secret | VARCHAR(255) | HMAC signing secret |
421
+ | events | TEXT[] | Subscribed event types |
422
+ | is_active | BOOLEAN | Active status |
423
+ | retry_count| INTEGER | Max retries (default: 3) |
424
+ | timeout_ms | INTEGER | Request timeout (default: 10000) |
425
+ | headers | JSONB | Custom HTTP headers |
426
+ | created_at | TIMESTAMP | Creation timestamp |
427
+ | updated_at | TIMESTAMP | Last update timestamp |
428
+
311
429
  ## Utilities
312
430
 
313
431
  ### Generate Short Code
@@ -389,7 +507,7 @@ await fastify.listen({ port: 3000 });
389
507
 
390
508
  LinkForty can be deployed in multiple ways depending on your needs:
391
509
 
392
- ### 🚀 Production Deployment (Recommended)
510
+ ### Production Deployment (Recommended)
393
511
 
394
512
  Deploy to managed platforms with minimal DevOps overhead:
395
513
 
@@ -399,11 +517,11 @@ Deploy to managed platforms with minimal DevOps overhead:
399
517
  - Auto-scaling and SSL included
400
518
  - Starting at ~$10-15/month
401
519
 
402
- [View Fly.io deployment guide](infra/fly.io/DEPLOYMENT.md)
520
+ [View Fly.io deployment guide](infra/fly.io/DEPLOYMENT.md)
403
521
 
404
522
  See [`infra/`](infra/) directory for all deployment options and platform-specific guides.
405
523
 
406
- ### 🐳 Docker Deployment (Recommended for Self-Hosting)
524
+ ### Docker Deployment (Recommended for Self-Hosting)
407
525
 
408
526
  **Production-ready Docker images available on Docker Hub:**
409
527
 
@@ -424,7 +542,7 @@ docker compose up -d
424
542
  ```yaml
425
543
  services:
426
544
  linkforty:
427
- image: linkforty/core:v1.4.0 # Pin to specific version
545
+ image: linkforty/core:v1.5.0 # Pin to specific version
428
546
  ```
429
547
 
430
548
  See [DOCKER.md](DOCKER.md) for complete deployment guide including:
@@ -456,8 +574,8 @@ See [`infra/CONTRIBUTING.md`](infra/CONTRIBUTING.md) to add support for addition
456
574
 
457
575
  - **Redis caching**: 5-minute TTL on link lookups reduces database queries by 90%
458
576
  - **Database indexes**: Optimized queries for fast link lookups and analytics
459
- - **Async click tracking**: Non-blocking click event logging
460
- - **Connection pooling**: Efficient database connection management
577
+ - **Async click tracking**: Non-blocking click event logging via `setImmediate()`
578
+ - **Connection pooling**: Efficient database connection management (min 2, max 10)
461
579
 
462
580
  ## Security
463
581
 
@@ -465,6 +583,8 @@ See [`infra/CONTRIBUTING.md`](infra/CONTRIBUTING.md) to add support for addition
465
583
  - **Input validation**: Zod schema validation on all inputs
466
584
  - **CORS configuration**: Configurable CORS for API access control
467
585
  - **Link expiration**: Automatic handling of expired links
586
+ - **Webhook signing**: HMAC SHA-256 signed payloads
587
+ - **No auth included**: Core does not include authentication. The optional `userId` parameter provides data scoping but does not verify identity. Add your own auth middleware as needed.
468
588
 
469
589
  ## Mobile SDK Integration
470
590
 
@@ -556,12 +676,12 @@ LinkForty Core supports iOS Universal Links and Android App Links for seamless d
556
676
 
557
677
  ### Available Mobile SDKs
558
678
 
559
- - **React Native**: `npm install @linkforty/react-native-sdk`
560
- - **iOS**: Coming soon
561
- - **Android**: Coming soon
679
+ - **React Native**: `npm install @linkforty/mobile-sdk-react-native`
680
+ - **iOS Native**: Coming soon
681
+ - **Android Native**: Coming soon
562
682
  - **Flutter**: Coming soon
563
683
 
564
- See [SDK Integration Guide](https://docs.linkforty.com/guides/sdk-integration) for detailed documentation.
684
+ See [SDK Integration Guide](https://docs.linkforty.com/sdks/react-native/overview) for detailed documentation.
565
685
 
566
686
  ### Testing Domain Verification
567
687
 
@@ -583,12 +703,13 @@ Contributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for det
583
703
 
584
704
  ## License
585
705
 
586
- MIT License - see [LICENSE](LICENSE) file for details.
706
+ AGPL-3.0 - see [LICENSE](LICENSE) file for details.
587
707
 
588
708
  ## Related Projects
589
709
 
590
710
  - **@linkforty/ui** - React UI components for link management
591
- - **[LinkForty Cloud](https://linkforty.com)** - Hosted SaaS version with additional features
711
+ - **@linkforty/mobile-sdk-react-native** - React Native SDK for deferred deep linking
712
+ - **[LinkForty Cloud](https://linkforty.com)** - Hosted SaaS version with authentication, teams, billing, and more
592
713
 
593
714
  ## Support
594
715
 
@@ -600,8 +721,8 @@ MIT License - see [LICENSE](LICENSE) file for details.
600
721
  - [Fastify](https://www.fastify.io/) - Fast web framework
601
722
  - [PostgreSQL](https://www.postgresql.org/) - Powerful database
602
723
  - [Redis](https://redis.io/) - In-memory cache
724
+ - [Zod](https://zod.dev/) - TypeScript-first schema validation
603
725
  - [nanoid](https://github.com/ai/nanoid) - Unique ID generation
604
726
  - [geoip-lite](https://github.com/geoip-lite/node-geoip) - IP geolocation
605
727
  - [ua-parser-js](https://github.com/faisalman/ua-parser-js) - User agent parsing
606
-
607
-
728
+ - [qrcode](https://github.com/soldair/node-qrcode) - QR code generation
@@ -1 +1 @@
1
- {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/lib/database.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AAIpB,MAAM,WAAW,eAAe;IAC9B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE;QACL,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,GAAG,CAAC,EAAE,MAAM,CAAC;KACd,CAAC;CACH;AAED,eAAO,IAAI,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC;AA6BvB,wBAAsB,kBAAkB,CAAC,OAAO,GAAE,eAAoB,iBAyWrE"}
1
+ {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/lib/database.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AAIpB,MAAM,WAAW,eAAe;IAC9B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE;QACL,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,GAAG,CAAC,EAAE,MAAM,CAAC;KACd,CAAC;CACH;AAED,eAAO,IAAI,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC;AA6BvB,wBAAsB,kBAAkB,CAAC,OAAO,GAAE,eAAoB,iBA2VrE"}
@@ -38,22 +38,11 @@ export async function initializeDatabase(options = {}) {
38
38
  });
39
39
  const client = await connectWithRetry();
40
40
  try {
41
- // Users table
42
- await client.query(`
43
- CREATE TABLE IF NOT EXISTS users (
44
- id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
45
- email VARCHAR(255) UNIQUE NOT NULL,
46
- name VARCHAR(255) NOT NULL,
47
- password_hash VARCHAR(255) NOT NULL,
48
- created_at TIMESTAMP DEFAULT NOW(),
49
- updated_at TIMESTAMP DEFAULT NOW()
50
- )
51
- `);
52
41
  // Links table
53
42
  await client.query(`
54
43
  CREATE TABLE IF NOT EXISTS links (
55
44
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
56
- user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
45
+ user_id UUID,
57
46
  short_code VARCHAR(20) UNIQUE NOT NULL,
58
47
  original_url TEXT NOT NULL,
59
48
  title VARCHAR(255),
@@ -149,7 +138,7 @@ export async function initializeDatabase(options = {}) {
149
138
  await client.query(`
150
139
  CREATE TABLE IF NOT EXISTS webhooks (
151
140
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
152
- user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
141
+ user_id UUID,
153
142
  name VARCHAR(255) NOT NULL,
154
143
  url TEXT NOT NULL,
155
144
  secret VARCHAR(255) NOT NULL,
@@ -328,7 +317,6 @@ export async function initializeDatabase(options = {}) {
328
317
  await client.query('CREATE INDEX IF NOT EXISTS idx_clicks_link_id ON click_events(link_id)');
329
318
  await client.query('CREATE INDEX IF NOT EXISTS idx_clicks_timestamp ON click_events(clicked_at DESC)');
330
319
  await client.query('CREATE INDEX IF NOT EXISTS idx_clicks_link_date ON click_events(link_id, clicked_at DESC)');
331
- await client.query('CREATE INDEX IF NOT EXISTS idx_users_email ON users(email)');
332
320
  // Indexes for deferred deep linking
333
321
  await client.query('CREATE INDEX IF NOT EXISTS idx_fingerprints_hash ON device_fingerprints(fingerprint_hash)');
334
322
  await client.query('CREATE INDEX IF NOT EXISTS idx_fingerprints_click_id ON device_fingerprints(click_id)');
@@ -1 +1 @@
1
- {"version":3,"file":"database.js","sourceRoot":"","sources":["../../src/lib/database.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"database.js","sourceRoot":"","sources":["../../src/lib/database.ts"],"names":[],"mappings":"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"}
@@ -12,7 +12,7 @@ export interface ClickEventData {
12
12
  timestamp: string;
13
13
  linkId: string;
14
14
  shortCode: string;
15
- userId: string;
15
+ userId?: string;
16
16
  organizationId?: string;
17
17
  ipAddress: string;
18
18
  userAgent: string;
@@ -1 +1 @@
1
- {"version":3,"file":"event-emitter.d.ts","sourceRoot":"","sources":["../../src/lib/event-emitter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAEtC;;;GAGG;AACH,eAAO,MAAM,iBAAiB,uBAAqB,CAAC;AAEpD;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,MAAM,CAAC;IAGxB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IAGd,UAAU,EAAE,KAAK,GAAG,SAAS,GAAG,KAAK,CAAC;IACtC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IAGjB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,EAAE,OAAO,CAAC;IAG1B,aAAa,CAAC,EAAE;QACd,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC;IAGF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,cAAc,QAEvD;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,CAAC,SAAS,EAAE,cAAc,KAAK,IAAI,GAC5C,MAAM,IAAI,CAOZ"}
1
+ {"version":3,"file":"event-emitter.d.ts","sourceRoot":"","sources":["../../src/lib/event-emitter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAEtC;;;GAGG;AACH,eAAO,MAAM,iBAAiB,uBAAqB,CAAC;AAEpD;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;IAGxB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IAGd,UAAU,EAAE,KAAK,GAAG,SAAS,GAAG,KAAK,CAAC;IACtC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IAGjB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,EAAE,OAAO,CAAC;IAG1B,aAAa,CAAC,EAAE;QACd,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC;IAGF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,cAAc,QAEvD;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,CAAC,SAAS,EAAE,cAAc,KAAK,IAAI,GAC5C,MAAM,IAAI,CAOZ"}
@@ -1 +1 @@
1
- {"version":3,"file":"analytics.d.ts","sourceRoot":"","sources":["../../src/routes/analytics.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAkB,MAAM,SAAS,CAAC;AAG1D,wBAAsB,eAAe,CAAC,OAAO,EAAE,eAAe,iBAgO7D"}
1
+ {"version":3,"file":"analytics.d.ts","sourceRoot":"","sources":["../../src/routes/analytics.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAkB,MAAM,SAAS,CAAC;AAG1D,wBAAsB,eAAe,CAAC,OAAO,EAAE,eAAe,iBAoO7D"}