@linkforty/core 1.20.0 → 1.21.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/README.md +7 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/database.d.ts.map +1 -1
- package/dist/lib/database.js +79 -1
- package/dist/lib/database.js.map +1 -1
- package/dist/lib/link-safety.d.ts +95 -0
- package/dist/lib/link-safety.d.ts.map +1 -0
- package/dist/lib/link-safety.js +158 -0
- package/dist/lib/link-safety.js.map +1 -0
- package/dist/lib/link-safety.test.d.ts +2 -0
- package/dist/lib/link-safety.test.d.ts.map +1 -0
- package/dist/lib/link-safety.test.js +117 -0
- package/dist/lib/link-safety.test.js.map +1 -0
- package/dist/routes/health.d.ts +24 -0
- package/dist/routes/health.d.ts.map +1 -0
- package/dist/routes/health.js +56 -0
- package/dist/routes/health.js.map +1 -0
- package/dist/routes/health.test.d.ts +2 -0
- package/dist/routes/health.test.d.ts.map +1 -0
- package/dist/routes/health.test.js +82 -0
- package/dist/routes/health.test.js.map +1 -0
- package/dist/routes/index.d.ts +1 -0
- package/dist/routes/index.d.ts.map +1 -1
- package/dist/routes/index.js +1 -0
- package/dist/routes/index.js.map +1 -1
- package/dist/routes/redirect.d.ts +8 -1
- package/dist/routes/redirect.d.ts.map +1 -1
- package/dist/routes/redirect.js +66 -1
- package/dist/routes/redirect.js.map +1 -1
- package/dist/routes/redirect.safety.test.d.ts +2 -0
- package/dist/routes/redirect.safety.test.d.ts.map +1 -0
- package/dist/routes/redirect.safety.test.js +181 -0
- package/dist/routes/redirect.safety.test.js.map +1 -0
- package/dist/routes/sdk-cache-bypass.test.d.ts +2 -0
- package/dist/routes/sdk-cache-bypass.test.d.ts.map +1 -0
- package/dist/routes/sdk-cache-bypass.test.js +167 -0
- package/dist/routes/sdk-cache-bypass.test.js.map +1 -0
- package/dist/routes/sdk.d.ts.map +1 -1
- package/dist/routes/sdk.js +60 -4
- package/dist/routes/sdk.js.map +1 -1
- package/package.json +1 -3
- package/dist/scripts/backfill-attribution.d.ts +0 -2
- package/dist/scripts/backfill-attribution.d.ts.map +0 -1
- package/dist/scripts/backfill-attribution.js +0 -40
- package/dist/scripts/backfill-attribution.js.map +0 -1
- package/dist/scripts/backfill-bot-flags.d.ts +0 -2
- package/dist/scripts/backfill-bot-flags.d.ts.map +0 -1
- package/dist/scripts/backfill-bot-flags.js +0 -45
- package/dist/scripts/backfill-bot-flags.js.map +0 -1
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { FastifyInstance } from 'fastify';
|
|
2
|
+
/**
|
|
3
|
+
* Health endpoints.
|
|
4
|
+
*
|
|
5
|
+
* Without these, `GET /health` fell through to the catch-all redirect route
|
|
6
|
+
* `/:shortCode` and was answered as a short-code lookup — a 404 at best, and on
|
|
7
|
+
* a self-hosted install a 500, which made the Docker HEALTHCHECK (which targets
|
|
8
|
+
* /health) permanently unhealthy and buried the real error (issue #35).
|
|
9
|
+
*
|
|
10
|
+
* Two levels, following the usual liveness/readiness split:
|
|
11
|
+
*
|
|
12
|
+
* /health — liveness. The process is up and serving. Never touches the
|
|
13
|
+
* database, so a database blip cannot get the container killed
|
|
14
|
+
* by an orchestrator that restarts on a failing probe.
|
|
15
|
+
* /health/ready — readiness. Confirms the database answers, and reports Redis
|
|
16
|
+
* when it is configured. 503 when the database is unreachable,
|
|
17
|
+
* so a load balancer can drain the instance.
|
|
18
|
+
*
|
|
19
|
+
* Both are static paths, which Fastify's router always prefers over the
|
|
20
|
+
* parametric `/:shortCode`, so they cannot be shadowed by a short link — and by
|
|
21
|
+
* the same token `health` is no longer usable as a short code.
|
|
22
|
+
*/
|
|
23
|
+
export declare function healthRoutes(fastify: FastifyInstance): Promise<void>;
|
|
24
|
+
//# sourceMappingURL=health.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"health.d.ts","sourceRoot":"","sources":["../../src/routes/health.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAG1C;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,YAAY,CAAC,OAAO,EAAE,eAAe,iBAkC1D"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { db } from '../lib/database.js';
|
|
2
|
+
/**
|
|
3
|
+
* Health endpoints.
|
|
4
|
+
*
|
|
5
|
+
* Without these, `GET /health` fell through to the catch-all redirect route
|
|
6
|
+
* `/:shortCode` and was answered as a short-code lookup — a 404 at best, and on
|
|
7
|
+
* a self-hosted install a 500, which made the Docker HEALTHCHECK (which targets
|
|
8
|
+
* /health) permanently unhealthy and buried the real error (issue #35).
|
|
9
|
+
*
|
|
10
|
+
* Two levels, following the usual liveness/readiness split:
|
|
11
|
+
*
|
|
12
|
+
* /health — liveness. The process is up and serving. Never touches the
|
|
13
|
+
* database, so a database blip cannot get the container killed
|
|
14
|
+
* by an orchestrator that restarts on a failing probe.
|
|
15
|
+
* /health/ready — readiness. Confirms the database answers, and reports Redis
|
|
16
|
+
* when it is configured. 503 when the database is unreachable,
|
|
17
|
+
* so a load balancer can drain the instance.
|
|
18
|
+
*
|
|
19
|
+
* Both are static paths, which Fastify's router always prefers over the
|
|
20
|
+
* parametric `/:shortCode`, so they cannot be shadowed by a short link — and by
|
|
21
|
+
* the same token `health` is no longer usable as a short code.
|
|
22
|
+
*/
|
|
23
|
+
export async function healthRoutes(fastify) {
|
|
24
|
+
fastify.get('/health', async () => ({
|
|
25
|
+
status: 'ok',
|
|
26
|
+
uptime: Math.floor(process.uptime()),
|
|
27
|
+
}));
|
|
28
|
+
fastify.get('/health/ready', async (_request, reply) => {
|
|
29
|
+
const checks = {};
|
|
30
|
+
try {
|
|
31
|
+
await db.query('SELECT 1');
|
|
32
|
+
checks.database = 'ok';
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
fastify.log.error(`Health: database check failed: ${error}`);
|
|
36
|
+
checks.database = 'error';
|
|
37
|
+
}
|
|
38
|
+
if (fastify.redis) {
|
|
39
|
+
try {
|
|
40
|
+
await fastify.redis.ping();
|
|
41
|
+
checks.redis = 'ok';
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
// Redis is an optional cache with database fallback, so a failure here
|
|
45
|
+
// is degraded, not unready — it must not flip the overall status.
|
|
46
|
+
checks.redis = 'error';
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
const ready = checks.database === 'ok';
|
|
50
|
+
return reply.status(ready ? 200 : 503).send({
|
|
51
|
+
status: ready ? 'ok' : 'error',
|
|
52
|
+
checks,
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=health.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"health.js","sourceRoot":"","sources":["../../src/routes/health.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,EAAE,EAAE,MAAM,oBAAoB,CAAC;AAExC;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,OAAwB;IACzD,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC;QAClC,MAAM,EAAE,IAAI;QACZ,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;KACrC,CAAC,CAAC,CAAC;IAEJ,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE;QACrD,MAAM,MAAM,GAA2B,EAAE,CAAC;QAE1C,IAAI,CAAC;YACH,MAAM,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;YAC3B,MAAM,CAAC,QAAQ,GAAG,IAAI,CAAC;QACzB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,kCAAkC,KAAK,EAAE,CAAC,CAAC;YAC7D,MAAM,CAAC,QAAQ,GAAG,OAAO,CAAC;QAC5B,CAAC;QAED,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;YAClB,IAAI,CAAC;gBACH,MAAM,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;gBAC3B,MAAM,CAAC,KAAK,GAAG,IAAI,CAAC;YACtB,CAAC;YAAC,MAAM,CAAC;gBACP,uEAAuE;gBACvE,kEAAkE;gBAClE,MAAM,CAAC,KAAK,GAAG,OAAO,CAAC;YACzB,CAAC;QACH,CAAC;QAED,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,KAAK,IAAI,CAAC;QACvC,OAAO,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;YAC1C,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO;YAC9B,MAAM;SACP,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"health.test.d.ts","sourceRoot":"","sources":["../../src/routes/health.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Route-level tests for the health endpoints.
|
|
3
|
+
*
|
|
4
|
+
* The regression these lock down (issue #35): /health used to have no route at
|
|
5
|
+
* all, so it fell through to the catch-all redirect `/:shortCode` and was
|
|
6
|
+
* answered as a short-code lookup. The last test registers the real redirect
|
|
7
|
+
* plugin alongside health to prove the static path wins.
|
|
8
|
+
*/
|
|
9
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
10
|
+
import Fastify from 'fastify';
|
|
11
|
+
import { healthRoutes } from './health.js';
|
|
12
|
+
import { redirectRoutes } from './redirect.js';
|
|
13
|
+
const query = vi.fn();
|
|
14
|
+
vi.mock('../lib/database.js', () => ({
|
|
15
|
+
db: {
|
|
16
|
+
query: (...args) => query(...args),
|
|
17
|
+
},
|
|
18
|
+
}));
|
|
19
|
+
let app;
|
|
20
|
+
beforeEach(async () => {
|
|
21
|
+
query.mockReset();
|
|
22
|
+
query.mockResolvedValue({ rows: [{ '?column?': 1 }], rowCount: 1 });
|
|
23
|
+
app = Fastify();
|
|
24
|
+
await app.register(healthRoutes);
|
|
25
|
+
await app.ready();
|
|
26
|
+
});
|
|
27
|
+
afterEach(async () => {
|
|
28
|
+
await app.close();
|
|
29
|
+
});
|
|
30
|
+
describe('GET /health', () => {
|
|
31
|
+
it('reports the process is up', async () => {
|
|
32
|
+
const res = await app.inject({ method: 'GET', url: '/health' });
|
|
33
|
+
expect(res.statusCode).toBe(200);
|
|
34
|
+
expect(res.json()).toMatchObject({ status: 'ok' });
|
|
35
|
+
});
|
|
36
|
+
it('never touches the database, so a database outage cannot fail liveness', async () => {
|
|
37
|
+
query.mockRejectedValue(new Error('connection refused'));
|
|
38
|
+
const res = await app.inject({ method: 'GET', url: '/health' });
|
|
39
|
+
expect(res.statusCode).toBe(200);
|
|
40
|
+
expect(query).not.toHaveBeenCalled();
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
describe('GET /health/ready', () => {
|
|
44
|
+
it('is ready when the database answers', async () => {
|
|
45
|
+
const res = await app.inject({ method: 'GET', url: '/health/ready' });
|
|
46
|
+
expect(res.statusCode).toBe(200);
|
|
47
|
+
expect(res.json()).toEqual({ status: 'ok', checks: { database: 'ok' } });
|
|
48
|
+
});
|
|
49
|
+
it('is 503 when the database is unreachable', async () => {
|
|
50
|
+
query.mockRejectedValue(new Error('connection refused'));
|
|
51
|
+
const res = await app.inject({ method: 'GET', url: '/health/ready' });
|
|
52
|
+
expect(res.statusCode).toBe(503);
|
|
53
|
+
expect(res.json()).toEqual({ status: 'error', checks: { database: 'error' } });
|
|
54
|
+
});
|
|
55
|
+
it('reports a failing Redis as degraded, not unready', async () => {
|
|
56
|
+
const withRedis = Fastify();
|
|
57
|
+
withRedis.decorate('redis', { ping: async () => { throw new Error('down'); } });
|
|
58
|
+
await withRedis.register(healthRoutes);
|
|
59
|
+
await withRedis.ready();
|
|
60
|
+
const res = await withRedis.inject({ method: 'GET', url: '/health/ready' });
|
|
61
|
+
expect(res.statusCode).toBe(200);
|
|
62
|
+
expect(res.json()).toEqual({ status: 'ok', checks: { database: 'ok', redis: 'error' } });
|
|
63
|
+
await withRedis.close();
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
describe('regression: /health is not swallowed by the redirect route', () => {
|
|
67
|
+
it('answers health, not a short-code lookup, when both plugins are registered', async () => {
|
|
68
|
+
const combined = Fastify();
|
|
69
|
+
// Redirect first — the static route must win on specificity, not order.
|
|
70
|
+
await combined.register(redirectRoutes);
|
|
71
|
+
await combined.register(healthRoutes);
|
|
72
|
+
await combined.ready();
|
|
73
|
+
const res = await combined.inject({ method: 'GET', url: '/health' });
|
|
74
|
+
expect(res.statusCode).toBe(200);
|
|
75
|
+
expect(res.json()).toMatchObject({ status: 'ok' });
|
|
76
|
+
// The redirect handler would have run a links lookup; health must not.
|
|
77
|
+
const linkLookups = query.mock.calls.filter(([sql]) => /FROM links/i.test(String(sql)));
|
|
78
|
+
expect(linkLookups).toHaveLength(0);
|
|
79
|
+
await combined.close();
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
//# sourceMappingURL=health.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"health.test.js","sourceRoot":"","sources":["../../src/routes/health.test.ts"],"names":[],"mappings":"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"}
|
package/dist/routes/index.d.ts
CHANGED
|
@@ -8,4 +8,5 @@ export { templateRoutes } from './templates.js';
|
|
|
8
8
|
export { previewRoutes } from './preview.js';
|
|
9
9
|
export { debugRoutes } from './debug.js';
|
|
10
10
|
export { wellKnownRoutes } from './well-known.js';
|
|
11
|
+
export { healthRoutes } from './health.js';
|
|
11
12
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +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;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACrC,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC"}
|
|
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;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACrC,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/routes/index.js
CHANGED
|
@@ -8,4 +8,5 @@ export { templateRoutes } from './templates.js';
|
|
|
8
8
|
export { previewRoutes } from './preview.js';
|
|
9
9
|
export { debugRoutes } from './debug.js';
|
|
10
10
|
export { wellKnownRoutes } from './well-known.js';
|
|
11
|
+
export { healthRoutes } from './health.js';
|
|
11
12
|
//# sourceMappingURL=index.js.map
|
package/dist/routes/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","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;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACrC,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","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;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACrC,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -29,5 +29,12 @@ export declare function pickMobileFallbackUrl(device: 'ios' | 'android', userAge
|
|
|
29
29
|
url: string;
|
|
30
30
|
reason: string;
|
|
31
31
|
} | null;
|
|
32
|
-
export
|
|
32
|
+
export interface RedirectRouteOptions {
|
|
33
|
+
/**
|
|
34
|
+
* Absolute URL of an abuse-reporting page. When set, the interstitial warning
|
|
35
|
+
* page links to it. Optional — deployments without one simply omit the link.
|
|
36
|
+
*/
|
|
37
|
+
abuseReportUrl?: string;
|
|
38
|
+
}
|
|
39
|
+
export declare function redirectRoutes(fastify: FastifyInstance, options?: RedirectRouteOptions): Promise<void>;
|
|
33
40
|
//# sourceMappingURL=redirect.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"redirect.d.ts","sourceRoot":"","sources":["../../src/routes/redirect.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"redirect.d.ts","sourceRoot":"","sources":["../../src/routes/redirect.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAa1C;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAa5D;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAiBhE;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,KAAK,GAAG,SAAS,EACzB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GAAG,IAAI,EACrB,UAAU,EAAE,MAAM,GAAG,IAAI,EACzB,cAAc,EAAE,MAAM,GAAG,IAAI,GAC5B;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAexC;AAkDD,MAAM,WAAW,oBAAoB;IACnC;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,wBAAsB,cAAc,CAClC,OAAO,EAAE,eAAe,EACxB,OAAO,GAAE,oBAAyB,iBA0hBnC"}
|
package/dist/routes/redirect.js
CHANGED
|
@@ -5,6 +5,7 @@ import { parseUserAgent, getLocationFromIP, buildRedirectUrl, detectDevice } fro
|
|
|
5
5
|
import { storeFingerprintForClick } from '../lib/fingerprint.js';
|
|
6
6
|
import { emitClickEvent } from '../lib/event-emitter.js';
|
|
7
7
|
import { classifyBot, edgeBotSignal } from '../lib/bot-detection.js';
|
|
8
|
+
import { evaluateLinkSafety, generateWarningLinkHTML, createOwnerSuspensionSelect, } from '../lib/link-safety.js';
|
|
8
9
|
/**
|
|
9
10
|
* Detect iOS in-app browsers where Universal Links don't fire.
|
|
10
11
|
* These browsers use WKWebView which bypasses the Universal Links mechanism.
|
|
@@ -127,7 +128,36 @@ function generateInterstitialHTML(schemeUrl, fallbackUrl, title) {
|
|
|
127
128
|
</script>
|
|
128
129
|
</body></html>`;
|
|
129
130
|
}
|
|
130
|
-
export async function redirectRoutes(fastify) {
|
|
131
|
+
export async function redirectRoutes(fastify, options = {}) {
|
|
132
|
+
/**
|
|
133
|
+
* Owner-restriction support is detected rather than assumed.
|
|
134
|
+
*
|
|
135
|
+
* The redirect query joins `organizations` for settings, and that table is now
|
|
136
|
+
* created by this package (#35) — before that fix a stock self-hosted install
|
|
137
|
+
* 500'd on every redirect with 42P01, which made this probe's guarantee hollow:
|
|
138
|
+
* it guarded the column while the table itself was missing.
|
|
139
|
+
*
|
|
140
|
+
* The column still needs probing separately, because `suspended_at` is added by
|
|
141
|
+
* downstream consumers that model owner restriction rather than by this package.
|
|
142
|
+
* Probing once and building the SELECT accordingly avoids failing every redirect
|
|
143
|
+
* on a missing column.
|
|
144
|
+
*
|
|
145
|
+
* Scoped to this registration rather than the module: module-level state would
|
|
146
|
+
* be shared by every createServer() in the process, so two servers pointed at
|
|
147
|
+
* different databases would share whichever answer landed first — and it forced
|
|
148
|
+
* a test-only reset export onto the package's public API.
|
|
149
|
+
*
|
|
150
|
+
* The in-flight promise is memoised, not just its result, so N concurrent cold
|
|
151
|
+
* requests issue one probe rather than N.
|
|
152
|
+
*
|
|
153
|
+
* Probed lazily because the database may not be reachable at registration time.
|
|
154
|
+
* A probe failure is treated as "unsupported", so it can never take the redirect
|
|
155
|
+
* path down.
|
|
156
|
+
*/
|
|
157
|
+
const resolveOwnerSuspensionSelect = createOwnerSuspensionSelect({
|
|
158
|
+
query: (sql) => db.query(sql),
|
|
159
|
+
onSupported: () => fastify.log.info('Redirect: owner restriction supported (organizations.suspended_at present)'),
|
|
160
|
+
});
|
|
131
161
|
// Helper function to handle the actual redirect logic
|
|
132
162
|
async function handleRedirect(request, reply, shortCode, templateSlug) {
|
|
133
163
|
let linkData = null;
|
|
@@ -146,11 +176,13 @@ export async function redirectRoutes(fastify) {
|
|
|
146
176
|
// Build query based on whether template slug is provided
|
|
147
177
|
let query;
|
|
148
178
|
let params;
|
|
179
|
+
const ownerSuspensionColumn = await resolveOwnerSuspensionSelect();
|
|
149
180
|
if (templateSlug) {
|
|
150
181
|
// Template-based URL: verify both template and link match
|
|
151
182
|
// Also fetch template settings and org settings for URL fallback chain
|
|
152
183
|
query = `
|
|
153
184
|
SELECT l.*, t.settings AS template_settings, o.settings AS org_settings
|
|
185
|
+
${ownerSuspensionColumn}
|
|
154
186
|
FROM links l
|
|
155
187
|
LEFT JOIN link_templates t ON l.template_id = t.id
|
|
156
188
|
LEFT JOIN organizations o ON l.organization_id = o.id
|
|
@@ -165,6 +197,7 @@ export async function redirectRoutes(fastify) {
|
|
|
165
197
|
// Also fetch template settings and org settings for URL fallback chain
|
|
166
198
|
query = `
|
|
167
199
|
SELECT l.*, t.settings AS template_settings, o.settings AS org_settings
|
|
200
|
+
${ownerSuspensionColumn}
|
|
168
201
|
FROM links l
|
|
169
202
|
LEFT JOIN link_templates t ON l.template_id = t.id
|
|
170
203
|
LEFT JOIN organizations o ON l.organization_id = o.id
|
|
@@ -189,6 +222,38 @@ export async function redirectRoutes(fastify) {
|
|
|
189
222
|
}
|
|
190
223
|
}
|
|
191
224
|
const link = JSON.parse(linkData);
|
|
225
|
+
// Safety gate, applied after the cache read so it covers cached rows too.
|
|
226
|
+
//
|
|
227
|
+
// It does NOT close the stale-cache window on its own, and an earlier version
|
|
228
|
+
// of this comment wrongly claimed it did: a cached row carries the value of
|
|
229
|
+
// `is_active` as at cache time, so reading it here sees the same stale `true`
|
|
230
|
+
// the old code did. Staleness is handled by invalidateLinkResolutionCache(),
|
|
231
|
+
// called when a link is updated or deleted.
|
|
232
|
+
//
|
|
233
|
+
// `is_active` therefore stays filtered in SQL — an inactive link never needs
|
|
234
|
+
// fetching or caching. What genuinely cannot be expressed in the WHERE clause
|
|
235
|
+
// is `warn_at`, which needs the row in hand to choose between redirecting and
|
|
236
|
+
// serving an interstitial.
|
|
237
|
+
const safety = evaluateLinkSafety({
|
|
238
|
+
isActive: link.is_active,
|
|
239
|
+
warnAt: link.warn_at,
|
|
240
|
+
ownerSuspendedAt: link.owner_suspended_at,
|
|
241
|
+
});
|
|
242
|
+
if (safety === 'block') {
|
|
243
|
+
// Same response as an unknown code — see evaluateLinkSafety for why.
|
|
244
|
+
return reply.status(404).send({ error: 'Link not found' });
|
|
245
|
+
}
|
|
246
|
+
if (safety === 'warn') {
|
|
247
|
+
// No click is recorded here. A warning view is not a click on the link, and
|
|
248
|
+
// counting it would silently inflate the owner's analytics.
|
|
249
|
+
const destination = link.original_url || link.web_fallback_url || link.deep_link_path || '';
|
|
250
|
+
return reply
|
|
251
|
+
.status(200)
|
|
252
|
+
.header('X-Robots-Tag', 'noindex, nofollow')
|
|
253
|
+
.header('Cache-Control', 'no-store')
|
|
254
|
+
.type('text/html')
|
|
255
|
+
.send(generateWarningLinkHTML(destination, { reportUrl: options?.abuseReportUrl }));
|
|
256
|
+
}
|
|
192
257
|
// Check targeting rules BEFORE redirecting
|
|
193
258
|
if (link.targeting_rules) {
|
|
194
259
|
const userAgent = request.headers['user-agent'] || '';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"redirect.js","sourceRoot":"","sources":["../../src/routes/redirect.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"redirect.js","sourceRoot":"","sources":["../../src/routes/redirect.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redirect.safety.test.d.ts","sourceRoot":"","sources":["../../src/routes/redirect.safety.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Route-level tests for the link safety gate.
|
|
3
|
+
*
|
|
4
|
+
* These drive the real `redirectRoutes` plugin through fastify.inject(), with only
|
|
5
|
+
* the data layer mocked. The unit tests in lib/link-safety.test.ts already cover
|
|
6
|
+
* the decision table; what matters here is the behaviour a visitor actually gets:
|
|
7
|
+
* status codes, headers, whether a 302 happens, and whether a click is recorded.
|
|
8
|
+
*/
|
|
9
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
10
|
+
import Fastify from 'fastify';
|
|
11
|
+
// vi.mock is hoisted above these imports, so redirect.js still receives the mock.
|
|
12
|
+
import { redirectRoutes } from './redirect.js';
|
|
13
|
+
const query = vi.fn();
|
|
14
|
+
vi.mock('../lib/database.js', () => ({
|
|
15
|
+
db: {
|
|
16
|
+
query: (...args) => query(...args),
|
|
17
|
+
},
|
|
18
|
+
}));
|
|
19
|
+
/** A resolved link row as the redirect query would return it. */
|
|
20
|
+
function linkRow(overrides = {}) {
|
|
21
|
+
return {
|
|
22
|
+
id: '00000000-0000-0000-0000-0000000000aa',
|
|
23
|
+
short_code: 'abc123',
|
|
24
|
+
original_url: 'https://example.com/landing',
|
|
25
|
+
web_fallback_url: null,
|
|
26
|
+
deep_link_path: null,
|
|
27
|
+
is_active: true,
|
|
28
|
+
warn_at: null,
|
|
29
|
+
owner_suspended_at: null,
|
|
30
|
+
expires_at: null,
|
|
31
|
+
targeting_rules: null,
|
|
32
|
+
template_settings: null,
|
|
33
|
+
org_settings: null,
|
|
34
|
+
utm_parameters: null,
|
|
35
|
+
append_click_id: false,
|
|
36
|
+
...overrides,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* @param row the link the lookup resolves to, or null for "not found"
|
|
41
|
+
* @param suspensionColumn whether organizations.suspended_at exists in this database
|
|
42
|
+
*/
|
|
43
|
+
function mockDb(row, suspensionColumn = true) {
|
|
44
|
+
query.mockReset();
|
|
45
|
+
query.mockImplementation(async (sql) => {
|
|
46
|
+
if (/information_schema\.columns/i.test(sql)) {
|
|
47
|
+
return { rows: suspensionColumn ? [{ '?column?': 1 }] : [], rowCount: suspensionColumn ? 1 : 0 };
|
|
48
|
+
}
|
|
49
|
+
if (/^\s*SELECT\s+l\.\*/i.test(sql) || /FROM links l/i.test(sql)) {
|
|
50
|
+
return { rows: row ? [row] : [], rowCount: row ? 1 : 0 };
|
|
51
|
+
}
|
|
52
|
+
// click inserts, anything else
|
|
53
|
+
return { rows: [], rowCount: 0 };
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
/** Did any click get written? Flushes the setImmediate the redirect uses. */
|
|
57
|
+
async function clickWasRecorded() {
|
|
58
|
+
await new Promise((r) => setImmediate(r));
|
|
59
|
+
return query.mock.calls.some(([sql]) => /INSERT INTO click_events/i.test(String(sql)));
|
|
60
|
+
}
|
|
61
|
+
let app;
|
|
62
|
+
beforeEach(async () => {
|
|
63
|
+
// No probe reset needed: the probe is scoped to each registration, so a fresh
|
|
64
|
+
// Fastify instance per test gets a fresh probe. That the reset export is gone is
|
|
65
|
+
// the point — it existed only to work around module-global state.
|
|
66
|
+
app = Fastify();
|
|
67
|
+
await app.register(redirectRoutes, { abuseReportUrl: 'https://example.org/abuse' });
|
|
68
|
+
await app.ready();
|
|
69
|
+
});
|
|
70
|
+
afterEach(async () => {
|
|
71
|
+
// Click recording is fire-and-forget (setImmediate). Let any pending insert from
|
|
72
|
+
// this test finish BEFORE the next test resets the mock, otherwise a stray click
|
|
73
|
+
// from a previous redirect lands in the next test's call log and looks like a bug.
|
|
74
|
+
await new Promise((r) => setImmediate(r));
|
|
75
|
+
await app.close();
|
|
76
|
+
});
|
|
77
|
+
describe('redirect safety gate', () => {
|
|
78
|
+
it('redirects a healthy link', async () => {
|
|
79
|
+
mockDb(linkRow());
|
|
80
|
+
const res = await app.inject({ method: 'GET', url: '/abc123' });
|
|
81
|
+
expect(res.statusCode).toBe(302);
|
|
82
|
+
expect(res.headers.location).toBe('https://example.com/landing');
|
|
83
|
+
});
|
|
84
|
+
it('404s an inactive link', async () => {
|
|
85
|
+
mockDb(linkRow({ is_active: false }));
|
|
86
|
+
const res = await app.inject({ method: 'GET', url: '/abc123' });
|
|
87
|
+
expect(res.statusCode).toBe(404);
|
|
88
|
+
});
|
|
89
|
+
it('404s when the owner is restricted, even though the link itself is fine', async () => {
|
|
90
|
+
mockDb(linkRow({ owner_suspended_at: '2026-08-10T00:00:00Z' }));
|
|
91
|
+
const res = await app.inject({ method: 'GET', url: '/abc123' });
|
|
92
|
+
expect(res.statusCode).toBe(404);
|
|
93
|
+
});
|
|
94
|
+
it('gives a restricted owner the SAME response as an unknown code, leaking nothing', async () => {
|
|
95
|
+
mockDb(linkRow({ owner_suspended_at: '2026-08-10T00:00:00Z' }));
|
|
96
|
+
const restricted = await app.inject({ method: 'GET', url: '/abc123' });
|
|
97
|
+
mockDb(null);
|
|
98
|
+
const unknown = await app.inject({ method: 'GET', url: '/nosuchcode' });
|
|
99
|
+
expect(restricted.statusCode).toBe(unknown.statusCode);
|
|
100
|
+
expect(restricted.body).toBe(unknown.body);
|
|
101
|
+
});
|
|
102
|
+
describe('a link flagged to warn', () => {
|
|
103
|
+
it('serves an interstitial instead of redirecting', async () => {
|
|
104
|
+
mockDb(linkRow({ warn_at: '2026-08-10T00:00:00Z' }));
|
|
105
|
+
const res = await app.inject({ method: 'GET', url: '/abc123' });
|
|
106
|
+
expect(res.statusCode).toBe(200);
|
|
107
|
+
expect(res.statusCode).not.toBe(302);
|
|
108
|
+
expect(res.headers['content-type']).toMatch(/text\/html/);
|
|
109
|
+
expect(res.body).toContain('Check this link before continuing');
|
|
110
|
+
});
|
|
111
|
+
it('shows the destination the short link was hiding', async () => {
|
|
112
|
+
mockDb(linkRow({ warn_at: '2026-08-10T00:00:00Z' }));
|
|
113
|
+
const res = await app.inject({ method: 'GET', url: '/abc123' });
|
|
114
|
+
expect(res.body).toContain('https://example.com/landing');
|
|
115
|
+
});
|
|
116
|
+
it('asks not to be indexed or cached', async () => {
|
|
117
|
+
mockDb(linkRow({ warn_at: '2026-08-10T00:00:00Z' }));
|
|
118
|
+
const res = await app.inject({ method: 'GET', url: '/abc123' });
|
|
119
|
+
expect(res.headers['x-robots-tag']).toMatch(/noindex/);
|
|
120
|
+
expect(res.headers['cache-control']).toMatch(/no-store/);
|
|
121
|
+
});
|
|
122
|
+
it('links to the configured reporting page', async () => {
|
|
123
|
+
mockDb(linkRow({ warn_at: '2026-08-10T00:00:00Z' }));
|
|
124
|
+
const res = await app.inject({ method: 'GET', url: '/abc123' });
|
|
125
|
+
expect(res.body).toContain('https://example.org/abuse');
|
|
126
|
+
});
|
|
127
|
+
it('records NO click — a warning view is not a click on the link', async () => {
|
|
128
|
+
mockDb(linkRow({ warn_at: '2026-08-10T00:00:00Z' }));
|
|
129
|
+
await app.inject({ method: 'GET', url: '/abc123' });
|
|
130
|
+
expect(await clickWasRecorded()).toBe(false);
|
|
131
|
+
});
|
|
132
|
+
it('falls back to the web fallback url when there is no original url', async () => {
|
|
133
|
+
mockDb(linkRow({ warn_at: '2026-08-10T00:00:00Z', original_url: '', web_fallback_url: 'https://example.net/x' }));
|
|
134
|
+
const res = await app.inject({ method: 'GET', url: '/abc123' });
|
|
135
|
+
expect(res.body).toContain('https://example.net/x');
|
|
136
|
+
});
|
|
137
|
+
it('is outranked by owner restriction', async () => {
|
|
138
|
+
mockDb(linkRow({ warn_at: '2026-08-10T00:00:00Z', owner_suspended_at: '2026-08-10T00:00:00Z' }));
|
|
139
|
+
const res = await app.inject({ method: 'GET', url: '/abc123' });
|
|
140
|
+
expect(res.statusCode).toBe(404);
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
describe('a database without organizations.suspended_at', () => {
|
|
144
|
+
it('still resolves links normally rather than erroring', async () => {
|
|
145
|
+
mockDb(linkRow(), /* suspensionColumn */ false);
|
|
146
|
+
const res = await app.inject({ method: 'GET', url: '/abc123' });
|
|
147
|
+
expect(res.statusCode).toBe(302);
|
|
148
|
+
});
|
|
149
|
+
it('never asks for the column it just proved absent', async () => {
|
|
150
|
+
mockDb(linkRow(), false);
|
|
151
|
+
await app.inject({ method: 'GET', url: '/abc123' });
|
|
152
|
+
const lookups = query.mock.calls
|
|
153
|
+
.map(([sql]) => String(sql))
|
|
154
|
+
.filter((sql) => /FROM links l/i.test(sql));
|
|
155
|
+
expect(lookups.length).toBeGreaterThan(0);
|
|
156
|
+
for (const sql of lookups)
|
|
157
|
+
expect(sql).not.toMatch(/owner_suspended_at/);
|
|
158
|
+
});
|
|
159
|
+
it('probes once even under CONCURRENT cold requests', async () => {
|
|
160
|
+
// The sequential test below passes trivially because inject() awaits. This one
|
|
161
|
+
// fires them together, which is what the memoised promise actually buys.
|
|
162
|
+
mockDb(linkRow(), false);
|
|
163
|
+
await Promise.all([
|
|
164
|
+
app.inject({ method: 'GET', url: '/abc123' }),
|
|
165
|
+
app.inject({ method: 'GET', url: '/abc123' }),
|
|
166
|
+
app.inject({ method: 'GET', url: '/abc123' }),
|
|
167
|
+
app.inject({ method: 'GET', url: '/abc123' }),
|
|
168
|
+
]);
|
|
169
|
+
const probes = query.mock.calls.filter(([sql]) => /information_schema\.columns/i.test(String(sql)));
|
|
170
|
+
expect(probes).toHaveLength(1);
|
|
171
|
+
});
|
|
172
|
+
it('probes only once across sequential requests', async () => {
|
|
173
|
+
mockDb(linkRow(), false);
|
|
174
|
+
await app.inject({ method: 'GET', url: '/abc123' });
|
|
175
|
+
await app.inject({ method: 'GET', url: '/abc123' });
|
|
176
|
+
const probes = query.mock.calls.filter(([sql]) => /information_schema\.columns/i.test(String(sql)));
|
|
177
|
+
expect(probes).toHaveLength(1);
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
//# sourceMappingURL=redirect.safety.test.js.map
|