@c15t/backend 2.0.0-rc.1 → 2.0.0-rc.10
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 +3 -3
- package/dist/302.js +473 -0
- package/dist/583.js +540 -0
- package/dist/915.js +1771 -0
- package/dist/cache.cjs +5 -5
- package/dist/cache.js +4 -415
- package/dist/core.cjs +1356 -120
- package/dist/core.js +163 -1981
- package/dist/db/adapters/drizzle.cjs +1 -1
- package/dist/db/adapters/drizzle.js +1 -2
- package/dist/db/adapters/kysely.cjs +1 -1
- package/dist/db/adapters/kysely.js +1 -2
- package/dist/db/adapters/mongo.cjs +1 -1
- package/dist/db/adapters/mongo.js +1 -2
- package/dist/db/adapters/prisma.cjs +1 -1
- package/dist/db/adapters/prisma.js +1 -2
- package/dist/db/adapters/typeorm.cjs +1 -1
- package/dist/db/adapters/typeorm.js +1 -2
- package/dist/db/adapters.cjs +1 -1
- package/dist/db/migrator.cjs +1 -1
- package/dist/db/schema.cjs +43 -3
- package/dist/db/schema.js +35 -4
- package/dist/define-config.cjs +1 -1
- package/dist/edge.cjs +1106 -0
- package/dist/edge.js +190 -0
- package/dist/router.cjs +885 -123
- package/dist/router.js +1 -1507
- package/dist/{types.cjs → types/index.cjs} +1 -1
- package/{dist → dist-types}/cache/adapters/cloudflare-kv.d.ts +0 -1
- package/{dist → dist-types}/cache/adapters/index.d.ts +0 -1
- package/{dist → dist-types}/cache/adapters/memory.d.ts +0 -1
- package/{dist → dist-types}/cache/adapters/upstash-redis.d.ts +0 -1
- package/{dist → dist-types}/cache/gvl-resolver.d.ts +0 -1
- package/{dist → dist-types}/cache/index.d.ts +0 -1
- package/{dist → dist-types}/cache/keys.d.ts +0 -1
- package/{dist → dist-types}/cache/types.d.ts +0 -1
- package/{dist → dist-types}/core.d.ts +8 -1
- package/{dist → dist-types}/db/migrator/index.d.ts +0 -1
- package/dist-types/db/registry/consent-policy.d.ts +78 -0
- package/{dist → dist-types}/db/registry/consent-purpose.d.ts +0 -1
- package/{dist → dist-types}/db/registry/domain.d.ts +0 -1
- package/dist-types/db/registry/index.d.ts +118 -0
- package/dist-types/db/registry/runtime-policy-decision.d.ts +60 -0
- package/{dist → dist-types}/db/registry/subject.d.ts +0 -2
- package/{dist → dist-types}/db/registry/types.d.ts +1 -1
- package/{dist → dist-types}/db/registry/utils/generate-id.d.ts +0 -1
- package/{dist → dist-types}/db/registry/utils.d.ts +0 -1
- package/{dist → dist-types}/db/schema/1.0.0/audit-log.d.ts +0 -1
- package/{dist → dist-types}/db/schema/1.0.0/consent-policy.d.ts +0 -1
- package/{dist → dist-types}/db/schema/1.0.0/consent-purpose.d.ts +0 -1
- package/{dist → dist-types}/db/schema/1.0.0/consent-record.d.ts +0 -1
- package/{dist → dist-types}/db/schema/1.0.0/consent.d.ts +1 -2
- package/{dist → dist-types}/db/schema/1.0.0/domain.d.ts +0 -1
- package/{dist → dist-types}/db/schema/1.0.0/index.d.ts +0 -32
- package/{dist → dist-types}/db/schema/1.0.0/subject.d.ts +0 -2
- package/{dist → dist-types}/db/schema/2.0.0/audit-log.d.ts +1 -2
- package/{dist → dist-types}/db/schema/2.0.0/consent-policy.d.ts +3 -3
- package/{dist → dist-types}/db/schema/2.0.0/consent-purpose.d.ts +1 -2
- package/{dist → dist-types}/db/schema/2.0.0/consent.d.ts +7 -2
- package/{dist → dist-types}/db/schema/2.0.0/domain.d.ts +1 -2
- package/{dist → dist-types}/db/schema/2.0.0/index.d.ts +455 -28
- package/dist-types/db/schema/2.0.0/runtime-policy-decision.d.ts +23 -0
- package/{dist → dist-types}/db/schema/2.0.0/subject.d.ts +1 -3
- package/{dist → dist-types}/db/schema/index.d.ts +908 -86
- package/{dist → dist-types}/db/tenant-scope.d.ts +0 -1
- package/dist-types/define-config.d.ts +17 -0
- package/dist-types/edge/index.d.ts +5 -0
- package/dist-types/edge/init-handler.d.ts +40 -0
- package/dist-types/edge/resolve-consent.d.ts +80 -0
- package/dist-types/edge/types.d.ts +13 -0
- package/{dist → dist-types}/handlers/consent/check.handler.d.ts +0 -1
- package/{src/handlers/consent/index.ts → dist-types/handlers/consent/index.d.ts} +0 -1
- package/{dist → dist-types}/handlers/init/geo.d.ts +2 -3
- package/{dist → dist-types}/handlers/init/index.d.ts +2 -3
- package/dist-types/handlers/init/policy.d.ts +26 -0
- package/dist-types/handlers/init/resolve-init.d.ts +44 -0
- package/dist-types/handlers/init/translations.d.ts +48 -0
- package/dist-types/handlers/legal-document/current.handler.d.ts +11 -0
- package/dist-types/handlers/legal-document/snapshot.d.ts +39 -0
- package/dist-types/handlers/policy/snapshot.d.ts +99 -0
- package/{src/handlers/status/index.ts → dist-types/handlers/status/index.d.ts} +0 -1
- package/{dist → dist-types}/handlers/status/status.handler.d.ts +0 -1
- package/{dist → dist-types}/handlers/subject/get.handler.d.ts +3 -2
- package/{src/handlers/subject/index.ts → dist-types/handlers/subject/index.d.ts} +0 -1
- package/{dist → dist-types}/handlers/subject/list.handler.d.ts +3 -2
- package/{dist → dist-types}/handlers/subject/patch.handler.d.ts +0 -2
- package/{dist → dist-types}/handlers/subject/post.handler.d.ts +12 -1
- package/{dist → dist-types}/handlers/utils/consent-enrichment.d.ts +3 -1
- package/{dist → dist-types}/init.d.ts +4 -7
- package/{dist → dist-types}/middleware/auth/index.d.ts +0 -1
- package/{dist → dist-types}/middleware/auth/validate-api-key.d.ts +0 -1
- package/{dist → dist-types}/middleware/cors/cors.d.ts +0 -1
- package/{src/middleware/cors/index.ts → dist-types/middleware/cors/index.d.ts} +0 -1
- package/{dist → dist-types}/middleware/cors/is-origin-trusted.d.ts +0 -1
- package/{dist → dist-types}/middleware/cors/process-cors.d.ts +0 -1
- package/{dist → dist-types}/middleware/openapi/config.d.ts +0 -1
- package/{dist → dist-types}/middleware/openapi/handlers.d.ts +0 -1
- package/{src/middleware/openapi/index.ts → dist-types/middleware/openapi/index.d.ts} +0 -1
- package/{dist → dist-types}/middleware/process-ip/index.d.ts +0 -1
- package/dist-types/policies/builder.d.ts +127 -0
- package/dist-types/policies/defaults.d.ts +2 -0
- package/dist-types/policies/matchers.d.ts +3 -0
- package/{dist → dist-types}/router.d.ts +0 -1
- package/{dist → dist-types}/routes/consent.d.ts +0 -1
- package/{dist → dist-types}/routes/index.d.ts +1 -1
- package/{dist → dist-types}/routes/init.d.ts +0 -1
- package/dist-types/routes/legal-document.d.ts +7 -0
- package/{dist → dist-types}/routes/status.d.ts +0 -1
- package/{dist → dist-types}/routes/subject.d.ts +0 -1
- package/{dist → dist-types}/types/api.d.ts +0 -1
- package/dist-types/types/index.d.ts +464 -0
- package/dist-types/utils/background.d.ts +6 -0
- package/{dist → dist-types}/utils/create-telemetry-options.d.ts +1 -2
- package/{dist → dist-types}/utils/env.d.ts +0 -1
- package/{dist → dist-types}/utils/extract-error-message.d.ts +0 -1
- package/{dist → dist-types}/utils/instrumentation.d.ts +2 -3
- package/{dist → dist-types}/utils/logger.d.ts +0 -1
- package/{dist → dist-types}/utils/metrics.d.ts +0 -1
- package/dist-types/version.d.ts +1 -0
- package/docs/README.md +49 -0
- package/docs/api/configuration.md +208 -0
- package/docs/api/endpoints.md +211 -0
- package/docs/guides/caching.md +85 -0
- package/docs/guides/database-setup.md +128 -0
- package/docs/guides/edge-deployment.md +251 -0
- package/docs/guides/framework-integration.md +142 -0
- package/docs/guides/iab-tcf.md +89 -0
- package/docs/guides/observability.md +96 -0
- package/docs/guides/policy-packs.md +396 -0
- package/docs/quickstart.md +129 -0
- package/package.json +51 -37
- package/.turbo/turbo-build.log +0 -49
- package/CHANGELOG.md +0 -99
- package/dist/cache/adapters/cloudflare-kv.d.ts.map +0 -1
- package/dist/cache/adapters/index.d.ts.map +0 -1
- package/dist/cache/adapters/memory.d.ts.map +0 -1
- package/dist/cache/adapters/upstash-redis.d.ts.map +0 -1
- package/dist/cache/gvl-resolver.d.ts.map +0 -1
- package/dist/cache/index.d.ts.map +0 -1
- package/dist/cache/keys.d.ts.map +0 -1
- package/dist/cache/types.d.ts.map +0 -1
- package/dist/core.d.ts.map +0 -1
- package/dist/db/adapters/drizzle.d.ts +0 -2
- package/dist/db/adapters/drizzle.d.ts.map +0 -1
- package/dist/db/adapters/index.d.ts +0 -2
- package/dist/db/adapters/index.d.ts.map +0 -1
- package/dist/db/adapters/kysely.d.ts +0 -2
- package/dist/db/adapters/kysely.d.ts.map +0 -1
- package/dist/db/adapters/mongo.d.ts +0 -2
- package/dist/db/adapters/mongo.d.ts.map +0 -1
- package/dist/db/adapters/prisma.d.ts +0 -2
- package/dist/db/adapters/prisma.d.ts.map +0 -1
- package/dist/db/adapters/typeorm.d.ts +0 -2
- package/dist/db/adapters/typeorm.d.ts.map +0 -1
- package/dist/db/migrator/index.d.ts.map +0 -1
- package/dist/db/registry/consent-policy.d.ts +0 -23
- package/dist/db/registry/consent-policy.d.ts.map +0 -1
- package/dist/db/registry/consent-purpose.d.ts.map +0 -1
- package/dist/db/registry/domain.d.ts.map +0 -1
- package/dist/db/registry/index.d.ts +0 -57
- package/dist/db/registry/index.d.ts.map +0 -1
- package/dist/db/registry/subject.d.ts.map +0 -1
- package/dist/db/registry/types.d.ts.map +0 -1
- package/dist/db/registry/utils/generate-id.d.ts.map +0 -1
- package/dist/db/registry/utils.d.ts.map +0 -1
- package/dist/db/schema/1.0.0/audit-log.d.ts.map +0 -1
- package/dist/db/schema/1.0.0/consent-policy.d.ts.map +0 -1
- package/dist/db/schema/1.0.0/consent-purpose.d.ts.map +0 -1
- package/dist/db/schema/1.0.0/consent-record.d.ts.map +0 -1
- package/dist/db/schema/1.0.0/consent.d.ts.map +0 -1
- package/dist/db/schema/1.0.0/domain.d.ts.map +0 -1
- package/dist/db/schema/1.0.0/index.d.ts.map +0 -1
- package/dist/db/schema/1.0.0/subject.d.ts.map +0 -1
- package/dist/db/schema/2.0.0/audit-log.d.ts.map +0 -1
- package/dist/db/schema/2.0.0/consent-policy.d.ts.map +0 -1
- package/dist/db/schema/2.0.0/consent-purpose.d.ts.map +0 -1
- package/dist/db/schema/2.0.0/consent.d.ts.map +0 -1
- package/dist/db/schema/2.0.0/domain.d.ts.map +0 -1
- package/dist/db/schema/2.0.0/index.d.ts.map +0 -1
- package/dist/db/schema/2.0.0/subject.d.ts.map +0 -1
- package/dist/db/schema/index.d.ts.map +0 -1
- package/dist/db/tenant-scope.d.ts.map +0 -1
- package/dist/define-config.d.ts +0 -5
- package/dist/define-config.d.ts.map +0 -1
- package/dist/handlers/consent/check.handler.d.ts.map +0 -1
- package/dist/handlers/consent/index.d.ts +0 -12
- package/dist/handlers/consent/index.d.ts.map +0 -1
- package/dist/handlers/init/geo.d.ts.map +0 -1
- package/dist/handlers/init/index.d.ts.map +0 -1
- package/dist/handlers/init/translations.d.ts +0 -28
- package/dist/handlers/init/translations.d.ts.map +0 -1
- package/dist/handlers/status/index.d.ts +0 -7
- package/dist/handlers/status/index.d.ts.map +0 -1
- package/dist/handlers/status/status.handler.d.ts.map +0 -1
- package/dist/handlers/subject/get.handler.d.ts.map +0 -1
- package/dist/handlers/subject/index.d.ts +0 -10
- package/dist/handlers/subject/index.d.ts.map +0 -1
- package/dist/handlers/subject/list.handler.d.ts.map +0 -1
- package/dist/handlers/subject/patch.handler.d.ts.map +0 -1
- package/dist/handlers/subject/post.handler.d.ts.map +0 -1
- package/dist/handlers/utils/consent-enrichment.d.ts.map +0 -1
- package/dist/init.d.ts.map +0 -1
- package/dist/middleware/auth/index.d.ts.map +0 -1
- package/dist/middleware/auth/validate-api-key.d.ts.map +0 -1
- package/dist/middleware/cors/cors.d.ts.map +0 -1
- package/dist/middleware/cors/index.d.ts +0 -30
- package/dist/middleware/cors/index.d.ts.map +0 -1
- package/dist/middleware/cors/is-origin-trusted.d.ts.map +0 -1
- package/dist/middleware/cors/process-cors.d.ts.map +0 -1
- package/dist/middleware/openapi/config.d.ts.map +0 -1
- package/dist/middleware/openapi/handlers.d.ts.map +0 -1
- package/dist/middleware/openapi/index.d.ts +0 -12
- package/dist/middleware/openapi/index.d.ts.map +0 -1
- package/dist/middleware/process-ip/index.d.ts.map +0 -1
- package/dist/router.d.ts.map +0 -1
- package/dist/routes/consent.d.ts.map +0 -1
- package/dist/routes/index.d.ts.map +0 -1
- package/dist/routes/init.d.ts.map +0 -1
- package/dist/routes/status.d.ts.map +0 -1
- package/dist/routes/subject.d.ts.map +0 -1
- package/dist/types/api.d.ts.map +0 -1
- package/dist/types/index.d.ts +0 -255
- package/dist/types/index.d.ts.map +0 -1
- package/dist/utils/create-telemetry-options.d.ts.map +0 -1
- package/dist/utils/env.d.ts.map +0 -1
- package/dist/utils/extract-error-message.d.ts.map +0 -1
- package/dist/utils/index.d.ts +0 -4
- package/dist/utils/index.d.ts.map +0 -1
- package/dist/utils/instrumentation.d.ts.map +0 -1
- package/dist/utils/logger.d.ts.map +0 -1
- package/dist/utils/metrics.d.ts.map +0 -1
- package/dist/version.d.ts +0 -2
- package/dist/version.d.ts.map +0 -1
- package/knip.json +0 -31
- package/rslib.config.ts +0 -93
- package/src/cache/adapters/cloudflare-kv.ts +0 -71
- package/src/cache/adapters/index.ts +0 -22
- package/src/cache/adapters/memory.ts +0 -111
- package/src/cache/adapters/upstash-redis.ts +0 -113
- package/src/cache/gvl-resolver.ts +0 -289
- package/src/cache/index.ts +0 -34
- package/src/cache/keys.ts +0 -68
- package/src/cache/types.ts +0 -66
- package/src/core.ts +0 -368
- package/src/db/migrator/index.ts +0 -80
- package/src/db/registry/consent-policy.test.ts +0 -451
- package/src/db/registry/consent-policy.ts +0 -82
- package/src/db/registry/consent-purpose.test.ts +0 -428
- package/src/db/registry/consent-purpose.ts +0 -61
- package/src/db/registry/domain.test.ts +0 -445
- package/src/db/registry/domain.ts +0 -91
- package/src/db/registry/index.ts +0 -14
- package/src/db/registry/subject.test.ts +0 -388
- package/src/db/registry/subject.ts +0 -129
- package/src/db/registry/types.ts +0 -10
- package/src/db/registry/utils/generate-id.test.ts +0 -216
- package/src/db/registry/utils/generate-id.ts +0 -133
- package/src/db/registry/utils.ts +0 -133
- package/src/db/schema/1.0.0/audit-log.ts +0 -15
- package/src/db/schema/1.0.0/consent-policy.ts +0 -14
- package/src/db/schema/1.0.0/consent-purpose.ts +0 -14
- package/src/db/schema/1.0.0/consent-record.ts +0 -10
- package/src/db/schema/1.0.0/consent.ts +0 -20
- package/src/db/schema/1.0.0/domain.ts +0 -12
- package/src/db/schema/1.0.0/index.ts +0 -48
- package/src/db/schema/1.0.0/subject.ts +0 -12
- package/src/db/schema/2.0.0/audit-log.ts +0 -18
- package/src/db/schema/2.0.0/consent-policy.ts +0 -28
- package/src/db/schema/2.0.0/consent-purpose.ts +0 -12
- package/src/db/schema/2.0.0/consent.ts +0 -26
- package/src/db/schema/2.0.0/domain.ts +0 -12
- package/src/db/schema/2.0.0/index.ts +0 -47
- package/src/db/schema/2.0.0/subject.ts +0 -14
- package/src/db/schema/index.ts +0 -15
- package/src/db/tenant-scope.test.ts +0 -750
- package/src/db/tenant-scope.ts +0 -103
- package/src/define-config.ts +0 -5
- package/src/handlers/consent/check.handler.ts +0 -126
- package/src/handlers/init/geo.test.ts +0 -317
- package/src/handlers/init/geo.ts +0 -195
- package/src/handlers/init/index.test.ts +0 -205
- package/src/handlers/init/index.ts +0 -114
- package/src/handlers/init/translations.test.ts +0 -121
- package/src/handlers/init/translations.ts +0 -72
- package/src/handlers/status/status.handler.test.ts +0 -155
- package/src/handlers/status/status.handler.ts +0 -51
- package/src/handlers/subject/get.handler.ts +0 -93
- package/src/handlers/subject/list.handler.ts +0 -93
- package/src/handlers/subject/patch.handler.ts +0 -122
- package/src/handlers/subject/post.handler.test.ts +0 -294
- package/src/handlers/subject/post.handler.ts +0 -254
- package/src/handlers/utils/consent-enrichment.test.ts +0 -380
- package/src/handlers/utils/consent-enrichment.ts +0 -218
- package/src/init.test.ts +0 -126
- package/src/init.ts +0 -87
- package/src/middleware/auth/index.ts +0 -11
- package/src/middleware/auth/validate-api-key.test.ts +0 -86
- package/src/middleware/auth/validate-api-key.ts +0 -107
- package/src/middleware/cors/cors.test.ts +0 -135
- package/src/middleware/cors/cors.ts +0 -186
- package/src/middleware/cors/is-origin-trusted.test.ts +0 -164
- package/src/middleware/cors/is-origin-trusted.ts +0 -130
- package/src/middleware/cors/process-cors.ts +0 -91
- package/src/middleware/openapi/config.ts +0 -29
- package/src/middleware/openapi/handlers.ts +0 -34
- package/src/middleware/process-ip/index.test.ts +0 -195
- package/src/middleware/process-ip/index.ts +0 -199
- package/src/router.ts +0 -15
- package/src/routes/consent.ts +0 -52
- package/src/routes/index.ts +0 -10
- package/src/routes/init.ts +0 -102
- package/src/routes/status.ts +0 -46
- package/src/routes/subject.ts +0 -152
- package/src/types/api.ts +0 -48
- package/src/types/index.ts +0 -288
- package/src/utils/create-telemetry-options.test.ts +0 -302
- package/src/utils/create-telemetry-options.ts +0 -229
- package/src/utils/env.ts +0 -84
- package/src/utils/extract-error-message.ts +0 -21
- package/src/utils/instrumentation.test.ts +0 -185
- package/src/utils/instrumentation.ts +0 -196
- package/src/utils/logger.ts +0 -41
- package/src/utils/metrics.test.ts +0 -323
- package/src/utils/metrics.ts +0 -402
- package/src/utils/telemetry-pii.test.ts +0 -325
- package/src/version.ts +0 -2
- package/tsconfig.json +0 -11
- package/vitest.config.ts +0 -28
- /package/dist/{types.js → types/index.js} +0 -0
- /package/{src/db/adapters/drizzle.ts → dist-types/db/adapters/drizzle.d.ts} +0 -0
- /package/{src/db/adapters/index.ts → dist-types/db/adapters/index.d.ts} +0 -0
- /package/{src/db/adapters/kysely.ts → dist-types/db/adapters/kysely.d.ts} +0 -0
- /package/{src/db/adapters/mongo.ts → dist-types/db/adapters/mongo.d.ts} +0 -0
- /package/{src/db/adapters/prisma.ts → dist-types/db/adapters/prisma.d.ts} +0 -0
- /package/{src/db/adapters/typeorm.ts → dist-types/db/adapters/typeorm.d.ts} +0 -0
- /package/{src/utils/index.ts → dist-types/utils/index.d.ts} +0 -0
|
@@ -1,155 +0,0 @@
|
|
|
1
|
-
import { HTTPException } from 'hono/http-exception';
|
|
2
|
-
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
3
|
-
import { statusHandler } from './status.handler';
|
|
4
|
-
|
|
5
|
-
describe('statusHandler', () => {
|
|
6
|
-
const mockLogger = {
|
|
7
|
-
debug: vi.fn(),
|
|
8
|
-
error: vi.fn(),
|
|
9
|
-
info: vi.fn(),
|
|
10
|
-
warn: vi.fn(),
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
const createMockContext = (db: unknown) => {
|
|
14
|
-
const headers = new Headers();
|
|
15
|
-
headers.set('cf-ipcountry', 'US');
|
|
16
|
-
headers.set('x-vercel-ip-country-region', 'CA');
|
|
17
|
-
headers.set('accept-language', 'en-US');
|
|
18
|
-
|
|
19
|
-
const ctx = {
|
|
20
|
-
db,
|
|
21
|
-
logger: mockLogger,
|
|
22
|
-
headers,
|
|
23
|
-
ipAddress: '192.168.1.100',
|
|
24
|
-
userAgent: 'Mozilla/5.0',
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
let jsonData: unknown;
|
|
28
|
-
|
|
29
|
-
return {
|
|
30
|
-
get: (key: string) => {
|
|
31
|
-
if (key === 'c15tContext') {
|
|
32
|
-
return ctx;
|
|
33
|
-
}
|
|
34
|
-
return undefined;
|
|
35
|
-
},
|
|
36
|
-
json: vi.fn((data) => {
|
|
37
|
-
jsonData = data;
|
|
38
|
-
return data;
|
|
39
|
-
}),
|
|
40
|
-
getJsonData: () => jsonData,
|
|
41
|
-
req: {
|
|
42
|
-
raw: { headers },
|
|
43
|
-
},
|
|
44
|
-
};
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
afterEach(() => {
|
|
48
|
-
vi.clearAllMocks();
|
|
49
|
-
vi.restoreAllMocks();
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it('should return health info when database is working', async () => {
|
|
53
|
-
const db = {
|
|
54
|
-
findFirst: vi.fn().mockResolvedValue({ id: 'sub_123' }),
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
const mockCtx = createMockContext(db);
|
|
58
|
-
// @ts-expect-error - simplified test context
|
|
59
|
-
await statusHandler(mockCtx);
|
|
60
|
-
|
|
61
|
-
const result = mockCtx.getJsonData() as {
|
|
62
|
-
version: string;
|
|
63
|
-
timestamp: Date;
|
|
64
|
-
client: {
|
|
65
|
-
ip: string | null;
|
|
66
|
-
acceptLanguage: string | null;
|
|
67
|
-
userAgent: string | null;
|
|
68
|
-
region: {
|
|
69
|
-
countryCode: string | null;
|
|
70
|
-
regionCode: string | null;
|
|
71
|
-
};
|
|
72
|
-
};
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
expect(result.version).toBeDefined();
|
|
76
|
-
expect(result.timestamp).toBeInstanceOf(Date);
|
|
77
|
-
expect(result.client).toEqual({
|
|
78
|
-
ip: '192.168.1.100',
|
|
79
|
-
acceptLanguage: 'en-US',
|
|
80
|
-
userAgent: 'Mozilla/5.0',
|
|
81
|
-
region: {
|
|
82
|
-
countryCode: 'US',
|
|
83
|
-
regionCode: 'CA',
|
|
84
|
-
},
|
|
85
|
-
});
|
|
86
|
-
expect(db.findFirst).toHaveBeenCalledWith('subject', {});
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
it('should throw HTTPException when database query fails', async () => {
|
|
90
|
-
const db = {
|
|
91
|
-
findFirst: vi.fn().mockRejectedValue(new Error('DB Connection failed')),
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
const mockCtx = createMockContext(db);
|
|
95
|
-
// @ts-expect-error - simplified test context
|
|
96
|
-
const promise = statusHandler(mockCtx);
|
|
97
|
-
|
|
98
|
-
await expect(promise).rejects.toThrow(HTTPException);
|
|
99
|
-
await expect(promise).rejects.toMatchObject({
|
|
100
|
-
status: 503,
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
expect(mockLogger.error).toHaveBeenCalledWith(
|
|
104
|
-
'Database health check failed',
|
|
105
|
-
expect.objectContaining({ error: expect.any(Error) })
|
|
106
|
-
);
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
it('should handle missing geo headers', async () => {
|
|
110
|
-
const db = {
|
|
111
|
-
findFirst: vi.fn().mockResolvedValue({ id: 'sub_123' }),
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
const emptyHeaders = new Headers();
|
|
115
|
-
const ctx = {
|
|
116
|
-
db,
|
|
117
|
-
logger: mockLogger,
|
|
118
|
-
headers: emptyHeaders,
|
|
119
|
-
ipAddress: '1.2.3.4',
|
|
120
|
-
userAgent: 'Test Agent',
|
|
121
|
-
};
|
|
122
|
-
|
|
123
|
-
let jsonData: unknown;
|
|
124
|
-
const mockCtx = {
|
|
125
|
-
get: (key: string) => {
|
|
126
|
-
if (key === 'c15tContext') {
|
|
127
|
-
return ctx;
|
|
128
|
-
}
|
|
129
|
-
return undefined;
|
|
130
|
-
},
|
|
131
|
-
json: vi.fn((data) => {
|
|
132
|
-
jsonData = data;
|
|
133
|
-
return data;
|
|
134
|
-
}),
|
|
135
|
-
req: {
|
|
136
|
-
raw: { headers: emptyHeaders },
|
|
137
|
-
},
|
|
138
|
-
};
|
|
139
|
-
|
|
140
|
-
// @ts-expect-error - simplified test context
|
|
141
|
-
await statusHandler(mockCtx);
|
|
142
|
-
|
|
143
|
-
const result = jsonData as {
|
|
144
|
-
client: {
|
|
145
|
-
region: {
|
|
146
|
-
countryCode: string | null;
|
|
147
|
-
regionCode: string | null;
|
|
148
|
-
};
|
|
149
|
-
};
|
|
150
|
-
};
|
|
151
|
-
|
|
152
|
-
expect(result.client.region.countryCode).toBeNull();
|
|
153
|
-
expect(result.client.region.regionCode).toBeNull();
|
|
154
|
-
});
|
|
155
|
-
});
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* GET /status handler - Health check and status endpoint.
|
|
3
|
-
*
|
|
4
|
-
* @packageDocumentation
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import type { Context } from 'hono';
|
|
8
|
-
import { HTTPException } from 'hono/http-exception';
|
|
9
|
-
import type { C15TContext } from '~/types';
|
|
10
|
-
import { version } from '~/version';
|
|
11
|
-
import { getHeaders } from '../init';
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Handles the status request to check the health of the service and its dependencies.
|
|
15
|
-
*
|
|
16
|
-
* This handler performs a simple query to the database to ensure it is reachable and
|
|
17
|
-
* functional. It also extracts client information from the request headers.
|
|
18
|
-
*/
|
|
19
|
-
export const statusHandler = async (c: Context) => {
|
|
20
|
-
const ctx = c.get('c15tContext') as C15TContext;
|
|
21
|
-
|
|
22
|
-
const { countryCode, regionCode, acceptLanguage } = getHeaders(ctx.headers);
|
|
23
|
-
|
|
24
|
-
const clientInfo = {
|
|
25
|
-
ip: ctx.ipAddress ?? null,
|
|
26
|
-
acceptLanguage,
|
|
27
|
-
userAgent: ctx.userAgent ?? null,
|
|
28
|
-
region: {
|
|
29
|
-
countryCode,
|
|
30
|
-
regionCode,
|
|
31
|
-
},
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
try {
|
|
35
|
-
// Perform a simple query to verify database connectivity
|
|
36
|
-
await ctx.db.findFirst('subject', {});
|
|
37
|
-
|
|
38
|
-
return c.json({
|
|
39
|
-
version,
|
|
40
|
-
timestamp: new Date(),
|
|
41
|
-
client: clientInfo,
|
|
42
|
-
});
|
|
43
|
-
} catch (error) {
|
|
44
|
-
ctx.logger.error('Database health check failed', { error });
|
|
45
|
-
|
|
46
|
-
throw new HTTPException(503, {
|
|
47
|
-
message: 'Database health check failed',
|
|
48
|
-
cause: { code: 'SERVICE_UNAVAILABLE', error },
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
};
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* GET /subjects/:id handler - Check this device's consent status.
|
|
3
|
-
*
|
|
4
|
-
* @packageDocumentation
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import type { Context } from 'hono';
|
|
8
|
-
import { HTTPException } from 'hono/http-exception';
|
|
9
|
-
import type { C15TContext } from '~/types';
|
|
10
|
-
import { extractErrorMessage } from '~/utils/extract-error-message';
|
|
11
|
-
import { enrichConsents } from '../utils/consent-enrichment';
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Handles retrieving a subject's consent status.
|
|
15
|
-
*
|
|
16
|
-
* Returns the subject's information and their consent records,
|
|
17
|
-
* optionally filtered by consent type(s).
|
|
18
|
-
*/
|
|
19
|
-
export const getSubjectHandler = async (c: Context) => {
|
|
20
|
-
const ctx = c.get('c15tContext') as C15TContext;
|
|
21
|
-
const logger = ctx.logger;
|
|
22
|
-
logger.info('Handling GET /subjects/:id request');
|
|
23
|
-
|
|
24
|
-
const { db, registry } = ctx;
|
|
25
|
-
|
|
26
|
-
// Get input from validated params and query
|
|
27
|
-
const subjectId = c.req.param('id');
|
|
28
|
-
const type = c.req.query('type');
|
|
29
|
-
const typeFilter = type?.split(',').map((t) => t.trim()) || [];
|
|
30
|
-
|
|
31
|
-
logger.debug('Request parameters', { subjectId, typeFilter });
|
|
32
|
-
|
|
33
|
-
try {
|
|
34
|
-
// Find the subject
|
|
35
|
-
const subject = await db.findFirst('subject', {
|
|
36
|
-
where: (b) => b('id', '=', subjectId),
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
if (!subject) {
|
|
40
|
-
throw new HTTPException(404, {
|
|
41
|
-
message: 'Subject not found',
|
|
42
|
-
cause: { code: 'SUBJECT_NOT_FOUND', subjectId },
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// Get all consents for this subject
|
|
47
|
-
const consents = await db.findMany('consent', {
|
|
48
|
-
where: (b) => b('subjectId', '=', subjectId),
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
const consentItems = await enrichConsents(consents, { db, registry });
|
|
52
|
-
|
|
53
|
-
// Filter by type if specified
|
|
54
|
-
const filteredConsents =
|
|
55
|
-
typeFilter.length > 0
|
|
56
|
-
? consentItems.filter((consent) => typeFilter.includes(consent.type))
|
|
57
|
-
: consentItems;
|
|
58
|
-
|
|
59
|
-
// Determine if consent is valid for requested types
|
|
60
|
-
const isValid =
|
|
61
|
-
typeFilter.length === 0 ||
|
|
62
|
-
typeFilter.every((t) =>
|
|
63
|
-
filteredConsents.some(
|
|
64
|
-
(consent) => consent.type === t && consent.isLatestPolicy
|
|
65
|
-
)
|
|
66
|
-
);
|
|
67
|
-
|
|
68
|
-
return c.json({
|
|
69
|
-
subject: {
|
|
70
|
-
id: subject.id,
|
|
71
|
-
externalId: subject.externalId ?? undefined,
|
|
72
|
-
isIdentified: subject.isIdentified,
|
|
73
|
-
createdAt: subject.createdAt,
|
|
74
|
-
},
|
|
75
|
-
consents: filteredConsents,
|
|
76
|
-
isValid,
|
|
77
|
-
});
|
|
78
|
-
} catch (error) {
|
|
79
|
-
logger.error('Error in GET /subjects/:id handler', {
|
|
80
|
-
error: extractErrorMessage(error),
|
|
81
|
-
errorType: error instanceof Error ? error.constructor.name : typeof error,
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
if (error instanceof HTTPException) {
|
|
85
|
-
throw error;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
throw new HTTPException(500, {
|
|
89
|
-
message: 'Internal server error',
|
|
90
|
-
cause: { code: 'INTERNAL_SERVER_ERROR' },
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
};
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* GET /subjects handler - List subjects by externalId (requires API key).
|
|
3
|
-
*
|
|
4
|
-
* @packageDocumentation
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import type { Context } from 'hono';
|
|
8
|
-
import { HTTPException } from 'hono/http-exception';
|
|
9
|
-
import type { C15TContext } from '~/types';
|
|
10
|
-
import { extractErrorMessage } from '~/utils/extract-error-message';
|
|
11
|
-
import { enrichConsents } from '../utils/consent-enrichment';
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Handles listing all subjects linked to an external ID.
|
|
15
|
-
*
|
|
16
|
-
* This endpoint requires API key authentication and is intended
|
|
17
|
-
* for server-side use only (Data Subject Access Requests).
|
|
18
|
-
*/
|
|
19
|
-
export const listSubjectsHandler = async (c: Context) => {
|
|
20
|
-
const ctx = c.get('c15tContext') as C15TContext;
|
|
21
|
-
const logger = ctx.logger;
|
|
22
|
-
logger.info('Handling GET /subjects request');
|
|
23
|
-
|
|
24
|
-
const { db, registry } = ctx;
|
|
25
|
-
|
|
26
|
-
// Check API key authentication
|
|
27
|
-
if (!ctx.apiKeyAuthenticated) {
|
|
28
|
-
throw new HTTPException(401, {
|
|
29
|
-
message: 'API key required. Use Authorization: Bearer <api_key>',
|
|
30
|
-
cause: { code: 'UNAUTHORIZED' },
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const externalId = c.req.query('externalId');
|
|
35
|
-
|
|
36
|
-
if (!externalId) {
|
|
37
|
-
throw new HTTPException(422, {
|
|
38
|
-
message: 'externalId query parameter is required',
|
|
39
|
-
cause: { code: 'EXTERNAL_ID_REQUIRED' },
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
logger.debug('Request parameters', { externalId });
|
|
44
|
-
|
|
45
|
-
try {
|
|
46
|
-
// Find all subjects with this externalId
|
|
47
|
-
const subjects = await db.findMany('subject', {
|
|
48
|
-
where: (b) => b('externalId', '=', externalId),
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
// Get consents for each subject
|
|
52
|
-
const subjectItems = await Promise.all(
|
|
53
|
-
subjects.map(async (subject) => {
|
|
54
|
-
const consents = await db.findMany('consent', {
|
|
55
|
-
where: (b) => b('subjectId', '=', subject.id),
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
const consentItems = await enrichConsents(consents, { db, registry });
|
|
59
|
-
|
|
60
|
-
return {
|
|
61
|
-
id: subject.id,
|
|
62
|
-
externalId: subject.externalId ?? externalId,
|
|
63
|
-
isIdentified: subject.isIdentified,
|
|
64
|
-
createdAt: subject.createdAt,
|
|
65
|
-
consents: consentItems,
|
|
66
|
-
};
|
|
67
|
-
})
|
|
68
|
-
);
|
|
69
|
-
|
|
70
|
-
logger.info('Found subjects for externalId', {
|
|
71
|
-
externalId,
|
|
72
|
-
count: subjectItems.length,
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
return c.json({
|
|
76
|
-
subjects: subjectItems,
|
|
77
|
-
});
|
|
78
|
-
} catch (error) {
|
|
79
|
-
logger.error('Error in GET /subjects handler', {
|
|
80
|
-
error: extractErrorMessage(error),
|
|
81
|
-
errorType: error instanceof Error ? error.constructor.name : typeof error,
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
if (error instanceof HTTPException) {
|
|
85
|
-
throw error;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
throw new HTTPException(500, {
|
|
89
|
-
message: 'Internal server error',
|
|
90
|
-
cause: { code: 'INTERNAL_SERVER_ERROR' },
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
};
|
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* PATCH /subjects/:id handler - Link external ID to subject.
|
|
3
|
-
*
|
|
4
|
-
* @packageDocumentation
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import type { Context } from 'hono';
|
|
8
|
-
import { HTTPException } from 'hono/http-exception';
|
|
9
|
-
import { generateUniqueId } from '~/db/registry/utils';
|
|
10
|
-
import type { C15TContext } from '~/types';
|
|
11
|
-
import { extractErrorMessage } from '~/utils/extract-error-message';
|
|
12
|
-
import { getMetrics } from '~/utils/metrics';
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Handles linking an external ID to a subject.
|
|
16
|
-
*
|
|
17
|
-
* Unlike the legacy identify endpoint, this does NOT merge subjects.
|
|
18
|
-
* Each device maintains its own independent consent history.
|
|
19
|
-
* The externalId allows querying all subjects via GET /subjects.
|
|
20
|
-
*/
|
|
21
|
-
export const patchSubjectHandler = async (c: Context) => {
|
|
22
|
-
const ctx = c.get('c15tContext') as C15TContext;
|
|
23
|
-
const logger = ctx.logger;
|
|
24
|
-
logger.info('Handling PATCH /subjects/:id request');
|
|
25
|
-
|
|
26
|
-
const { db } = ctx;
|
|
27
|
-
|
|
28
|
-
// Get input from validated params and body
|
|
29
|
-
const subjectId = c.req.param('id');
|
|
30
|
-
const body = await c.req.json<{
|
|
31
|
-
externalId: string;
|
|
32
|
-
identityProvider?: string;
|
|
33
|
-
}>();
|
|
34
|
-
const { externalId, identityProvider = 'external' } = body;
|
|
35
|
-
|
|
36
|
-
logger.debug('Request parameters', {
|
|
37
|
-
subjectId,
|
|
38
|
-
externalId,
|
|
39
|
-
identityProvider,
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
try {
|
|
43
|
-
// Find the subject
|
|
44
|
-
const subject = await db.findFirst('subject', {
|
|
45
|
-
where: (b) => b('id', '=', subjectId),
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
if (!subject) {
|
|
49
|
-
throw new HTTPException(404, {
|
|
50
|
-
message: 'Subject not found',
|
|
51
|
-
cause: { code: 'SUBJECT_NOT_FOUND', subjectId },
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// Update the subject with externalId (no merge logic)
|
|
56
|
-
await db.transaction(async (tx) => {
|
|
57
|
-
await tx.updateMany('subject', {
|
|
58
|
-
where: (b) => b('id', '=', subjectId),
|
|
59
|
-
set: {
|
|
60
|
-
externalId,
|
|
61
|
-
identityProvider,
|
|
62
|
-
isIdentified: true,
|
|
63
|
-
updatedAt: new Date(),
|
|
64
|
-
},
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
// Create audit log
|
|
68
|
-
await tx.create('auditLog', {
|
|
69
|
-
id: await generateUniqueId(tx, 'auditLog', ctx),
|
|
70
|
-
subjectId,
|
|
71
|
-
entityType: 'subject',
|
|
72
|
-
entityId: subjectId,
|
|
73
|
-
actionType: 'identify_user',
|
|
74
|
-
ipAddress: ctx.ipAddress || null,
|
|
75
|
-
userAgent: ctx.userAgent || null,
|
|
76
|
-
changes: {
|
|
77
|
-
externalId: { from: subject.externalId, to: externalId },
|
|
78
|
-
identityProvider: {
|
|
79
|
-
from: subject.identityProvider,
|
|
80
|
-
to: identityProvider,
|
|
81
|
-
},
|
|
82
|
-
isIdentified: { from: subject.isIdentified, to: true },
|
|
83
|
-
},
|
|
84
|
-
metadata: {
|
|
85
|
-
externalId,
|
|
86
|
-
identityProvider,
|
|
87
|
-
},
|
|
88
|
-
});
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
logger.info('Subject linked to external ID', {
|
|
92
|
-
subjectId,
|
|
93
|
-
externalId,
|
|
94
|
-
identityProvider,
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
getMetrics()?.recordSubjectLinked(identityProvider);
|
|
98
|
-
|
|
99
|
-
return c.json({
|
|
100
|
-
success: true,
|
|
101
|
-
subject: {
|
|
102
|
-
id: subjectId,
|
|
103
|
-
externalId,
|
|
104
|
-
isIdentified: true,
|
|
105
|
-
},
|
|
106
|
-
});
|
|
107
|
-
} catch (error) {
|
|
108
|
-
logger.error('Error in PATCH /subjects/:id handler', {
|
|
109
|
-
error: extractErrorMessage(error),
|
|
110
|
-
errorType: error instanceof Error ? error.constructor.name : typeof error,
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
if (error instanceof HTTPException) {
|
|
114
|
-
throw error;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
throw new HTTPException(500, {
|
|
118
|
-
message: 'Internal server error',
|
|
119
|
-
cause: { code: 'INTERNAL_SERVER_ERROR' },
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
|
-
};
|