locallytics 0.1.8 → 0.2.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/dist/adapters/drizzle.d.mts +25 -0
- package/dist/adapters/drizzle.d.ts +25 -0
- package/dist/adapters/drizzle.js +38 -0
- package/dist/adapters/drizzle.mjs +7 -0
- package/dist/adapters/prisma.d.mts +25 -0
- package/dist/adapters/prisma.d.ts +25 -0
- package/dist/adapters/prisma.js +38 -0
- package/dist/adapters/prisma.mjs +7 -0
- package/dist/adapters.d.mts +7 -0
- package/dist/adapters.d.ts +7 -0
- package/dist/adapters.js +39 -0
- package/dist/adapters.mjs +8 -0
- package/dist/browser.d.mts +41 -0
- package/dist/browser.d.ts +41 -0
- package/dist/browser.js +79 -0
- package/dist/browser.mjs +10 -0
- package/dist/index.d.mts +59 -0
- package/dist/index.d.ts +59 -7
- package/dist/index.js +367 -7
- package/dist/index.mjs +336 -0
- package/dist/react.d.mts +48 -0
- package/dist/react.d.ts +48 -0
- package/dist/react.js +132 -0
- package/dist/react.mjs +61 -0
- package/dist/shared/chunk-8tbv1rg5.js +45 -0
- package/package.json +60 -48
- package/README.md +0 -73
- package/dist/client/LocallyticsGrabber.d.ts +0 -30
- package/dist/client/LocallyticsGrabber.d.ts.map +0 -1
- package/dist/client/LocallyticsGrabber.js +0 -71
- package/dist/client/LocallyticsGrabber.js.map +0 -1
- package/dist/client/batcher.d.ts +0 -48
- package/dist/client/batcher.d.ts.map +0 -1
- package/dist/client/batcher.js +0 -139
- package/dist/client/batcher.js.map +0 -1
- package/dist/client/tracker.d.ts +0 -18
- package/dist/client/tracker.d.ts.map +0 -1
- package/dist/client/tracker.js +0 -108
- package/dist/client/tracker.js.map +0 -1
- package/dist/db/factory.d.ts +0 -11
- package/dist/db/factory.d.ts.map +0 -1
- package/dist/db/factory.js +0 -47
- package/dist/db/factory.js.map +0 -1
- package/dist/db/mysql.d.ts +0 -17
- package/dist/db/mysql.d.ts.map +0 -1
- package/dist/db/mysql.js +0 -144
- package/dist/db/mysql.js.map +0 -1
- package/dist/db/postgres.d.ts +0 -16
- package/dist/db/postgres.d.ts.map +0 -1
- package/dist/db/postgres.js +0 -143
- package/dist/db/postgres.js.map +0 -1
- package/dist/db/sqlite.d.ts +0 -17
- package/dist/db/sqlite.d.ts.map +0 -1
- package/dist/db/sqlite.js +0 -130
- package/dist/db/sqlite.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/server/handlers.d.ts +0 -10
- package/dist/server/handlers.d.ts.map +0 -1
- package/dist/server/handlers.js +0 -105
- package/dist/server/handlers.js.map +0 -1
- package/dist/server/index.d.ts +0 -42
- package/dist/server/index.d.ts.map +0 -1
- package/dist/server/index.js +0 -100
- package/dist/server/index.js.map +0 -1
- package/dist/server/queries.d.ts +0 -10
- package/dist/server/queries.d.ts.map +0 -1
- package/dist/server/queries.js +0 -24
- package/dist/server/queries.js.map +0 -1
- package/dist/server/validator.d.ts +0 -32
- package/dist/server/validator.d.ts.map +0 -1
- package/dist/server/validator.js +0 -144
- package/dist/server/validator.js.map +0 -1
- package/dist/types/index.d.ts +0 -135
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/index.js +0 -24
- package/dist/types/index.js.map +0 -1
- package/dist/utils/hash.d.ts +0 -16
- package/dist/utils/hash.d.ts.map +0 -1
- package/dist/utils/hash.js +0 -36
- package/dist/utils/hash.js.map +0 -1
- package/dist/utils/rate-limit.d.ts +0 -32
- package/dist/utils/rate-limit.d.ts.map +0 -1
- package/dist/utils/rate-limit.js +0 -73
- package/dist/utils/rate-limit.js.map +0 -1
- package/src/db/schema-mysql.sql +0 -17
- package/src/db/schema-sqlite.sql +0 -29
- package/src/db/schema.sql +0 -35
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import type { PageviewEvent } from "../types/index.js";
|
|
2
|
-
/**
|
|
3
|
-
* Sanitize a URL by extracting pathname and search, removing credentials
|
|
4
|
-
*
|
|
5
|
-
* @param url - Raw URL string
|
|
6
|
-
* @returns Sanitized URL (pathname + search only)
|
|
7
|
-
*/
|
|
8
|
-
export declare function sanitizeUrl(url: string): string;
|
|
9
|
-
/**
|
|
10
|
-
* Check if Do Not Track is enabled in the request
|
|
11
|
-
*
|
|
12
|
-
* @param request - Incoming request
|
|
13
|
-
* @returns true if DNT is enabled
|
|
14
|
-
*/
|
|
15
|
-
export declare function checkDNT(request: Request): boolean;
|
|
16
|
-
/**
|
|
17
|
-
* Validate a single pageview event
|
|
18
|
-
*
|
|
19
|
-
* @param data - Unknown input data
|
|
20
|
-
* @returns Validated PageviewEvent
|
|
21
|
-
* @throws LocallyticsError if validation fails
|
|
22
|
-
*/
|
|
23
|
-
export declare function validatePageviewEvent(data: unknown): PageviewEvent;
|
|
24
|
-
/**
|
|
25
|
-
* Validate a batch of pageview events
|
|
26
|
-
*
|
|
27
|
-
* @param data - Unknown input data
|
|
28
|
-
* @returns Array of validated PageviewEvents
|
|
29
|
-
* @throws LocallyticsError if validation fails
|
|
30
|
-
*/
|
|
31
|
-
export declare function validatePageviewBatch(data: unknown): PageviewEvent[];
|
|
32
|
-
//# sourceMappingURL=validator.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../../src/server/validator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAQvD;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAkB/C;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAUlD;AASD;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,OAAO,GAAG,aAAa,CA6ElE;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,OAAO,GAAG,aAAa,EAAE,CA2BpE"}
|
package/dist/server/validator.js
DELETED
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
import { LocallyticsError } from "../types/index.js";
|
|
2
|
-
const MAX_SESSION_ID_LENGTH = 255;
|
|
3
|
-
const MAX_USER_AGENT_LENGTH = 512;
|
|
4
|
-
const MAX_URL_LENGTH = 2048;
|
|
5
|
-
const MAX_BATCH_SIZE = 100;
|
|
6
|
-
/**
|
|
7
|
-
* Sanitize a URL by extracting pathname and search, removing credentials
|
|
8
|
-
*
|
|
9
|
-
* @param url - Raw URL string
|
|
10
|
-
* @returns Sanitized URL (pathname + search only)
|
|
11
|
-
*/
|
|
12
|
-
export function sanitizeUrl(url) {
|
|
13
|
-
try {
|
|
14
|
-
// Handle relative URLs by adding a base
|
|
15
|
-
const parsed = new URL(url, "http://localhost");
|
|
16
|
-
// Extract pathname and search only (no origin, hash, credentials)
|
|
17
|
-
let sanitized = parsed.pathname + parsed.search;
|
|
18
|
-
// Limit length
|
|
19
|
-
if (sanitized.length > MAX_URL_LENGTH) {
|
|
20
|
-
sanitized = sanitized.slice(0, MAX_URL_LENGTH);
|
|
21
|
-
}
|
|
22
|
-
return sanitized;
|
|
23
|
-
}
|
|
24
|
-
catch {
|
|
25
|
-
// On parse error, return root
|
|
26
|
-
return "/";
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* Check if Do Not Track is enabled in the request
|
|
31
|
-
*
|
|
32
|
-
* @param request - Incoming request
|
|
33
|
-
* @returns true if DNT is enabled
|
|
34
|
-
*/
|
|
35
|
-
export function checkDNT(request) {
|
|
36
|
-
const headers = request.headers;
|
|
37
|
-
// Check DNT header
|
|
38
|
-
const dnt = headers.get("dnt");
|
|
39
|
-
if (dnt === "1") {
|
|
40
|
-
return true;
|
|
41
|
-
}
|
|
42
|
-
return false;
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* Type guard to check if value is a record
|
|
46
|
-
*/
|
|
47
|
-
function isRecord(value) {
|
|
48
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
49
|
-
}
|
|
50
|
-
/**
|
|
51
|
-
* Validate a single pageview event
|
|
52
|
-
*
|
|
53
|
-
* @param data - Unknown input data
|
|
54
|
-
* @returns Validated PageviewEvent
|
|
55
|
-
* @throws LocallyticsError if validation fails
|
|
56
|
-
*/
|
|
57
|
-
export function validatePageviewEvent(data) {
|
|
58
|
-
if (!isRecord(data)) {
|
|
59
|
-
throw LocallyticsError.validation("Event must be an object");
|
|
60
|
-
}
|
|
61
|
-
// sessionId
|
|
62
|
-
if (typeof data["sessionId"] !== "string" || data["sessionId"].length === 0) {
|
|
63
|
-
throw LocallyticsError.validation("sessionId must be a non-empty string");
|
|
64
|
-
}
|
|
65
|
-
if (data["sessionId"].length > MAX_SESSION_ID_LENGTH) {
|
|
66
|
-
throw LocallyticsError.validation(`sessionId must be at most ${MAX_SESSION_ID_LENGTH} characters`);
|
|
67
|
-
}
|
|
68
|
-
// pageUrl
|
|
69
|
-
if (typeof data["pageUrl"] !== "string" || data["pageUrl"].length === 0) {
|
|
70
|
-
throw LocallyticsError.validation("pageUrl must be a non-empty string");
|
|
71
|
-
}
|
|
72
|
-
// referrer (nullable)
|
|
73
|
-
if (data["referrer"] !== null &&
|
|
74
|
-
data["referrer"] !== undefined &&
|
|
75
|
-
typeof data["referrer"] !== "string") {
|
|
76
|
-
throw LocallyticsError.validation("referrer must be a string or null");
|
|
77
|
-
}
|
|
78
|
-
// userAgent
|
|
79
|
-
if (typeof data["userAgent"] !== "string") {
|
|
80
|
-
throw LocallyticsError.validation("userAgent must be a string");
|
|
81
|
-
}
|
|
82
|
-
// screenWidth
|
|
83
|
-
if (typeof data["screenWidth"] !== "number" ||
|
|
84
|
-
data["screenWidth"] <= 0 ||
|
|
85
|
-
!Number.isFinite(data["screenWidth"])) {
|
|
86
|
-
throw LocallyticsError.validation("screenWidth must be a positive number");
|
|
87
|
-
}
|
|
88
|
-
// screenHeight
|
|
89
|
-
if (typeof data["screenHeight"] !== "number" ||
|
|
90
|
-
data["screenHeight"] <= 0 ||
|
|
91
|
-
!Number.isFinite(data["screenHeight"])) {
|
|
92
|
-
throw LocallyticsError.validation("screenHeight must be a positive number");
|
|
93
|
-
}
|
|
94
|
-
// timestamp
|
|
95
|
-
if (typeof data["timestamp"] !== "string") {
|
|
96
|
-
throw LocallyticsError.validation("timestamp must be a string");
|
|
97
|
-
}
|
|
98
|
-
// Validate ISO 8601 format
|
|
99
|
-
const timestampDate = new Date(data["timestamp"]);
|
|
100
|
-
if (isNaN(timestampDate.getTime())) {
|
|
101
|
-
throw LocallyticsError.validation("timestamp must be a valid ISO 8601 date");
|
|
102
|
-
}
|
|
103
|
-
return {
|
|
104
|
-
sessionId: data["sessionId"],
|
|
105
|
-
pageUrl: sanitizeUrl(data["pageUrl"]),
|
|
106
|
-
referrer: typeof data["referrer"] === "string"
|
|
107
|
-
? sanitizeUrl(data["referrer"])
|
|
108
|
-
: null,
|
|
109
|
-
userAgent: data["userAgent"].slice(0, MAX_USER_AGENT_LENGTH),
|
|
110
|
-
screenWidth: Math.round(data["screenWidth"]),
|
|
111
|
-
screenHeight: Math.round(data["screenHeight"]),
|
|
112
|
-
timestamp: data["timestamp"],
|
|
113
|
-
};
|
|
114
|
-
}
|
|
115
|
-
/**
|
|
116
|
-
* Validate a batch of pageview events
|
|
117
|
-
*
|
|
118
|
-
* @param data - Unknown input data
|
|
119
|
-
* @returns Array of validated PageviewEvents
|
|
120
|
-
* @throws LocallyticsError if validation fails
|
|
121
|
-
*/
|
|
122
|
-
export function validatePageviewBatch(data) {
|
|
123
|
-
if (!Array.isArray(data)) {
|
|
124
|
-
throw LocallyticsError.validation("Batch must be an array");
|
|
125
|
-
}
|
|
126
|
-
if (data.length === 0) {
|
|
127
|
-
throw LocallyticsError.validation("Batch cannot be empty");
|
|
128
|
-
}
|
|
129
|
-
if (data.length > MAX_BATCH_SIZE) {
|
|
130
|
-
throw LocallyticsError.validation(`Batch cannot exceed ${MAX_BATCH_SIZE} events`);
|
|
131
|
-
}
|
|
132
|
-
return data.map((event, index) => {
|
|
133
|
-
try {
|
|
134
|
-
return validatePageviewEvent(event);
|
|
135
|
-
}
|
|
136
|
-
catch (error) {
|
|
137
|
-
if (error instanceof LocallyticsError) {
|
|
138
|
-
throw LocallyticsError.validation(`Event at index ${index}: ${error.message}`);
|
|
139
|
-
}
|
|
140
|
-
throw error;
|
|
141
|
-
}
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
//# sourceMappingURL=validator.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"validator.js","sourceRoot":"","sources":["../../src/server/validator.ts"],"names":[],"mappings":"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"}
|
package/dist/types/index.d.ts
DELETED
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Database type supported by locallytics
|
|
3
|
-
*/
|
|
4
|
-
export type DatabaseType = "postgres" | "mysql" | "sqlite";
|
|
5
|
-
/**
|
|
6
|
-
* Configuration for the locallytics() function
|
|
7
|
-
*/
|
|
8
|
-
export interface LocallyticsConfig {
|
|
9
|
-
/** Database connection string or file path (for SQLite) */
|
|
10
|
-
database: string;
|
|
11
|
-
/** Database type - auto-detected from connection string if omitted */
|
|
12
|
-
type?: DatabaseType;
|
|
13
|
-
/** Optional API key for GET endpoint authentication */
|
|
14
|
-
apiKey?: string;
|
|
15
|
-
}
|
|
16
|
-
/**
|
|
17
|
-
* Pageview event sent from client
|
|
18
|
-
*/
|
|
19
|
-
export interface PageviewEvent {
|
|
20
|
-
/** Client-generated session ID (ephemeral) */
|
|
21
|
-
sessionId: string;
|
|
22
|
-
/** Sanitized page URL (pathname + search) */
|
|
23
|
-
pageUrl: string;
|
|
24
|
-
/** Sanitized referrer URL (nullable) */
|
|
25
|
-
referrer: string | null;
|
|
26
|
-
/** User agent string (limited to 512 chars) */
|
|
27
|
-
userAgent: string;
|
|
28
|
-
/** Screen width in pixels */
|
|
29
|
-
screenWidth: number;
|
|
30
|
-
/** Screen height in pixels */
|
|
31
|
-
screenHeight: number;
|
|
32
|
-
/** ISO 8601 timestamp */
|
|
33
|
-
timestamp: string;
|
|
34
|
-
}
|
|
35
|
-
/**
|
|
36
|
-
* Pageview as stored in database
|
|
37
|
-
*/
|
|
38
|
-
export interface StoredPageview extends PageviewEvent {
|
|
39
|
-
/** UUID primary key */
|
|
40
|
-
id: string;
|
|
41
|
-
/** SHA-256 hashed IP address (nullable) */
|
|
42
|
-
ipHash: string | null;
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* Date range for analytics queries
|
|
46
|
-
*/
|
|
47
|
-
export type DateRange = "last24h" | "last7d" | "last30d" | {
|
|
48
|
-
start: Date;
|
|
49
|
-
end: Date;
|
|
50
|
-
};
|
|
51
|
-
/**
|
|
52
|
-
* Page statistics
|
|
53
|
-
*/
|
|
54
|
-
export interface PageStats {
|
|
55
|
-
/** Page URL */
|
|
56
|
-
pageUrl: string;
|
|
57
|
-
/** Number of pageviews */
|
|
58
|
-
count: number;
|
|
59
|
-
}
|
|
60
|
-
/**
|
|
61
|
-
* Daily statistics
|
|
62
|
-
*/
|
|
63
|
-
export interface DailyStats {
|
|
64
|
-
/** Date in YYYY-MM-DD format */
|
|
65
|
-
date: string;
|
|
66
|
-
/** Total pageviews */
|
|
67
|
-
pageviews: number;
|
|
68
|
-
/** Unique visitors (distinct sessions) */
|
|
69
|
-
uniqueVisitors: number;
|
|
70
|
-
}
|
|
71
|
-
/**
|
|
72
|
-
* Complete analytics data response
|
|
73
|
-
*/
|
|
74
|
-
export interface AnalyticsResult {
|
|
75
|
-
/** Total pageviews in date range */
|
|
76
|
-
pageviews: number;
|
|
77
|
-
/** Unique visitors (distinct sessions) in date range */
|
|
78
|
-
uniqueVisitors: number;
|
|
79
|
-
/** Top pages by pageview count */
|
|
80
|
-
topPages: PageStats[];
|
|
81
|
-
/** Top referrers by pageview count */
|
|
82
|
-
topReferrers: PageStats[];
|
|
83
|
-
/** Daily breakdown of stats */
|
|
84
|
-
dailyStats: DailyStats[];
|
|
85
|
-
/** Error message if fetch failed */
|
|
86
|
-
error?: string;
|
|
87
|
-
}
|
|
88
|
-
/**
|
|
89
|
-
* Database interface for analytics operations
|
|
90
|
-
*/
|
|
91
|
-
export interface AnalyticsDB {
|
|
92
|
-
/** Insert a pageview event */
|
|
93
|
-
insertPageview(event: PageviewEvent, ipHash: string | null): Promise<void>;
|
|
94
|
-
/** Get total pageview count */
|
|
95
|
-
getPageviews(dateRange: DateRange): Promise<number>;
|
|
96
|
-
/** Get unique visitor count (distinct sessions) */
|
|
97
|
-
getUniqueVisitors(dateRange: DateRange): Promise<number>;
|
|
98
|
-
/** Get top pages by pageview count */
|
|
99
|
-
getTopPages(dateRange: DateRange, limit: number): Promise<PageStats[]>;
|
|
100
|
-
/** Get top referrers by pageview count */
|
|
101
|
-
getTopReferrers(dateRange: DateRange, limit: number): Promise<PageStats[]>;
|
|
102
|
-
/** Get daily stats breakdown */
|
|
103
|
-
getDailyStats(dateRange: DateRange): Promise<DailyStats[]>;
|
|
104
|
-
/** Close database connection */
|
|
105
|
-
close(): Promise<void>;
|
|
106
|
-
}
|
|
107
|
-
/**
|
|
108
|
-
* Route handler type for Next.js App Router
|
|
109
|
-
*/
|
|
110
|
-
export interface RouteHandler {
|
|
111
|
-
GET: (request: Request) => Promise<Response>;
|
|
112
|
-
POST: (request: Request) => Promise<Response>;
|
|
113
|
-
}
|
|
114
|
-
/**
|
|
115
|
-
* Options for AnalyticsJSON helper
|
|
116
|
-
*/
|
|
117
|
-
export interface AnalyticsDataOptions {
|
|
118
|
-
/** Optional date range for analytics data (default: last7d) */
|
|
119
|
-
dateRange?: DateRange;
|
|
120
|
-
/** API endpoint URL (defaults to /api/analytics) */
|
|
121
|
-
endpoint?: string;
|
|
122
|
-
}
|
|
123
|
-
/**
|
|
124
|
-
* Custom error class for Locallytics errors
|
|
125
|
-
*/
|
|
126
|
-
export declare class LocallyticsError extends Error {
|
|
127
|
-
readonly code: string;
|
|
128
|
-
readonly statusCode: number;
|
|
129
|
-
constructor(message: string, code: string, statusCode?: number);
|
|
130
|
-
static validation(message: string): LocallyticsError;
|
|
131
|
-
static rateLimit(): LocallyticsError;
|
|
132
|
-
static unauthorized(): LocallyticsError;
|
|
133
|
-
static database(message: string): LocallyticsError;
|
|
134
|
-
}
|
|
135
|
-
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"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"}
|
package/dist/types/index.js
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Custom error class for Locallytics errors
|
|
3
|
-
*/
|
|
4
|
-
export class LocallyticsError extends Error {
|
|
5
|
-
constructor(message, code, statusCode = 500) {
|
|
6
|
-
super(message);
|
|
7
|
-
this.code = code;
|
|
8
|
-
this.statusCode = statusCode;
|
|
9
|
-
this.name = "LocallyticsError";
|
|
10
|
-
}
|
|
11
|
-
static validation(message) {
|
|
12
|
-
return new LocallyticsError(message, "VALIDATION_ERROR", 400);
|
|
13
|
-
}
|
|
14
|
-
static rateLimit() {
|
|
15
|
-
return new LocallyticsError("Rate limit exceeded", "RATE_LIMIT_EXCEEDED", 429);
|
|
16
|
-
}
|
|
17
|
-
static unauthorized() {
|
|
18
|
-
return new LocallyticsError("Unauthorized", "UNAUTHORIZED", 401);
|
|
19
|
-
}
|
|
20
|
-
static database(message) {
|
|
21
|
-
return new LocallyticsError(message, "DATABASE_ERROR", 500);
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
//# sourceMappingURL=index.js.map
|
package/dist/types/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAsIA;;GAEG;AACH,MAAM,OAAO,gBAAiB,SAAQ,KAAK;IACzC,YACE,OAAe,EACC,IAAY,EACZ,aAAqB,GAAG;QAExC,KAAK,CAAC,OAAO,CAAC,CAAC;QAHC,SAAI,GAAJ,IAAI,CAAQ;QACZ,eAAU,GAAV,UAAU,CAAc;QAGxC,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;IACjC,CAAC;IAED,MAAM,CAAC,UAAU,CAAC,OAAe;QAC/B,OAAO,IAAI,gBAAgB,CAAC,OAAO,EAAE,kBAAkB,EAAE,GAAG,CAAC,CAAC;IAChE,CAAC;IAED,MAAM,CAAC,SAAS;QACd,OAAO,IAAI,gBAAgB,CACzB,qBAAqB,EACrB,qBAAqB,EACrB,GAAG,CACJ,CAAC;IACJ,CAAC;IAED,MAAM,CAAC,YAAY;QACjB,OAAO,IAAI,gBAAgB,CAAC,cAAc,EAAE,cAAc,EAAE,GAAG,CAAC,CAAC;IACnE,CAAC;IAED,MAAM,CAAC,QAAQ,CAAC,OAAe;QAC7B,OAAO,IAAI,gBAAgB,CAAC,OAAO,EAAE,gBAAgB,EAAE,GAAG,CAAC,CAAC;IAC9D,CAAC;CACF"}
|
package/dist/utils/hash.d.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Hash an IP address using SHA-256 for privacy
|
|
3
|
-
*
|
|
4
|
-
* @param ip - Raw IP address
|
|
5
|
-
* @returns SHA-256 hash of the IP address
|
|
6
|
-
*/
|
|
7
|
-
export declare function hashIP(ip: string): string;
|
|
8
|
-
/**
|
|
9
|
-
* Extract IP address from request headers
|
|
10
|
-
* Checks x-forwarded-for and x-real-ip headers
|
|
11
|
-
*
|
|
12
|
-
* @param request - Incoming request
|
|
13
|
-
* @returns IP address or null if not found
|
|
14
|
-
*/
|
|
15
|
-
export declare function getIPFromRequest(request: Request): string | null;
|
|
16
|
-
//# sourceMappingURL=hash.d.ts.map
|
package/dist/utils/hash.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"hash.d.ts","sourceRoot":"","sources":["../../src/utils/hash.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,wBAAgB,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAEzC;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAoBhE"}
|
package/dist/utils/hash.js
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { createHash } from "crypto";
|
|
2
|
-
/**
|
|
3
|
-
* Hash an IP address using SHA-256 for privacy
|
|
4
|
-
*
|
|
5
|
-
* @param ip - Raw IP address
|
|
6
|
-
* @returns SHA-256 hash of the IP address
|
|
7
|
-
*/
|
|
8
|
-
export function hashIP(ip) {
|
|
9
|
-
return createHash("sha256").update(ip).digest("hex");
|
|
10
|
-
}
|
|
11
|
-
/**
|
|
12
|
-
* Extract IP address from request headers
|
|
13
|
-
* Checks x-forwarded-for and x-real-ip headers
|
|
14
|
-
*
|
|
15
|
-
* @param request - Incoming request
|
|
16
|
-
* @returns IP address or null if not found
|
|
17
|
-
*/
|
|
18
|
-
export function getIPFromRequest(request) {
|
|
19
|
-
const headers = request.headers;
|
|
20
|
-
// Check x-forwarded-for first (common for proxies/load balancers)
|
|
21
|
-
const forwardedFor = headers.get("x-forwarded-for");
|
|
22
|
-
if (forwardedFor) {
|
|
23
|
-
// Take the first IP in the chain (original client)
|
|
24
|
-
const firstIP = forwardedFor.split(",")[0];
|
|
25
|
-
if (firstIP) {
|
|
26
|
-
return firstIP.trim();
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
// Check x-real-ip (nginx)
|
|
30
|
-
const realIP = headers.get("x-real-ip");
|
|
31
|
-
if (realIP) {
|
|
32
|
-
return realIP.trim();
|
|
33
|
-
}
|
|
34
|
-
return null;
|
|
35
|
-
}
|
|
36
|
-
//# sourceMappingURL=hash.js.map
|
package/dist/utils/hash.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"hash.js","sourceRoot":"","sources":["../../src/utils/hash.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAEpC;;;;;GAKG;AACH,MAAM,UAAU,MAAM,CAAC,EAAU;IAC/B,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACvD,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAAC,OAAgB;IAC/C,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IAEhC,kEAAkE;IAClE,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;IACpD,IAAI,YAAY,EAAE,CAAC;QACjB,mDAAmD;QACnD,MAAM,OAAO,GAAG,YAAY,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QAC3C,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO,OAAO,CAAC,IAAI,EAAE,CAAC;QACxB,CAAC;IACH,CAAC;IAED,0BAA0B;IAC1B,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IACxC,IAAI,MAAM,EAAE,CAAC;QACX,OAAO,MAAM,CAAC,IAAI,EAAE,CAAC;IACvB,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Simple in-memory rate limiter
|
|
3
|
-
*/
|
|
4
|
-
export declare class RateLimiter {
|
|
5
|
-
private readonly requests;
|
|
6
|
-
private readonly maxRequests;
|
|
7
|
-
private readonly windowMs;
|
|
8
|
-
private cleanupInterval;
|
|
9
|
-
/**
|
|
10
|
-
* Create a new rate limiter
|
|
11
|
-
*
|
|
12
|
-
* @param maxRequests - Maximum requests allowed in the time window
|
|
13
|
-
* @param windowMs - Time window in milliseconds
|
|
14
|
-
*/
|
|
15
|
-
constructor(maxRequests?: number, windowMs?: number);
|
|
16
|
-
/**
|
|
17
|
-
* Check if an identifier is within rate limits
|
|
18
|
-
*
|
|
19
|
-
* @param identifier - Unique identifier (e.g., IP address)
|
|
20
|
-
* @returns true if request is allowed, false if rate limited
|
|
21
|
-
*/
|
|
22
|
-
check(identifier: string): boolean;
|
|
23
|
-
/**
|
|
24
|
-
* Clean up expired entries to prevent memory leaks
|
|
25
|
-
*/
|
|
26
|
-
private cleanup;
|
|
27
|
-
/**
|
|
28
|
-
* Stop the cleanup interval (for graceful shutdown)
|
|
29
|
-
*/
|
|
30
|
-
destroy(): void;
|
|
31
|
-
}
|
|
32
|
-
//# sourceMappingURL=rate-limit.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../../src/utils/rate-limit.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAoC;IAC7D,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,eAAe,CAA+C;IAEtE;;;;;OAKG;gBACS,WAAW,GAAE,MAAY,EAAE,QAAQ,GAAE,MAAc;IAe/D;;;;;OAKG;IACH,KAAK,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO;IAsBlC;;OAEG;IACH,OAAO,CAAC,OAAO;IAef;;OAEG;IACH,OAAO,IAAI,IAAI;CAMhB"}
|
package/dist/utils/rate-limit.js
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Simple in-memory rate limiter
|
|
3
|
-
*/
|
|
4
|
-
export class RateLimiter {
|
|
5
|
-
/**
|
|
6
|
-
* Create a new rate limiter
|
|
7
|
-
*
|
|
8
|
-
* @param maxRequests - Maximum requests allowed in the time window
|
|
9
|
-
* @param windowMs - Time window in milliseconds
|
|
10
|
-
*/
|
|
11
|
-
constructor(maxRequests = 100, windowMs = 60000) {
|
|
12
|
-
this.requests = new Map();
|
|
13
|
-
this.cleanupInterval = null;
|
|
14
|
-
this.maxRequests = maxRequests;
|
|
15
|
-
this.windowMs = windowMs;
|
|
16
|
-
// Cleanup expired entries every minute
|
|
17
|
-
this.cleanupInterval = setInterval(() => {
|
|
18
|
-
this.cleanup();
|
|
19
|
-
}, 60000);
|
|
20
|
-
// Prevent the interval from keeping the process alive
|
|
21
|
-
if (this.cleanupInterval.unref) {
|
|
22
|
-
this.cleanupInterval.unref();
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* Check if an identifier is within rate limits
|
|
27
|
-
*
|
|
28
|
-
* @param identifier - Unique identifier (e.g., IP address)
|
|
29
|
-
* @returns true if request is allowed, false if rate limited
|
|
30
|
-
*/
|
|
31
|
-
check(identifier) {
|
|
32
|
-
const now = Date.now();
|
|
33
|
-
const windowStart = now - this.windowMs;
|
|
34
|
-
// Get existing requests for this identifier
|
|
35
|
-
const existing = this.requests.get(identifier) ?? [];
|
|
36
|
-
// Filter to only requests within the current window
|
|
37
|
-
const recentRequests = existing.filter((time) => time > windowStart);
|
|
38
|
-
// Check if limit exceeded
|
|
39
|
-
if (recentRequests.length >= this.maxRequests) {
|
|
40
|
-
return false;
|
|
41
|
-
}
|
|
42
|
-
// Add current request
|
|
43
|
-
recentRequests.push(now);
|
|
44
|
-
this.requests.set(identifier, recentRequests);
|
|
45
|
-
return true;
|
|
46
|
-
}
|
|
47
|
-
/**
|
|
48
|
-
* Clean up expired entries to prevent memory leaks
|
|
49
|
-
*/
|
|
50
|
-
cleanup() {
|
|
51
|
-
const now = Date.now();
|
|
52
|
-
const windowStart = now - this.windowMs;
|
|
53
|
-
for (const [identifier, timestamps] of this.requests.entries()) {
|
|
54
|
-
const recentRequests = timestamps.filter((time) => time > windowStart);
|
|
55
|
-
if (recentRequests.length === 0) {
|
|
56
|
-
this.requests.delete(identifier);
|
|
57
|
-
}
|
|
58
|
-
else {
|
|
59
|
-
this.requests.set(identifier, recentRequests);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
/**
|
|
64
|
-
* Stop the cleanup interval (for graceful shutdown)
|
|
65
|
-
*/
|
|
66
|
-
destroy() {
|
|
67
|
-
if (this.cleanupInterval) {
|
|
68
|
-
clearInterval(this.cleanupInterval);
|
|
69
|
-
this.cleanupInterval = null;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
//# sourceMappingURL=rate-limit.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"rate-limit.js","sourceRoot":"","sources":["../../src/utils/rate-limit.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,OAAO,WAAW;IAMtB;;;;;OAKG;IACH,YAAY,cAAsB,GAAG,EAAE,WAAmB,KAAK;QAX9C,aAAQ,GAA0B,IAAI,GAAG,EAAE,CAAC;QAGrD,oBAAe,GAA0C,IAAI,CAAC;QASpE,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;QAC/B,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QAEzB,uCAAuC;QACvC,IAAI,CAAC,eAAe,GAAG,WAAW,CAAC,GAAG,EAAE;YACtC,IAAI,CAAC,OAAO,EAAE,CAAC;QACjB,CAAC,EAAE,KAAK,CAAC,CAAC;QAEV,sDAAsD;QACtD,IAAI,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,CAAC;YAC/B,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,CAAC;QAC/B,CAAC;IACH,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,UAAkB;QACtB,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,WAAW,GAAG,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC;QAExC,4CAA4C;QAC5C,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC;QAErD,oDAAoD;QACpD,MAAM,cAAc,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,GAAG,WAAW,CAAC,CAAC;QAErE,0BAA0B;QAC1B,IAAI,cAAc,CAAC,MAAM,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YAC9C,OAAO,KAAK,CAAC;QACf,CAAC;QAED,sBAAsB;QACtB,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACzB,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,UAAU,EAAE,cAAc,CAAC,CAAC;QAE9C,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;OAEG;IACK,OAAO;QACb,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,WAAW,GAAG,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC;QAExC,KAAK,MAAM,CAAC,UAAU,EAAE,UAAU,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC;YAC/D,MAAM,cAAc,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,GAAG,WAAW,CAAC,CAAC;YAEvE,IAAI,cAAc,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAChC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;YACnC,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,UAAU,EAAE,cAAc,CAAC,CAAC;YAChD,CAAC;QACH,CAAC;IACH,CAAC;IAED;;OAEG;IACH,OAAO;QACL,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;YACzB,aAAa,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;YACpC,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC;QAC9B,CAAC;IACH,CAAC;CACF"}
|
package/src/db/schema-mysql.sql
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
-- Locallytics MySQL Schema
|
|
2
|
-
|
|
3
|
-
CREATE TABLE IF NOT EXISTS locallytics_pageviews (
|
|
4
|
-
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
5
|
-
session_id VARCHAR(255) NOT NULL,
|
|
6
|
-
page_url TEXT NOT NULL,
|
|
7
|
-
referrer TEXT,
|
|
8
|
-
user_agent VARCHAR(512) NOT NULL,
|
|
9
|
-
screen_width INT NOT NULL,
|
|
10
|
-
screen_height INT NOT NULL,
|
|
11
|
-
ip_hash VARCHAR(64),
|
|
12
|
-
timestamp DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
13
|
-
INDEX idx_pageviews_timestamp (timestamp DESC),
|
|
14
|
-
INDEX idx_pageviews_session (session_id),
|
|
15
|
-
INDEX idx_pageviews_page_url (page_url(255)),
|
|
16
|
-
INDEX idx_pageviews_referrer (referrer(255))
|
|
17
|
-
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
package/src/db/schema-sqlite.sql
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
-- Locallytics SQLite Schema
|
|
2
|
-
|
|
3
|
-
CREATE TABLE IF NOT EXISTS locallytics_pageviews (
|
|
4
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
5
|
-
session_id TEXT NOT NULL,
|
|
6
|
-
page_url TEXT NOT NULL,
|
|
7
|
-
referrer TEXT,
|
|
8
|
-
user_agent TEXT NOT NULL,
|
|
9
|
-
screen_width INTEGER NOT NULL,
|
|
10
|
-
screen_height INTEGER NOT NULL,
|
|
11
|
-
ip_hash TEXT,
|
|
12
|
-
timestamp TEXT NOT NULL DEFAULT (datetime('now'))
|
|
13
|
-
);
|
|
14
|
-
|
|
15
|
-
-- Index for time-based queries (most common)
|
|
16
|
-
CREATE INDEX IF NOT EXISTS idx_pageviews_timestamp
|
|
17
|
-
ON locallytics_pageviews(timestamp DESC);
|
|
18
|
-
|
|
19
|
-
-- Index for session lookups
|
|
20
|
-
CREATE INDEX IF NOT EXISTS idx_pageviews_session
|
|
21
|
-
ON locallytics_pageviews(session_id);
|
|
22
|
-
|
|
23
|
-
-- Index for page URL grouping
|
|
24
|
-
CREATE INDEX IF NOT EXISTS idx_pageviews_page_url
|
|
25
|
-
ON locallytics_pageviews(page_url);
|
|
26
|
-
|
|
27
|
-
-- Index for referrer grouping
|
|
28
|
-
CREATE INDEX IF NOT EXISTS idx_pageviews_referrer
|
|
29
|
-
ON locallytics_pageviews(referrer);
|
package/src/db/schema.sql
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
-- Locallytics PostgreSQL Schema
|
|
2
|
-
-- Run this to create the required table and indexes
|
|
3
|
-
|
|
4
|
-
CREATE TABLE IF NOT EXISTS locallytics_pageviews (
|
|
5
|
-
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
6
|
-
session_id TEXT NOT NULL,
|
|
7
|
-
page_url TEXT NOT NULL,
|
|
8
|
-
referrer TEXT,
|
|
9
|
-
user_agent TEXT NOT NULL,
|
|
10
|
-
screen_width INTEGER NOT NULL,
|
|
11
|
-
screen_height INTEGER NOT NULL,
|
|
12
|
-
ip_hash TEXT,
|
|
13
|
-
timestamp TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
14
|
-
);
|
|
15
|
-
|
|
16
|
-
-- Index for time-based queries (most common)
|
|
17
|
-
CREATE INDEX IF NOT EXISTS idx_pageviews_timestamp
|
|
18
|
-
ON locallytics_pageviews(timestamp DESC);
|
|
19
|
-
|
|
20
|
-
-- Index for session lookups
|
|
21
|
-
CREATE INDEX IF NOT EXISTS idx_pageviews_session
|
|
22
|
-
ON locallytics_pageviews(session_id);
|
|
23
|
-
|
|
24
|
-
-- Index for page URL grouping
|
|
25
|
-
CREATE INDEX IF NOT EXISTS idx_pageviews_page_url
|
|
26
|
-
ON locallytics_pageviews(page_url);
|
|
27
|
-
|
|
28
|
-
-- Partial index for referrer (only non-null values)
|
|
29
|
-
CREATE INDEX IF NOT EXISTS idx_pageviews_referrer
|
|
30
|
-
ON locallytics_pageviews(referrer)
|
|
31
|
-
WHERE referrer IS NOT NULL;
|
|
32
|
-
|
|
33
|
-
-- Composite index for time + session queries
|
|
34
|
-
CREATE INDEX IF NOT EXISTS idx_pageviews_timestamp_session
|
|
35
|
-
ON locallytics_pageviews(timestamp DESC, session_id);
|