@linkforty/core 1.0.0 → 1.2.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.
Files changed (61) hide show
  1. package/dist/index.d.ts +4 -1
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +26 -43
  4. package/dist/index.js.map +1 -1
  5. package/dist/lib/database.d.ts.map +1 -1
  6. package/dist/lib/database.js +160 -12
  7. package/dist/lib/database.js.map +1 -1
  8. package/dist/lib/event-emitter.d.ts +46 -0
  9. package/dist/lib/event-emitter.d.ts.map +1 -0
  10. package/dist/lib/event-emitter.js +24 -0
  11. package/dist/lib/event-emitter.js.map +1 -0
  12. package/dist/lib/fingerprint.d.ts +64 -0
  13. package/dist/lib/fingerprint.d.ts.map +1 -0
  14. package/dist/lib/fingerprint.js +343 -0
  15. package/dist/lib/fingerprint.js.map +1 -0
  16. package/dist/lib/utils.d.ts +1 -0
  17. package/dist/lib/utils.d.ts.map +1 -1
  18. package/dist/lib/utils.js +12 -21
  19. package/dist/lib/utils.js.map +1 -1
  20. package/dist/lib/webhook.d.ts +18 -0
  21. package/dist/lib/webhook.d.ts.map +1 -0
  22. package/dist/lib/webhook.js +141 -0
  23. package/dist/lib/webhook.js.map +1 -0
  24. package/dist/routes/analytics.js +14 -17
  25. package/dist/routes/analytics.js.map +1 -1
  26. package/dist/routes/debug.d.ts +7 -0
  27. package/dist/routes/debug.d.ts.map +1 -0
  28. package/dist/routes/debug.js +318 -0
  29. package/dist/routes/debug.js.map +1 -0
  30. package/dist/routes/index.d.ts +5 -0
  31. package/dist/routes/index.d.ts.map +1 -1
  32. package/dist/routes/index.js +8 -9
  33. package/dist/routes/index.js.map +1 -1
  34. package/dist/routes/links.d.ts.map +1 -1
  35. package/dist/routes/links.js +53 -38
  36. package/dist/routes/links.js.map +1 -1
  37. package/dist/routes/preview.d.ts +3 -0
  38. package/dist/routes/preview.d.ts.map +1 -0
  39. package/dist/routes/preview.js +222 -0
  40. package/dist/routes/preview.js.map +1 -0
  41. package/dist/routes/qr.d.ts +6 -0
  42. package/dist/routes/qr.d.ts.map +1 -0
  43. package/dist/routes/qr.js +130 -0
  44. package/dist/routes/qr.js.map +1 -0
  45. package/dist/routes/redirect.d.ts.map +1 -1
  46. package/dist/routes/redirect.js +142 -22
  47. package/dist/routes/redirect.js.map +1 -1
  48. package/dist/routes/sdk.d.ts +7 -0
  49. package/dist/routes/sdk.d.ts.map +1 -0
  50. package/dist/routes/sdk.js +262 -0
  51. package/dist/routes/sdk.js.map +1 -0
  52. package/dist/routes/webhooks.d.ts +3 -0
  53. package/dist/routes/webhooks.d.ts.map +1 -0
  54. package/dist/routes/webhooks.js +176 -0
  55. package/dist/routes/webhooks.js.map +1 -0
  56. package/dist/scripts/migrate.js +2 -4
  57. package/dist/scripts/migrate.js.map +1 -1
  58. package/dist/types/index.d.ts +81 -0
  59. package/dist/types/index.d.ts.map +1 -1
  60. package/dist/types/index.js +1 -2
  61. package/package.json +11 -7
@@ -1,35 +1,42 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.linkRoutes = linkRoutes;
4
- const zod_1 = require("zod");
5
- const database_js_1 = require("../lib/database.js");
6
- const utils_js_1 = require("../lib/utils.js");
7
- const createLinkSchema = zod_1.z.object({
8
- userId: zod_1.z.string().uuid(),
9
- originalUrl: zod_1.z.string().url(),
10
- title: zod_1.z.string().optional(),
11
- iosUrl: zod_1.z.string().url().optional(),
12
- androidUrl: zod_1.z.string().url().optional(),
13
- webFallbackUrl: zod_1.z.string().url().optional(),
14
- customCode: zod_1.z.string().optional(),
15
- utmParameters: zod_1.z.object({
16
- source: zod_1.z.string().optional(),
17
- medium: zod_1.z.string().optional(),
18
- campaign: zod_1.z.string().optional(),
19
- term: zod_1.z.string().optional(),
20
- content: zod_1.z.string().optional(),
1
+ import { z } from 'zod';
2
+ import { db } from '../lib/database.js';
3
+ import { generateShortCode } from '../lib/utils.js';
4
+ const createLinkSchema = z.object({
5
+ userId: z.string().uuid(),
6
+ originalUrl: z.string().url(),
7
+ title: z.string().optional(),
8
+ description: z.string().optional(),
9
+ iosUrl: z.string().url().optional(),
10
+ androidUrl: z.string().url().optional(),
11
+ webFallbackUrl: z.string().url().optional(),
12
+ customCode: z.string().optional(),
13
+ utmParameters: z.object({
14
+ source: z.string().optional(),
15
+ medium: z.string().optional(),
16
+ campaign: z.string().optional(),
17
+ term: z.string().optional(),
18
+ content: z.string().optional(),
21
19
  }).optional(),
22
- targetingRules: zod_1.z.object({
23
- countries: zod_1.z.array(zod_1.z.string()).optional(),
24
- devices: zod_1.z.array(zod_1.z.enum(['ios', 'android', 'web'])).optional(),
25
- languages: zod_1.z.array(zod_1.z.string()).optional(),
20
+ targetingRules: z.object({
21
+ countries: z.array(z.string()).optional(),
22
+ devices: z.array(z.enum(['ios', 'android', 'web'])).optional(),
23
+ languages: z.array(z.string()).optional(),
26
24
  }).optional(),
27
- expiresAt: zod_1.z.string().datetime().optional(),
25
+ ogTitle: z.string().optional(),
26
+ ogDescription: z.string().optional(),
27
+ ogImageUrl: z.string().url().optional(),
28
+ ogType: z.string().optional(),
29
+ attributionWindowHours: z.number()
30
+ .int('Attribution window must be an integer')
31
+ .min(1, 'Attribution window must be at least 1 hour')
32
+ .max(2160, 'Attribution window must be at most 2160 hours (90 days)')
33
+ .optional(),
34
+ expiresAt: z.string().datetime().optional(),
28
35
  });
29
36
  const updateLinkSchema = createLinkSchema.partial().extend({
30
- isActive: zod_1.z.boolean().optional(),
37
+ isActive: z.boolean().optional(),
31
38
  }).omit({ userId: true });
32
- async function linkRoutes(fastify) {
39
+ export async function linkRoutes(fastify) {
33
40
  // Get all links for a user
34
41
  fastify.get('/api/links', async (request) => {
35
42
  const { userId } = request.query;
@@ -45,7 +52,7 @@ async function linkRoutes(fastify) {
45
52
  GROUP BY l.id
46
53
  ORDER BY l.created_at DESC
47
54
  `;
48
- const result = await database_js_1.db.query(query, [userId]);
55
+ const result = await db.query(query, [userId]);
49
56
  return result.rows.map(row => ({
50
57
  ...row,
51
58
  clickCount: parseInt(row.click_count),
@@ -60,7 +67,7 @@ async function linkRoutes(fastify) {
60
67
  if (!userId) {
61
68
  throw new Error('userId query parameter is required');
62
69
  }
63
- const result = await database_js_1.db.query(`SELECT l.*, COUNT(ce.id) as click_count
70
+ const result = await db.query(`SELECT l.*, COUNT(ce.id) as click_count
64
71
  FROM links l
65
72
  LEFT JOIN click_events ce ON l.id = ce.link_id
66
73
  WHERE l.id = $1 AND l.user_id = $2
@@ -80,34 +87,42 @@ async function linkRoutes(fastify) {
80
87
  fastify.post('/api/links', async (request) => {
81
88
  const data = createLinkSchema.parse(request.body);
82
89
  // Generate short code
83
- let shortCode = data.customCode || (0, utils_js_1.generateShortCode)();
90
+ let shortCode = data.customCode || generateShortCode();
84
91
  // Ensure short code is unique
85
92
  let attempts = 0;
86
93
  while (attempts < 10) {
87
- const existing = await database_js_1.db.query('SELECT id FROM links WHERE short_code = $1', [shortCode]);
94
+ const existing = await db.query('SELECT id FROM links WHERE short_code = $1', [shortCode]);
88
95
  if (existing.rows.length === 0) {
89
96
  break;
90
97
  }
91
- shortCode = (0, utils_js_1.generateShortCode)();
98
+ shortCode = generateShortCode();
92
99
  attempts++;
93
100
  }
94
101
  if (attempts >= 10) {
95
102
  throw new Error('Unable to generate unique short code');
96
103
  }
97
- const result = await database_js_1.db.query(`INSERT INTO links (
98
- user_id, short_code, original_url, title,
99
- ios_url, android_url, web_fallback_url, utm_parameters, targeting_rules, expires_at
100
- ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
104
+ const result = await db.query(`INSERT INTO links (
105
+ user_id, short_code, original_url, title, description,
106
+ ios_url, android_url, web_fallback_url, utm_parameters, targeting_rules,
107
+ og_title, og_description, og_image_url, og_type,
108
+ attribution_window_hours, expires_at
109
+ ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16)
101
110
  RETURNING *`, [
102
111
  data.userId,
103
112
  shortCode,
104
113
  data.originalUrl,
105
114
  data.title || null,
115
+ data.description || null,
106
116
  data.iosUrl || null,
107
117
  data.androidUrl || null,
108
118
  data.webFallbackUrl || null,
109
119
  JSON.stringify(data.utmParameters || {}),
110
120
  JSON.stringify(data.targetingRules || {}),
121
+ data.ogTitle || null,
122
+ data.ogDescription || null,
123
+ data.ogImageUrl || null,
124
+ data.ogType || 'website',
125
+ data.attributionWindowHours || 168, // Default 7 days
111
126
  data.expiresAt || null,
112
127
  ]);
113
128
  const link = result.rows[0];
@@ -149,7 +164,7 @@ async function linkRoutes(fastify) {
149
164
  }
150
165
  updates.push('updated_at = NOW()');
151
166
  values.push(id, userId);
152
- const result = await database_js_1.db.query(`UPDATE links SET ${updates.join(', ')}
167
+ const result = await db.query(`UPDATE links SET ${updates.join(', ')}
153
168
  WHERE id = $${paramIndex} AND user_id = $${paramIndex + 1}
154
169
  RETURNING *`, values);
155
170
  if (result.rows.length === 0) {
@@ -169,7 +184,7 @@ async function linkRoutes(fastify) {
169
184
  if (!userId) {
170
185
  throw new Error('userId query parameter is required');
171
186
  }
172
- const result = await database_js_1.db.query('DELETE FROM links WHERE id = $1 AND user_id = $2 RETURNING id', [id, userId]);
187
+ const result = await db.query('DELETE FROM links WHERE id = $1 AND user_id = $2 RETURNING id', [id, userId]);
173
188
  if (result.rows.length === 0) {
174
189
  throw new Error('Link not found');
175
190
  }
@@ -1 +1 @@
1
- {"version":3,"file":"links.js","sourceRoot":"","sources":["../../src/routes/links.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"links.js","sourceRoot":"","sources":["../../src/routes/links.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { FastifyInstance } from 'fastify';
2
+ export declare function previewRoutes(fastify: FastifyInstance): Promise<void>;
3
+ //# sourceMappingURL=preview.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview.d.ts","sourceRoot":"","sources":["../../src/routes/preview.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AA2J1C,wBAAsB,aAAa,CAAC,OAAO,EAAE,eAAe,iBAgG3D"}
@@ -0,0 +1,222 @@
1
+ import { db } from '../lib/database.js';
2
+ /**
3
+ * Detect if the request is from a social media scraper/bot
4
+ * These bots crawl links to generate previews when shared on social platforms
5
+ */
6
+ function isSocialScraper(userAgent) {
7
+ const scraperPatterns = [
8
+ /facebookexternalhit/i, // Facebook
9
+ /Facebot/i, // Facebook
10
+ /Twitterbot/i, // Twitter
11
+ /LinkedInBot/i, // LinkedIn
12
+ /Slackbot/i, // Slack
13
+ /Discordbot/i, // Discord
14
+ /TelegramBot/i, // Telegram
15
+ /WhatsApp/i, // WhatsApp
16
+ /PinterestBot/i, // Pinterest
17
+ /SkypeUriPreview/i, // Skype
18
+ /Googlebot/i, // Google (for search previews)
19
+ /bingbot/i, // Bing
20
+ /ia_archiver/i, // Alexa
21
+ ];
22
+ return scraperPatterns.some(pattern => pattern.test(userAgent));
23
+ }
24
+ /**
25
+ * Generate HTML preview page with Open Graph meta tags
26
+ */
27
+ function generatePreviewHTML(link, shortUrl, autoRedirect = true) {
28
+ // Use OG-specific values if provided, otherwise fall back to regular title/description
29
+ const ogTitle = link.og_title || link.title || 'Shared Link';
30
+ const ogDescription = link.og_description || link.description || '';
31
+ const ogImage = link.og_image_url || '';
32
+ const ogType = link.og_type || 'website';
33
+ const ogUrl = shortUrl;
34
+ // Auto-redirect after 2 seconds for human visitors
35
+ const metaRefresh = autoRedirect
36
+ ? `<meta http-equiv="refresh" content="2;url=${link.original_url}">`
37
+ : '';
38
+ return `<!DOCTYPE html>
39
+ <html lang="en">
40
+ <head>
41
+ <meta charset="UTF-8">
42
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
43
+ <title>${escapeHtml(ogTitle)}</title>
44
+
45
+ <!-- Open Graph / Facebook -->
46
+ <meta property="og:type" content="${escapeHtml(ogType)}">
47
+ <meta property="og:url" content="${escapeHtml(ogUrl)}">
48
+ <meta property="og:title" content="${escapeHtml(ogTitle)}">
49
+ <meta property="og:description" content="${escapeHtml(ogDescription)}">
50
+ ${ogImage ? `<meta property="og:image" content="${escapeHtml(ogImage)}">` : ''}
51
+ ${ogImage ? `<meta property="og:image:secure_url" content="${escapeHtml(ogImage)}">` : ''}
52
+
53
+ <!-- Twitter -->
54
+ <meta name="twitter:card" content="${ogImage ? 'summary_large_image' : 'summary'}">
55
+ <meta name="twitter:url" content="${escapeHtml(ogUrl)}">
56
+ <meta name="twitter:title" content="${escapeHtml(ogTitle)}">
57
+ <meta name="twitter:description" content="${escapeHtml(ogDescription)}">
58
+ ${ogImage ? `<meta name="twitter:image" content="${escapeHtml(ogImage)}">` : ''}
59
+
60
+ <!-- LinkedIn -->
61
+ <meta property="og:site_name" content="LinkForty">
62
+
63
+ ${metaRefresh}
64
+
65
+ <style>
66
+ body {
67
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
68
+ display: flex;
69
+ align-items: center;
70
+ justify-content: center;
71
+ min-height: 100vh;
72
+ margin: 0;
73
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
74
+ color: white;
75
+ text-align: center;
76
+ padding: 20px;
77
+ }
78
+ .container {
79
+ max-width: 600px;
80
+ }
81
+ h1 {
82
+ font-size: 2.5rem;
83
+ margin-bottom: 1rem;
84
+ }
85
+ p {
86
+ font-size: 1.25rem;
87
+ margin-bottom: 2rem;
88
+ opacity: 0.9;
89
+ }
90
+ .link {
91
+ display: inline-block;
92
+ padding: 12px 24px;
93
+ background: white;
94
+ color: #667eea;
95
+ text-decoration: none;
96
+ border-radius: 8px;
97
+ font-weight: 600;
98
+ transition: transform 0.2s;
99
+ }
100
+ .link:hover {
101
+ transform: translateY(-2px);
102
+ }
103
+ .loader {
104
+ margin: 2rem auto;
105
+ border: 4px solid rgba(255, 255, 255, 0.3);
106
+ border-top: 4px solid white;
107
+ border-radius: 50%;
108
+ width: 40px;
109
+ height: 40px;
110
+ animation: spin 1s linear infinite;
111
+ }
112
+ @keyframes spin {
113
+ 0% { transform: rotate(0deg); }
114
+ 100% { transform: rotate(360deg); }
115
+ }
116
+ </style>
117
+ </head>
118
+ <body>
119
+ <div class="container">
120
+ ${ogImage ? `<img src="${escapeHtml(ogImage)}" alt="${escapeHtml(ogTitle)}" style="max-width: 100%; border-radius: 12px; margin-bottom: 2rem; box-shadow: 0 10px 40px rgba(0, 0, 0, 0.3);">` : ''}
121
+ <h1>${escapeHtml(ogTitle)}</h1>
122
+ ${ogDescription ? `<p>${escapeHtml(ogDescription)}</p>` : ''}
123
+ ${autoRedirect ? '<div class="loader"></div><p>Redirecting you...</p>' : ''}
124
+ <a href="${escapeHtml(link.original_url)}" class="link">
125
+ ${autoRedirect ? 'Click here if not redirected' : 'Continue to destination'}
126
+ </a>
127
+ </div>
128
+ </body>
129
+ </html>`;
130
+ }
131
+ /**
132
+ * Escape HTML to prevent XSS
133
+ */
134
+ function escapeHtml(text) {
135
+ const map = {
136
+ '&': '&amp;',
137
+ '<': '&lt;',
138
+ '>': '&gt;',
139
+ '"': '&quot;',
140
+ "'": '&#039;',
141
+ };
142
+ return text.replace(/[&<>"']/g, (m) => map[m]);
143
+ }
144
+ export async function previewRoutes(fastify) {
145
+ /**
146
+ * GET /:shortCode/preview
147
+ * Always return HTML preview page with Open Graph tags
148
+ * Auto-redirects after 2 seconds for human visitors
149
+ */
150
+ fastify.get('/:shortCode/preview', async (request, reply) => {
151
+ const { shortCode } = request.params;
152
+ const baseUrl = request.headers.host
153
+ ? `${request.protocol}://${request.headers.host}`
154
+ : 'https://link.forty';
155
+ try {
156
+ // Lookup link by short code
157
+ const result = await db.query(`SELECT * FROM links
158
+ WHERE short_code = $1 AND is_active = true
159
+ AND (expires_at IS NULL OR expires_at > NOW())`, [shortCode]);
160
+ if (result.rows.length === 0) {
161
+ return reply.status(404).send('Link not found');
162
+ }
163
+ const link = result.rows[0];
164
+ const shortUrl = `${baseUrl}/${shortCode}`;
165
+ // Return HTML with OG tags and auto-redirect
166
+ const html = generatePreviewHTML(link, shortUrl, true);
167
+ return reply
168
+ .header('Content-Type', 'text/html; charset=utf-8')
169
+ .send(html);
170
+ }
171
+ catch (error) {
172
+ fastify.log.error(`Error generating preview: ${error}`);
173
+ return reply.status(500).send('Error generating preview');
174
+ }
175
+ });
176
+ /**
177
+ * Middleware for /:shortCode route to detect social scrapers
178
+ * If scraper detected, return OG preview instead of redirecting
179
+ *
180
+ * Note: This should be registered BEFORE the main redirect route
181
+ */
182
+ fastify.addHook('preHandler', async (request, reply) => {
183
+ // Only apply to short code routes (not API routes, not preview routes)
184
+ const path = request.url.split('?')[0]; // Remove query string
185
+ if (path.startsWith('/api/') ||
186
+ path.endsWith('/preview') ||
187
+ path === '/' ||
188
+ path.includes('.')) {
189
+ return; // Skip this hook
190
+ }
191
+ const userAgent = request.headers['user-agent'] || '';
192
+ const shortCode = path.split('/').pop() || '';
193
+ const baseUrl = request.headers.host
194
+ ? `${request.protocol}://${request.headers.host}`
195
+ : 'https://link.forty';
196
+ // If it's a social scraper, return OG preview HTML
197
+ if (isSocialScraper(userAgent)) {
198
+ try {
199
+ const result = await db.query(`SELECT * FROM links
200
+ WHERE short_code = $1 AND is_active = true
201
+ AND (expires_at IS NULL OR expires_at > NOW())`, [shortCode]);
202
+ if (result.rows.length > 0) {
203
+ const link = result.rows[0];
204
+ const shortUrl = `${baseUrl}/${shortCode}`;
205
+ // Return HTML with OG tags, no auto-redirect for bots
206
+ const html = generatePreviewHTML(link, shortUrl, false);
207
+ reply
208
+ .header('Content-Type', 'text/html; charset=utf-8')
209
+ .send(html);
210
+ // Stop the request here, don't continue to redirect route
211
+ return reply;
212
+ }
213
+ }
214
+ catch (error) {
215
+ fastify.log.error(`Error in social scraper detection: ${error}`);
216
+ // Continue to normal redirect route on error
217
+ }
218
+ }
219
+ // Not a social scraper, continue to normal redirect logic
220
+ });
221
+ }
222
+ //# sourceMappingURL=preview.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview.js","sourceRoot":"","sources":["../../src/routes/preview.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import { FastifyInstance } from 'fastify';
2
+ /**
3
+ * QR Code Routes - Generate QR codes for links
4
+ */
5
+ export declare function qrRoutes(fastify: FastifyInstance): Promise<void>;
6
+ //# sourceMappingURL=qr.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"qr.d.ts","sourceRoot":"","sources":["../../src/routes/qr.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAI1C;;GAEG;AACH,wBAAsB,QAAQ,CAAC,OAAO,EAAE,eAAe,iBAuItD"}
@@ -0,0 +1,130 @@
1
+ import QRCode from 'qrcode';
2
+ import { db } from '../lib/database.js';
3
+ /**
4
+ * QR Code Routes - Generate QR codes for links
5
+ */
6
+ export async function qrRoutes(fastify) {
7
+ /**
8
+ * GET /api/links/:id/qr
9
+ * Generate QR code for a link
10
+ *
11
+ * Query parameters:
12
+ * - format: 'png' | 'svg' (default: 'png')
13
+ * - size: number 128-2048 (default: 512)
14
+ * - color: hex color for foreground (default: '#000000')
15
+ * - bgcolor: hex color for background (default: '#ffffff')
16
+ *
17
+ * Returns: QR code image (PNG or SVG)
18
+ */
19
+ fastify.get('/api/links/:id/qr', async (request, reply) => {
20
+ const { id } = request.params;
21
+ const query = request.query;
22
+ const format = (query.format || 'png');
23
+ const size = Math.min(Math.max(parseInt(query.size || '512', 10), 128), 2048);
24
+ const color = query.color || '#000000';
25
+ const bgcolor = query.bgcolor || '#ffffff';
26
+ // Validate format
27
+ if (!['png', 'svg'].includes(format)) {
28
+ return reply.status(400).send({ error: 'Invalid format. Use "png" or "svg".' });
29
+ }
30
+ // Build cache key
31
+ const cacheKey = `qr:${id}:${format}:${size}:${color}:${bgcolor}`;
32
+ // Try to get from cache
33
+ if (fastify.redis) {
34
+ try {
35
+ const cached = await fastify.redis.get(cacheKey);
36
+ if (cached) {
37
+ fastify.log.info(`QR code cache hit: ${cacheKey}`);
38
+ if (format === 'png') {
39
+ // Cached PNG is base64
40
+ const buffer = Buffer.from(cached, 'base64');
41
+ return reply
42
+ .type('image/png')
43
+ .header('Cache-Control', 'public, max-age=86400') // 24 hours
44
+ .send(buffer);
45
+ }
46
+ else {
47
+ // Cached SVG is text
48
+ return reply
49
+ .type('image/svg+xml')
50
+ .header('Cache-Control', 'public, max-age=86400')
51
+ .send(cached);
52
+ }
53
+ }
54
+ }
55
+ catch (error) {
56
+ fastify.log.warn('Redis QR cache lookup failed');
57
+ }
58
+ }
59
+ // Get link from database
60
+ const result = await db.query('SELECT short_code, original_url FROM links WHERE id = $1 AND is_active = true', [id]);
61
+ if (result.rows.length === 0) {
62
+ return reply.status(404).send({ error: 'Link not found' });
63
+ }
64
+ const link = result.rows[0];
65
+ // Build short URL (use original_url as fallback if short_code not available)
66
+ // In production, you'd want to use your actual domain
67
+ const shortUrl = link.short_code
68
+ ? `${request.protocol}://${request.hostname}/${link.short_code}`
69
+ : link.original_url;
70
+ try {
71
+ // QR code options
72
+ const options = {
73
+ errorCorrectionLevel: 'M', // Medium error correction
74
+ margin: 1, // Quiet zone margin
75
+ width: size,
76
+ color: {
77
+ dark: color,
78
+ light: bgcolor,
79
+ },
80
+ };
81
+ if (format === 'png') {
82
+ // Generate PNG as buffer
83
+ const buffer = await QRCode.toBuffer(shortUrl, options);
84
+ // Cache as base64
85
+ if (fastify.redis) {
86
+ try {
87
+ await fastify.redis.setex(cacheKey, 86400, buffer.toString('base64')); // 24 hour TTL
88
+ }
89
+ catch (error) {
90
+ fastify.log.warn('Failed to cache QR code');
91
+ }
92
+ }
93
+ return reply
94
+ .type('image/png')
95
+ .header('Cache-Control', 'public, max-age=86400')
96
+ .header('Content-Disposition', `inline; filename="qr-${link.short_code || 'code'}.png"`)
97
+ .send(buffer);
98
+ }
99
+ else {
100
+ // Generate SVG as string
101
+ const svg = await QRCode.toString(shortUrl, {
102
+ ...options,
103
+ type: 'svg',
104
+ });
105
+ // Cache SVG text
106
+ if (fastify.redis) {
107
+ try {
108
+ await fastify.redis.setex(cacheKey, 86400, svg);
109
+ }
110
+ catch (error) {
111
+ fastify.log.warn('Failed to cache QR code');
112
+ }
113
+ }
114
+ return reply
115
+ .type('image/svg+xml')
116
+ .header('Cache-Control', 'public, max-age=86400')
117
+ .header('Content-Disposition', `inline; filename="qr-${link.short_code || 'code'}.svg"`)
118
+ .send(svg);
119
+ }
120
+ }
121
+ catch (error) {
122
+ fastify.log.error(`QR code generation failed: ${error.message}`);
123
+ return reply.status(500).send({
124
+ error: 'Failed to generate QR code',
125
+ message: error.message
126
+ });
127
+ }
128
+ });
129
+ }
130
+ //# sourceMappingURL=qr.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"qr.js","sourceRoot":"","sources":["../../src/routes/qr.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"redirect.d.ts","sourceRoot":"","sources":["../../src/routes/redirect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAI1C,wBAAsB,cAAc,CAAC,OAAO,EAAE,eAAe,iBA4J5D"}
1
+ {"version":3,"file":"redirect.d.ts","sourceRoot":"","sources":["../../src/routes/redirect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAM1C,wBAAsB,cAAc,CAAC,OAAO,EAAE,eAAe,iBAsS5D"}