@hasna/shortlinks 0.1.19 → 0.1.20
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/cli/index.js +24 -9
- package/dist/index.js +24 -9
- package/dist/server.d.ts +5 -0
- package/dist/server.js +24 -9
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -4138,6 +4138,9 @@ function getClientIp(request) {
|
|
|
4138
4138
|
function isExpired(link) {
|
|
4139
4139
|
return Boolean(link.expires_at && new Date(link.expires_at).getTime() <= Date.now());
|
|
4140
4140
|
}
|
|
4141
|
+
function logRecordClickError(link) {
|
|
4142
|
+
console.error(`[shortlinks] Click analytics recording failed for ${link.hostname}/${link.slug}.`);
|
|
4143
|
+
}
|
|
4141
4144
|
function createShortlinksHandler(options = {}) {
|
|
4142
4145
|
const store = options.store || new ShortlinksStore(options.dbPath);
|
|
4143
4146
|
const redirectStatus = options.redirectStatus || 302;
|
|
@@ -4176,16 +4179,28 @@ function createShortlinksHandler(options = {}) {
|
|
|
4176
4179
|
if (isExpired(link))
|
|
4177
4180
|
return json({ error: "Shortlink is expired.", slug, host }, 410);
|
|
4178
4181
|
if (request.method === "GET") {
|
|
4179
|
-
|
|
4180
|
-
|
|
4181
|
-
|
|
4182
|
-
|
|
4183
|
-
|
|
4184
|
-
|
|
4185
|
-
|
|
4186
|
-
|
|
4182
|
+
try {
|
|
4183
|
+
await store.recordClick(link, {
|
|
4184
|
+
ip: getClientIp(request),
|
|
4185
|
+
userAgent: request.headers.get("user-agent"),
|
|
4186
|
+
referer: request.headers.get("referer"),
|
|
4187
|
+
country: request.headers.get("cf-ipcountry"),
|
|
4188
|
+
metadata: {
|
|
4189
|
+
path: url.pathname,
|
|
4190
|
+
query: url.search
|
|
4191
|
+
}
|
|
4192
|
+
});
|
|
4193
|
+
} catch (error) {
|
|
4194
|
+
if (options.onRecordClickError) {
|
|
4195
|
+
try {
|
|
4196
|
+
await options.onRecordClickError(error, { link, request });
|
|
4197
|
+
} catch {
|
|
4198
|
+
logRecordClickError(link);
|
|
4199
|
+
}
|
|
4200
|
+
} else {
|
|
4201
|
+
logRecordClickError(link);
|
|
4187
4202
|
}
|
|
4188
|
-
}
|
|
4203
|
+
}
|
|
4189
4204
|
}
|
|
4190
4205
|
return Response.redirect(link.destination_url, redirectStatus);
|
|
4191
4206
|
};
|
package/dist/index.js
CHANGED
|
@@ -874,6 +874,9 @@ function getClientIp(request) {
|
|
|
874
874
|
function isExpired(link) {
|
|
875
875
|
return Boolean(link.expires_at && new Date(link.expires_at).getTime() <= Date.now());
|
|
876
876
|
}
|
|
877
|
+
function logRecordClickError(link) {
|
|
878
|
+
console.error(`[shortlinks] Click analytics recording failed for ${link.hostname}/${link.slug}.`);
|
|
879
|
+
}
|
|
877
880
|
function createShortlinksHandler(options = {}) {
|
|
878
881
|
const store = options.store || new ShortlinksStore(options.dbPath);
|
|
879
882
|
const redirectStatus = options.redirectStatus || 302;
|
|
@@ -912,16 +915,28 @@ function createShortlinksHandler(options = {}) {
|
|
|
912
915
|
if (isExpired(link))
|
|
913
916
|
return json({ error: "Shortlink is expired.", slug, host }, 410);
|
|
914
917
|
if (request.method === "GET") {
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
918
|
+
try {
|
|
919
|
+
await store.recordClick(link, {
|
|
920
|
+
ip: getClientIp(request),
|
|
921
|
+
userAgent: request.headers.get("user-agent"),
|
|
922
|
+
referer: request.headers.get("referer"),
|
|
923
|
+
country: request.headers.get("cf-ipcountry"),
|
|
924
|
+
metadata: {
|
|
925
|
+
path: url.pathname,
|
|
926
|
+
query: url.search
|
|
927
|
+
}
|
|
928
|
+
});
|
|
929
|
+
} catch (error) {
|
|
930
|
+
if (options.onRecordClickError) {
|
|
931
|
+
try {
|
|
932
|
+
await options.onRecordClickError(error, { link, request });
|
|
933
|
+
} catch {
|
|
934
|
+
logRecordClickError(link);
|
|
935
|
+
}
|
|
936
|
+
} else {
|
|
937
|
+
logRecordClickError(link);
|
|
923
938
|
}
|
|
924
|
-
}
|
|
939
|
+
}
|
|
925
940
|
}
|
|
926
941
|
return Response.redirect(link.destination_url, redirectStatus);
|
|
927
942
|
};
|
package/dist/server.d.ts
CHANGED
|
@@ -12,11 +12,16 @@ export interface ShortlinksRuntimeStore {
|
|
|
12
12
|
resolve(hostname: string, slug: string): Link | null | Promise<Link | null>;
|
|
13
13
|
recordClick(link: Link, input?: ClickInput): unknown | Promise<unknown>;
|
|
14
14
|
}
|
|
15
|
+
export interface RecordClickErrorContext {
|
|
16
|
+
link: Link;
|
|
17
|
+
request: Request;
|
|
18
|
+
}
|
|
15
19
|
export interface ShortlinksHandlerOptions {
|
|
16
20
|
store?: ShortlinksRuntimeStore;
|
|
17
21
|
dbPath?: string;
|
|
18
22
|
defaultHost?: string;
|
|
19
23
|
redirectStatus?: 301 | 302 | 307 | 308;
|
|
24
|
+
onRecordClickError?: (error: unknown, context: RecordClickErrorContext) => void | Promise<void>;
|
|
20
25
|
}
|
|
21
26
|
export declare function createShortlinksHandler(options?: ShortlinksHandlerOptions): (request: Request) => Response | Promise<Response>;
|
|
22
27
|
export declare function serveShortlinks(options?: ShortlinksHandlerOptions & {
|
package/dist/server.js
CHANGED
|
@@ -555,6 +555,9 @@ function getClientIp(request) {
|
|
|
555
555
|
function isExpired(link) {
|
|
556
556
|
return Boolean(link.expires_at && new Date(link.expires_at).getTime() <= Date.now());
|
|
557
557
|
}
|
|
558
|
+
function logRecordClickError(link) {
|
|
559
|
+
console.error(`[shortlinks] Click analytics recording failed for ${link.hostname}/${link.slug}.`);
|
|
560
|
+
}
|
|
558
561
|
function createShortlinksHandler(options = {}) {
|
|
559
562
|
const store = options.store || new ShortlinksStore(options.dbPath);
|
|
560
563
|
const redirectStatus = options.redirectStatus || 302;
|
|
@@ -593,16 +596,28 @@ function createShortlinksHandler(options = {}) {
|
|
|
593
596
|
if (isExpired(link))
|
|
594
597
|
return json({ error: "Shortlink is expired.", slug, host }, 410);
|
|
595
598
|
if (request.method === "GET") {
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
599
|
+
try {
|
|
600
|
+
await store.recordClick(link, {
|
|
601
|
+
ip: getClientIp(request),
|
|
602
|
+
userAgent: request.headers.get("user-agent"),
|
|
603
|
+
referer: request.headers.get("referer"),
|
|
604
|
+
country: request.headers.get("cf-ipcountry"),
|
|
605
|
+
metadata: {
|
|
606
|
+
path: url.pathname,
|
|
607
|
+
query: url.search
|
|
608
|
+
}
|
|
609
|
+
});
|
|
610
|
+
} catch (error) {
|
|
611
|
+
if (options.onRecordClickError) {
|
|
612
|
+
try {
|
|
613
|
+
await options.onRecordClickError(error, { link, request });
|
|
614
|
+
} catch {
|
|
615
|
+
logRecordClickError(link);
|
|
616
|
+
}
|
|
617
|
+
} else {
|
|
618
|
+
logRecordClickError(link);
|
|
604
619
|
}
|
|
605
|
-
}
|
|
620
|
+
}
|
|
606
621
|
}
|
|
607
622
|
return Response.redirect(link.destination_url, redirectStatus);
|
|
608
623
|
};
|
package/package.json
CHANGED