@linkforty/core 1.0.0
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/LICENSE +21 -0
- package/README.md +459 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +59 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/database.d.ts +11 -0
- package/dist/lib/database.d.ts.map +1 -0
- package/dist/lib/database.js +118 -0
- package/dist/lib/database.js.map +1 -0
- package/dist/lib/utils.d.ts +26 -0
- package/dist/lib/utils.d.ts.map +1 -0
- package/dist/lib/utils.js +119 -0
- package/dist/lib/utils.js.map +1 -0
- package/dist/routes/analytics.d.ts +3 -0
- package/dist/routes/analytics.d.ts.map +1 -0
- package/dist/routes/analytics.js +171 -0
- package/dist/routes/analytics.js.map +1 -0
- package/dist/routes/index.d.ts +4 -0
- package/dist/routes/index.d.ts.map +1 -0
- package/dist/routes/index.js +10 -0
- package/dist/routes/index.js.map +1 -0
- package/dist/routes/links.d.ts +3 -0
- package/dist/routes/links.d.ts.map +1 -0
- package/dist/routes/links.js +179 -0
- package/dist/routes/links.js.map +1 -0
- package/dist/routes/redirect.d.ts +3 -0
- package/dist/routes/redirect.d.ts.map +1 -0
- package/dist/routes/redirect.js +138 -0
- package/dist/routes/redirect.js.map +1 -0
- package/dist/scripts/migrate.d.ts +2 -0
- package/dist/scripts/migrate.d.ts.map +1 -0
- package/dist/scripts/migrate.js +17 -0
- package/dist/scripts/migrate.js.map +1 -0
- package/dist/types/index.d.ts +139 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +3 -0
- package/dist/types/index.js.map +1 -0
- package/package.json +59 -0
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.db = void 0;
|
|
7
|
+
exports.initializeDatabase = initializeDatabase;
|
|
8
|
+
const pg_1 = __importDefault(require("pg"));
|
|
9
|
+
const { Pool } = pg_1.default;
|
|
10
|
+
// Helper function to wait for a specified time
|
|
11
|
+
function sleep(ms) {
|
|
12
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
13
|
+
}
|
|
14
|
+
// Retry database connection with exponential backoff
|
|
15
|
+
async function connectWithRetry(maxRetries = 10, baseDelay = 1000) {
|
|
16
|
+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
17
|
+
try {
|
|
18
|
+
const client = await exports.db.connect();
|
|
19
|
+
console.log('Database connection established successfully');
|
|
20
|
+
return client;
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
if (error.code === 'ECONNREFUSED' && attempt < maxRetries) {
|
|
24
|
+
const delay = baseDelay * Math.pow(2, attempt - 1); // Exponential backoff
|
|
25
|
+
console.log(`Database connection attempt ${attempt} failed. Retrying in ${delay}ms...`);
|
|
26
|
+
await sleep(delay);
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
console.error('Failed to connect to database after all retries:', error);
|
|
30
|
+
throw error;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
throw new Error('Max retries exceeded');
|
|
35
|
+
}
|
|
36
|
+
// Initialize database schema
|
|
37
|
+
async function initializeDatabase(options = {}) {
|
|
38
|
+
// Initialize pool
|
|
39
|
+
exports.db = new Pool({
|
|
40
|
+
connectionString: options.url || process.env.DATABASE_URL || 'postgresql://postgres:password@localhost:5432/linkforty',
|
|
41
|
+
ssl: process.env.NODE_ENV === 'production' ? { rejectUnauthorized: false } : false,
|
|
42
|
+
min: options.pool?.min || 2,
|
|
43
|
+
max: options.pool?.max || 10,
|
|
44
|
+
});
|
|
45
|
+
const client = await connectWithRetry();
|
|
46
|
+
try {
|
|
47
|
+
// Users table
|
|
48
|
+
await client.query(`
|
|
49
|
+
CREATE TABLE IF NOT EXISTS users (
|
|
50
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
51
|
+
email VARCHAR(255) UNIQUE NOT NULL,
|
|
52
|
+
name VARCHAR(255) NOT NULL,
|
|
53
|
+
password_hash VARCHAR(255) NOT NULL,
|
|
54
|
+
created_at TIMESTAMP DEFAULT NOW(),
|
|
55
|
+
updated_at TIMESTAMP DEFAULT NOW()
|
|
56
|
+
)
|
|
57
|
+
`);
|
|
58
|
+
// Links table
|
|
59
|
+
await client.query(`
|
|
60
|
+
CREATE TABLE IF NOT EXISTS links (
|
|
61
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
62
|
+
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
63
|
+
short_code VARCHAR(20) UNIQUE NOT NULL,
|
|
64
|
+
original_url TEXT NOT NULL,
|
|
65
|
+
title VARCHAR(255),
|
|
66
|
+
ios_url TEXT,
|
|
67
|
+
android_url TEXT,
|
|
68
|
+
web_fallback_url TEXT,
|
|
69
|
+
utm_parameters JSONB DEFAULT '{}',
|
|
70
|
+
targeting_rules JSONB DEFAULT '{}',
|
|
71
|
+
is_active BOOLEAN DEFAULT true,
|
|
72
|
+
expires_at TIMESTAMP,
|
|
73
|
+
created_at TIMESTAMP DEFAULT NOW(),
|
|
74
|
+
updated_at TIMESTAMP DEFAULT NOW()
|
|
75
|
+
)
|
|
76
|
+
`);
|
|
77
|
+
// Click events table
|
|
78
|
+
await client.query(`
|
|
79
|
+
CREATE TABLE IF NOT EXISTS click_events (
|
|
80
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
81
|
+
link_id UUID NOT NULL REFERENCES links(id) ON DELETE CASCADE,
|
|
82
|
+
clicked_at TIMESTAMP DEFAULT NOW(),
|
|
83
|
+
ip_address INET,
|
|
84
|
+
user_agent TEXT,
|
|
85
|
+
device_type VARCHAR(20),
|
|
86
|
+
platform VARCHAR(20),
|
|
87
|
+
country_code CHAR(2),
|
|
88
|
+
country_name VARCHAR(100),
|
|
89
|
+
region VARCHAR(100),
|
|
90
|
+
city VARCHAR(100),
|
|
91
|
+
latitude DECIMAL(10, 8),
|
|
92
|
+
longitude DECIMAL(11, 8),
|
|
93
|
+
timezone VARCHAR(100),
|
|
94
|
+
utm_source VARCHAR(255),
|
|
95
|
+
utm_medium VARCHAR(255),
|
|
96
|
+
utm_campaign VARCHAR(255),
|
|
97
|
+
referrer TEXT
|
|
98
|
+
)
|
|
99
|
+
`);
|
|
100
|
+
// Create indexes for performance
|
|
101
|
+
await client.query('CREATE UNIQUE INDEX IF NOT EXISTS idx_links_short_code ON links(short_code)');
|
|
102
|
+
await client.query('CREATE INDEX IF NOT EXISTS idx_links_user_id ON links(user_id)');
|
|
103
|
+
await client.query('CREATE INDEX IF NOT EXISTS idx_links_created_at ON links(created_at DESC)');
|
|
104
|
+
await client.query('CREATE INDEX IF NOT EXISTS idx_clicks_link_id ON click_events(link_id)');
|
|
105
|
+
await client.query('CREATE INDEX IF NOT EXISTS idx_clicks_timestamp ON click_events(clicked_at DESC)');
|
|
106
|
+
await client.query('CREATE INDEX IF NOT EXISTS idx_clicks_link_date ON click_events(link_id, clicked_at DESC)');
|
|
107
|
+
await client.query('CREATE INDEX IF NOT EXISTS idx_users_email ON users(email)');
|
|
108
|
+
console.log('Database schema initialized successfully');
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
console.error('Error initializing database:', error);
|
|
112
|
+
throw error;
|
|
113
|
+
}
|
|
114
|
+
finally {
|
|
115
|
+
client.release();
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=database.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"database.js","sourceRoot":"","sources":["../../src/lib/database.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export declare function generateShortCode(length?: number): string;
|
|
2
|
+
export declare function parseUserAgent(userAgent: string): {
|
|
3
|
+
deviceType: string;
|
|
4
|
+
platform: string;
|
|
5
|
+
browser: string;
|
|
6
|
+
};
|
|
7
|
+
export declare function getLocationFromIP(ip: string): {
|
|
8
|
+
countryCode: null;
|
|
9
|
+
countryName: null;
|
|
10
|
+
region: null;
|
|
11
|
+
city: null;
|
|
12
|
+
latitude: null;
|
|
13
|
+
longitude: null;
|
|
14
|
+
timezone: null;
|
|
15
|
+
} | {
|
|
16
|
+
countryCode: string;
|
|
17
|
+
countryName: string;
|
|
18
|
+
region: string;
|
|
19
|
+
city: string;
|
|
20
|
+
latitude: number | null;
|
|
21
|
+
longitude: number | null;
|
|
22
|
+
timezone: string;
|
|
23
|
+
};
|
|
24
|
+
export declare function buildRedirectUrl(originalUrl: string, utmParameters?: Record<string, string>): string;
|
|
25
|
+
export declare function detectDevice(userAgent: string): 'ios' | 'android' | 'web';
|
|
26
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/lib/utils.ts"],"names":[],"mappings":"AAIA,wBAAgB,iBAAiB,CAAC,MAAM,GAAE,MAAU,GAAG,MAAM,CAE5D;AAED,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM;;;;EAS/C;AAoDD,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,MAAM;;;;;;;;;;;;;;;;EAwB3C;AAED,wBAAgB,gBAAgB,CAC9B,WAAW,EAAE,MAAM,EACnB,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GACrC,MAAM,CAYR;AAED,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,KAAK,GAAG,SAAS,GAAG,KAAK,CAYzE"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.generateShortCode = generateShortCode;
|
|
7
|
+
exports.parseUserAgent = parseUserAgent;
|
|
8
|
+
exports.getLocationFromIP = getLocationFromIP;
|
|
9
|
+
exports.buildRedirectUrl = buildRedirectUrl;
|
|
10
|
+
exports.detectDevice = detectDevice;
|
|
11
|
+
const nanoid_1 = require("nanoid");
|
|
12
|
+
const geoip_lite_1 = __importDefault(require("geoip-lite"));
|
|
13
|
+
const ua_parser_js_1 = __importDefault(require("ua-parser-js"));
|
|
14
|
+
function generateShortCode(length = 8) {
|
|
15
|
+
return (0, nanoid_1.nanoid)(length);
|
|
16
|
+
}
|
|
17
|
+
function parseUserAgent(userAgent) {
|
|
18
|
+
const parser = new ua_parser_js_1.default(userAgent);
|
|
19
|
+
const result = parser.getResult();
|
|
20
|
+
return {
|
|
21
|
+
deviceType: result.device.type || 'desktop',
|
|
22
|
+
platform: result.os.name || 'unknown',
|
|
23
|
+
browser: result.browser.name || 'unknown',
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
// Country code to name mapping (common countries)
|
|
27
|
+
const COUNTRY_NAMES = {
|
|
28
|
+
US: 'United States',
|
|
29
|
+
GB: 'United Kingdom',
|
|
30
|
+
CA: 'Canada',
|
|
31
|
+
AU: 'Australia',
|
|
32
|
+
DE: 'Germany',
|
|
33
|
+
FR: 'France',
|
|
34
|
+
IT: 'Italy',
|
|
35
|
+
ES: 'Spain',
|
|
36
|
+
NL: 'Netherlands',
|
|
37
|
+
SE: 'Sweden',
|
|
38
|
+
NO: 'Norway',
|
|
39
|
+
DK: 'Denmark',
|
|
40
|
+
FI: 'Finland',
|
|
41
|
+
PL: 'Poland',
|
|
42
|
+
BR: 'Brazil',
|
|
43
|
+
MX: 'Mexico',
|
|
44
|
+
AR: 'Argentina',
|
|
45
|
+
IN: 'India',
|
|
46
|
+
CN: 'China',
|
|
47
|
+
JP: 'Japan',
|
|
48
|
+
KR: 'South Korea',
|
|
49
|
+
SG: 'Singapore',
|
|
50
|
+
MY: 'Malaysia',
|
|
51
|
+
TH: 'Thailand',
|
|
52
|
+
ID: 'Indonesia',
|
|
53
|
+
PH: 'Philippines',
|
|
54
|
+
VN: 'Vietnam',
|
|
55
|
+
ZA: 'South Africa',
|
|
56
|
+
EG: 'Egypt',
|
|
57
|
+
NG: 'Nigeria',
|
|
58
|
+
KE: 'Kenya',
|
|
59
|
+
RU: 'Russia',
|
|
60
|
+
TR: 'Turkey',
|
|
61
|
+
AE: 'United Arab Emirates',
|
|
62
|
+
SA: 'Saudi Arabia',
|
|
63
|
+
IL: 'Israel',
|
|
64
|
+
NZ: 'New Zealand',
|
|
65
|
+
IE: 'Ireland',
|
|
66
|
+
CH: 'Switzerland',
|
|
67
|
+
AT: 'Austria',
|
|
68
|
+
BE: 'Belgium',
|
|
69
|
+
PT: 'Portugal',
|
|
70
|
+
GR: 'Greece',
|
|
71
|
+
CZ: 'Czech Republic',
|
|
72
|
+
HU: 'Hungary',
|
|
73
|
+
RO: 'Romania',
|
|
74
|
+
};
|
|
75
|
+
function getLocationFromIP(ip) {
|
|
76
|
+
const geo = geoip_lite_1.default.lookup(ip);
|
|
77
|
+
if (!geo) {
|
|
78
|
+
return {
|
|
79
|
+
countryCode: null,
|
|
80
|
+
countryName: null,
|
|
81
|
+
region: null,
|
|
82
|
+
city: null,
|
|
83
|
+
latitude: null,
|
|
84
|
+
longitude: null,
|
|
85
|
+
timezone: null,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
countryCode: geo.country,
|
|
90
|
+
countryName: COUNTRY_NAMES[geo.country] || geo.country,
|
|
91
|
+
region: geo.region,
|
|
92
|
+
city: geo.city,
|
|
93
|
+
latitude: geo.ll?.[0] || null,
|
|
94
|
+
longitude: geo.ll?.[1] || null,
|
|
95
|
+
timezone: geo.timezone,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
function buildRedirectUrl(originalUrl, utmParameters) {
|
|
99
|
+
const url = new URL(originalUrl);
|
|
100
|
+
if (utmParameters) {
|
|
101
|
+
Object.entries(utmParameters).forEach(([key, value]) => {
|
|
102
|
+
if (value) {
|
|
103
|
+
url.searchParams.set(`utm_${key}`, value);
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
return url.toString();
|
|
108
|
+
}
|
|
109
|
+
function detectDevice(userAgent) {
|
|
110
|
+
const ua = userAgent.toLowerCase();
|
|
111
|
+
if (ua.includes('iphone') || ua.includes('ipad') || ua.includes('ipod')) {
|
|
112
|
+
return 'ios';
|
|
113
|
+
}
|
|
114
|
+
if (ua.includes('android')) {
|
|
115
|
+
return 'android';
|
|
116
|
+
}
|
|
117
|
+
return 'web';
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/lib/utils.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.analyticsRoutes = analyticsRoutes;
|
|
4
|
+
const database_js_1 = require("../lib/database.js");
|
|
5
|
+
async function analyticsRoutes(fastify) {
|
|
6
|
+
// Get overall analytics for a user
|
|
7
|
+
fastify.get('/api/analytics/overview', async (request) => {
|
|
8
|
+
const { userId, days = 30 } = request.query;
|
|
9
|
+
if (!userId) {
|
|
10
|
+
throw new Error('userId query parameter is required');
|
|
11
|
+
}
|
|
12
|
+
// Get total and unique clicks
|
|
13
|
+
const clicksResult = await database_js_1.db.query(`SELECT
|
|
14
|
+
COUNT(*) as total_clicks,
|
|
15
|
+
COUNT(DISTINCT ip_address) as unique_clicks
|
|
16
|
+
FROM click_events ce
|
|
17
|
+
JOIN links l ON ce.link_id = l.id
|
|
18
|
+
WHERE l.user_id = $1 AND ce.clicked_at >= NOW() - INTERVAL '${days} days'`, [userId]);
|
|
19
|
+
// Get clicks by date
|
|
20
|
+
const dateResult = await database_js_1.db.query(`SELECT
|
|
21
|
+
DATE(ce.clicked_at) as date,
|
|
22
|
+
COUNT(*) as clicks
|
|
23
|
+
FROM click_events ce
|
|
24
|
+
JOIN links l ON ce.link_id = l.id
|
|
25
|
+
WHERE l.user_id = $1 AND ce.clicked_at >= NOW() - INTERVAL '${days} days'
|
|
26
|
+
GROUP BY DATE(ce.clicked_at)
|
|
27
|
+
ORDER BY date`, [userId]);
|
|
28
|
+
// Get clicks by country
|
|
29
|
+
const countryResult = await database_js_1.db.query(`SELECT
|
|
30
|
+
COALESCE(ce.country_code, 'Unknown') as country_code,
|
|
31
|
+
COALESCE(ce.country_name, ce.country_code, 'Unknown') as country,
|
|
32
|
+
COUNT(*) as clicks
|
|
33
|
+
FROM click_events ce
|
|
34
|
+
JOIN links l ON ce.link_id = l.id
|
|
35
|
+
WHERE l.user_id = $1 AND ce.clicked_at >= NOW() - INTERVAL '${days} days'
|
|
36
|
+
GROUP BY ce.country_code, ce.country_name
|
|
37
|
+
ORDER BY clicks DESC`, [userId]);
|
|
38
|
+
// Get clicks by device
|
|
39
|
+
const deviceResult = await database_js_1.db.query(`SELECT
|
|
40
|
+
COALESCE(ce.device_type, 'Unknown') as device,
|
|
41
|
+
COUNT(*) as clicks
|
|
42
|
+
FROM click_events ce
|
|
43
|
+
JOIN links l ON ce.link_id = l.id
|
|
44
|
+
WHERE l.user_id = $1 AND ce.clicked_at >= NOW() - INTERVAL '${days} days'
|
|
45
|
+
GROUP BY ce.device_type
|
|
46
|
+
ORDER BY clicks DESC`, [userId]);
|
|
47
|
+
// Get clicks by platform
|
|
48
|
+
const platformResult = await database_js_1.db.query(`SELECT
|
|
49
|
+
COALESCE(ce.platform, 'Unknown') as platform,
|
|
50
|
+
COUNT(*) as clicks
|
|
51
|
+
FROM click_events ce
|
|
52
|
+
JOIN links l ON ce.link_id = l.id
|
|
53
|
+
WHERE l.user_id = $1 AND ce.clicked_at >= NOW() - INTERVAL '${days} days'
|
|
54
|
+
GROUP BY ce.platform
|
|
55
|
+
ORDER BY clicks DESC`, [userId]);
|
|
56
|
+
// Get top performing links
|
|
57
|
+
const topLinksResult = await database_js_1.db.query(`SELECT
|
|
58
|
+
l.id,
|
|
59
|
+
l.short_code,
|
|
60
|
+
l.title,
|
|
61
|
+
l.original_url,
|
|
62
|
+
COUNT(ce.id) as total_clicks,
|
|
63
|
+
COUNT(DISTINCT ce.ip_address) as unique_clicks
|
|
64
|
+
FROM links l
|
|
65
|
+
LEFT JOIN click_events ce ON l.id = ce.link_id
|
|
66
|
+
AND ce.clicked_at >= NOW() - INTERVAL '${days} days'
|
|
67
|
+
WHERE l.user_id = $1
|
|
68
|
+
GROUP BY l.id
|
|
69
|
+
ORDER BY total_clicks DESC
|
|
70
|
+
LIMIT 10`, [userId]);
|
|
71
|
+
return {
|
|
72
|
+
totalClicks: parseInt(clicksResult.rows[0]?.total_clicks || '0'),
|
|
73
|
+
uniqueClicks: parseInt(clicksResult.rows[0]?.unique_clicks || '0'),
|
|
74
|
+
clicksByDate: dateResult.rows.map(row => ({
|
|
75
|
+
date: row.date,
|
|
76
|
+
clicks: parseInt(row.clicks),
|
|
77
|
+
})),
|
|
78
|
+
clicksByCountry: countryResult.rows.map(row => ({
|
|
79
|
+
countryCode: row.country_code,
|
|
80
|
+
country: row.country,
|
|
81
|
+
clicks: parseInt(row.clicks),
|
|
82
|
+
})),
|
|
83
|
+
clicksByDevice: deviceResult.rows.map(row => ({
|
|
84
|
+
device: row.device,
|
|
85
|
+
clicks: parseInt(row.clicks),
|
|
86
|
+
})),
|
|
87
|
+
clicksByPlatform: platformResult.rows.map(row => ({
|
|
88
|
+
platform: row.platform,
|
|
89
|
+
clicks: parseInt(row.clicks),
|
|
90
|
+
})),
|
|
91
|
+
topLinks: topLinksResult.rows.map(row => ({
|
|
92
|
+
id: row.id,
|
|
93
|
+
shortCode: row.short_code,
|
|
94
|
+
title: row.title,
|
|
95
|
+
originalUrl: row.original_url,
|
|
96
|
+
totalClicks: parseInt(row.total_clicks),
|
|
97
|
+
uniqueClicks: parseInt(row.unique_clicks),
|
|
98
|
+
})),
|
|
99
|
+
};
|
|
100
|
+
});
|
|
101
|
+
// Get link-specific analytics
|
|
102
|
+
fastify.get('/api/analytics/links/:linkId', async (request) => {
|
|
103
|
+
const { linkId } = request.params;
|
|
104
|
+
const { userId, days = 30 } = request.query;
|
|
105
|
+
if (!userId) {
|
|
106
|
+
throw new Error('userId query parameter is required');
|
|
107
|
+
}
|
|
108
|
+
// Verify link ownership
|
|
109
|
+
const linkResult = await database_js_1.db.query('SELECT id FROM links WHERE id = $1 AND user_id = $2', [linkId, userId]);
|
|
110
|
+
if (linkResult.rows.length === 0) {
|
|
111
|
+
throw new Error('Link not found');
|
|
112
|
+
}
|
|
113
|
+
// Get analytics for specific link
|
|
114
|
+
const clicksResult = await database_js_1.db.query(`SELECT
|
|
115
|
+
COUNT(*) as total_clicks,
|
|
116
|
+
COUNT(DISTINCT ip_address) as unique_clicks
|
|
117
|
+
FROM click_events
|
|
118
|
+
WHERE link_id = $1 AND clicked_at >= NOW() - INTERVAL '${days} days'`, [linkId]);
|
|
119
|
+
const dateResult = await database_js_1.db.query(`SELECT
|
|
120
|
+
DATE(clicked_at) as date,
|
|
121
|
+
COUNT(*) as clicks
|
|
122
|
+
FROM click_events
|
|
123
|
+
WHERE link_id = $1 AND clicked_at >= NOW() - INTERVAL '${days} days'
|
|
124
|
+
GROUP BY DATE(clicked_at)
|
|
125
|
+
ORDER BY date`, [linkId]);
|
|
126
|
+
const countryResult = await database_js_1.db.query(`SELECT
|
|
127
|
+
COALESCE(country_code, 'Unknown') as country_code,
|
|
128
|
+
COALESCE(country_name, country_code, 'Unknown') as country,
|
|
129
|
+
COUNT(*) as clicks
|
|
130
|
+
FROM click_events
|
|
131
|
+
WHERE link_id = $1 AND clicked_at >= NOW() - INTERVAL '${days} days'
|
|
132
|
+
GROUP BY country_code, country_name
|
|
133
|
+
ORDER BY clicks DESC`, [linkId]);
|
|
134
|
+
const deviceResult = await database_js_1.db.query(`SELECT
|
|
135
|
+
COALESCE(device_type, 'Unknown') as device,
|
|
136
|
+
COUNT(*) as clicks
|
|
137
|
+
FROM click_events
|
|
138
|
+
WHERE link_id = $1 AND clicked_at >= NOW() - INTERVAL '${days} days'
|
|
139
|
+
GROUP BY device_type
|
|
140
|
+
ORDER BY clicks DESC`, [linkId]);
|
|
141
|
+
const platformResult = await database_js_1.db.query(`SELECT
|
|
142
|
+
COALESCE(platform, 'Unknown') as platform,
|
|
143
|
+
COUNT(*) as clicks
|
|
144
|
+
FROM click_events
|
|
145
|
+
WHERE link_id = $1 AND clicked_at >= NOW() - INTERVAL '${days} days'
|
|
146
|
+
GROUP BY platform
|
|
147
|
+
ORDER BY clicks DESC`, [linkId]);
|
|
148
|
+
return {
|
|
149
|
+
totalClicks: parseInt(clicksResult.rows[0]?.total_clicks || '0'),
|
|
150
|
+
uniqueClicks: parseInt(clicksResult.rows[0]?.unique_clicks || '0'),
|
|
151
|
+
clicksByDate: dateResult.rows.map(row => ({
|
|
152
|
+
date: row.date,
|
|
153
|
+
clicks: parseInt(row.clicks),
|
|
154
|
+
})),
|
|
155
|
+
clicksByCountry: countryResult.rows.map(row => ({
|
|
156
|
+
countryCode: row.country_code,
|
|
157
|
+
country: row.country,
|
|
158
|
+
clicks: parseInt(row.clicks),
|
|
159
|
+
})),
|
|
160
|
+
clicksByDevice: deviceResult.rows.map(row => ({
|
|
161
|
+
device: row.device,
|
|
162
|
+
clicks: parseInt(row.clicks),
|
|
163
|
+
})),
|
|
164
|
+
clicksByPlatform: platformResult.rows.map(row => ({
|
|
165
|
+
platform: row.platform,
|
|
166
|
+
clicks: parseInt(row.clicks),
|
|
167
|
+
})),
|
|
168
|
+
};
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
//# sourceMappingURL=analytics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analytics.js","sourceRoot":"","sources":["../../src/routes/analytics.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/routes/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.analyticsRoutes = exports.linkRoutes = exports.redirectRoutes = void 0;
|
|
4
|
+
var redirect_js_1 = require("./redirect.js");
|
|
5
|
+
Object.defineProperty(exports, "redirectRoutes", { enumerable: true, get: function () { return redirect_js_1.redirectRoutes; } });
|
|
6
|
+
var links_js_1 = require("./links.js");
|
|
7
|
+
Object.defineProperty(exports, "linkRoutes", { enumerable: true, get: function () { return links_js_1.linkRoutes; } });
|
|
8
|
+
var analytics_js_1 = require("./analytics.js");
|
|
9
|
+
Object.defineProperty(exports, "analyticsRoutes", { enumerable: true, get: function () { return analytics_js_1.analyticsRoutes; } });
|
|
10
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/routes/index.ts"],"names":[],"mappings":";;;AAAA,6CAA+C;AAAtC,6GAAA,cAAc,OAAA;AACvB,uCAAwC;AAA/B,sGAAA,UAAU,OAAA;AACnB,+CAAiD;AAAxC,+GAAA,eAAe,OAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"links.d.ts","sourceRoot":"","sources":["../../src/routes/links.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAkB,MAAM,SAAS,CAAC;AAgC1D,wBAAsB,UAAU,CAAC,OAAO,EAAE,eAAe,iBA2MxD"}
|