@voyantjs/transactions 0.2.0 → 0.3.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.
Files changed (43) hide show
  1. package/dist/routes-offers.d.ts +885 -0
  2. package/dist/routes-offers.d.ts.map +1 -0
  3. package/dist/routes-offers.js +161 -0
  4. package/dist/routes-orders.d.ts +1056 -0
  5. package/dist/routes-orders.d.ts.map +1 -0
  6. package/dist/routes-orders.js +180 -0
  7. package/dist/routes-shared.d.ts +103 -0
  8. package/dist/routes-shared.d.ts.map +1 -0
  9. package/dist/routes-shared.js +97 -0
  10. package/dist/routes.d.ts +3 -1979
  11. package/dist/routes.d.ts.map +1 -1
  12. package/dist/routes.js +5 -604
  13. package/dist/schema-audit.d.ts +216 -0
  14. package/dist/schema-audit.d.ts.map +1 -0
  15. package/dist/schema-audit.js +22 -0
  16. package/dist/schema-offers.d.ts +1301 -0
  17. package/dist/schema-offers.d.ts.map +1 -0
  18. package/dist/schema-offers.js +120 -0
  19. package/dist/schema-orders.d.ts +1582 -0
  20. package/dist/schema-orders.d.ts.map +1 -0
  21. package/dist/schema-orders.js +149 -0
  22. package/dist/schema-relations.d.ts +42 -0
  23. package/dist/schema-relations.d.ts.map +1 -0
  24. package/dist/schema-relations.js +56 -0
  25. package/dist/schema-shared.d.ts +12 -0
  26. package/dist/schema-shared.d.ts.map +1 -0
  27. package/dist/schema-shared.js +84 -0
  28. package/dist/schema.d.ts +5 -3148
  29. package/dist/schema.d.ts.map +1 -1
  30. package/dist/schema.js +5 -420
  31. package/dist/service-offers.d.ts +447 -0
  32. package/dist/service-offers.d.ts.map +1 -0
  33. package/dist/service-offers.js +262 -0
  34. package/dist/service-orders.d.ts +443 -0
  35. package/dist/service-orders.d.ts.map +1 -0
  36. package/dist/service-orders.js +278 -0
  37. package/dist/service-shared.d.ts +86 -0
  38. package/dist/service-shared.d.ts.map +1 -0
  39. package/dist/service-shared.js +49 -0
  40. package/dist/service.d.ts +48 -927
  41. package/dist/service.d.ts.map +1 -1
  42. package/dist/service.js +48 -595
  43. package/package.json +5 -5
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes-orders.d.ts","sourceRoot":"","sources":["../src/routes-orders.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,GAAG,EAIT,MAAM,oBAAoB,CAAA;AAoB3B,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2BAkQ/B,CAAA"}
@@ -0,0 +1,180 @@
1
+ import { Hono } from "hono";
2
+ import { authorizeTransactionPiiAccess, createPiiService, hasParticipantIdentityInput, logTransactionPiiAccess, notFound, } from "./routes-shared.js";
3
+ import { transactionsService } from "./service.js";
4
+ import { insertOrderItemParticipantSchema, insertOrderItemSchema, insertOrderParticipantSchema, insertOrderSchema, insertOrderTermSchema, orderItemListQuerySchema, orderItemParticipantListQuerySchema, orderListQuerySchema, orderParticipantListQuerySchema, orderTermListQuerySchema, updateOrderItemParticipantSchema, updateOrderItemSchema, updateOrderParticipantSchema, updateOrderSchema, updateOrderTermSchema, } from "./validation.js";
5
+ export const transactionOrderRoutes = new Hono()
6
+ .get("/orders", async (c) => {
7
+ const query = orderListQuerySchema.parse(Object.fromEntries(new URL(c.req.url).searchParams));
8
+ return c.json(await transactionsService.listOrders(c.get("db"), query));
9
+ })
10
+ .post("/orders", async (c) => c.json({
11
+ data: await transactionsService.createOrder(c.get("db"), insertOrderSchema.parse(await c.req.json())),
12
+ }, 201))
13
+ .get("/orders/:id", async (c) => {
14
+ const row = await transactionsService.getOrderById(c.get("db"), c.req.param("id"));
15
+ return row ? c.json({ data: row }) : notFound(c, "Order not found");
16
+ })
17
+ .patch("/orders/:id", async (c) => {
18
+ const row = await transactionsService.updateOrder(c.get("db"), c.req.param("id"), updateOrderSchema.parse(await c.req.json()));
19
+ return row ? c.json({ data: row }) : notFound(c, "Order not found");
20
+ })
21
+ .delete("/orders/:id", async (c) => {
22
+ const row = await transactionsService.deleteOrder(c.get("db"), c.req.param("id"));
23
+ return row ? c.json({ success: true }) : notFound(c, "Order not found");
24
+ })
25
+ .get("/order-participants", async (c) => {
26
+ const query = orderParticipantListQuerySchema.parse(Object.fromEntries(new URL(c.req.url).searchParams));
27
+ return c.json(await transactionsService.listOrderParticipants(c.get("db"), query));
28
+ })
29
+ .post("/order-participants", async (c) => {
30
+ const payload = insertOrderParticipantSchema.parse(await c.req.json());
31
+ const row = await transactionsService.createOrderParticipant(c.get("db"), payload);
32
+ if (!row)
33
+ return c.json({ data: row }, 201);
34
+ if (hasParticipantIdentityInput(payload)) {
35
+ const pii = createPiiService(c, "order", row.orderId);
36
+ await pii.upsertParticipantIdentity(c.get("db"), "order", row.id, payload, c.get("userId"));
37
+ return c.json({ data: await transactionsService.getOrderParticipantById(c.get("db"), row.id) }, 201);
38
+ }
39
+ return c.json({ data: row }, 201);
40
+ })
41
+ .get("/order-participants/:id", async (c) => {
42
+ const row = await transactionsService.getOrderParticipantById(c.get("db"), c.req.param("id"));
43
+ return row ? c.json({ data: row }) : notFound(c, "Order participant not found");
44
+ })
45
+ .patch("/order-participants/:id", async (c) => {
46
+ const payload = updateOrderParticipantSchema.parse(await c.req.json());
47
+ const row = await transactionsService.updateOrderParticipant(c.get("db"), c.req.param("id"), payload);
48
+ if (!row)
49
+ return notFound(c, "Order participant not found");
50
+ if (hasParticipantIdentityInput(payload)) {
51
+ const pii = createPiiService(c, "order", row.orderId);
52
+ await pii.upsertParticipantIdentity(c.get("db"), "order", row.id, payload, c.get("userId"));
53
+ return c.json({
54
+ data: await transactionsService.getOrderParticipantById(c.get("db"), row.id),
55
+ });
56
+ }
57
+ return c.json({ data: row });
58
+ })
59
+ .get("/order-participants/:id/travel-details", async (c) => {
60
+ const participant = await transactionsService.getOrderParticipantById(c.get("db"), c.req.param("id"));
61
+ if (!participant)
62
+ return notFound(c, "Order participant not found");
63
+ const auth = await authorizeTransactionPiiAccess(c, {
64
+ participantKind: "order",
65
+ participantId: participant.id,
66
+ parentId: participant.orderId,
67
+ action: "read",
68
+ });
69
+ if (!auth.allowed)
70
+ return auth.response;
71
+ const pii = createPiiService(c, "order", participant.orderId);
72
+ const row = await pii.getParticipantIdentity(c.get("db"), "order", participant.id, c.get("userId"));
73
+ if (!row) {
74
+ await logTransactionPiiAccess(c, {
75
+ participantKind: "order",
76
+ parentId: participant.orderId,
77
+ participantId: participant.id,
78
+ action: "read",
79
+ outcome: "denied",
80
+ reason: "travel_details_not_found",
81
+ });
82
+ return c.json({ error: "Order participant travel details not found" }, 404);
83
+ }
84
+ return c.json({ data: row });
85
+ })
86
+ .patch("/order-participants/:id/travel-details", async (c) => {
87
+ const participant = await transactionsService.getOrderParticipantById(c.get("db"), c.req.param("id"));
88
+ if (!participant)
89
+ return notFound(c, "Order participant not found");
90
+ const auth = await authorizeTransactionPiiAccess(c, {
91
+ participantKind: "order",
92
+ participantId: participant.id,
93
+ parentId: participant.orderId,
94
+ action: "update",
95
+ });
96
+ if (!auth.allowed)
97
+ return auth.response;
98
+ const pii = createPiiService(c, "order", participant.orderId);
99
+ const row = await pii.upsertParticipantIdentity(c.get("db"), "order", participant.id, updateOrderParticipantSchema.parse(await c.req.json()), c.get("userId"));
100
+ return row ? c.json({ data: row }) : notFound(c, "Order participant not found");
101
+ })
102
+ .delete("/order-participants/:id/travel-details", async (c) => {
103
+ const participant = await transactionsService.getOrderParticipantById(c.get("db"), c.req.param("id"));
104
+ if (!participant)
105
+ return notFound(c, "Order participant not found");
106
+ const auth = await authorizeTransactionPiiAccess(c, {
107
+ participantKind: "order",
108
+ participantId: participant.id,
109
+ parentId: participant.orderId,
110
+ action: "delete",
111
+ });
112
+ if (!auth.allowed)
113
+ return auth.response;
114
+ const pii = createPiiService(c, "order", participant.orderId);
115
+ const row = await pii.deleteParticipantIdentity(c.get("db"), "order", participant.id, c.get("userId"));
116
+ return row
117
+ ? c.json({ success: true })
118
+ : c.json({ error: "Order participant travel details not found" }, 404);
119
+ })
120
+ .delete("/order-participants/:id", async (c) => {
121
+ const row = await transactionsService.deleteOrderParticipant(c.get("db"), c.req.param("id"));
122
+ return row ? c.json({ success: true }) : notFound(c, "Order participant not found");
123
+ })
124
+ .get("/order-items", async (c) => {
125
+ const query = orderItemListQuerySchema.parse(Object.fromEntries(new URL(c.req.url).searchParams));
126
+ return c.json(await transactionsService.listOrderItems(c.get("db"), query));
127
+ })
128
+ .post("/order-items", async (c) => c.json({
129
+ data: await transactionsService.createOrderItem(c.get("db"), insertOrderItemSchema.parse(await c.req.json())),
130
+ }, 201))
131
+ .get("/order-items/:id", async (c) => {
132
+ const row = await transactionsService.getOrderItemById(c.get("db"), c.req.param("id"));
133
+ return row ? c.json({ data: row }) : notFound(c, "Order item not found");
134
+ })
135
+ .patch("/order-items/:id", async (c) => {
136
+ const row = await transactionsService.updateOrderItem(c.get("db"), c.req.param("id"), updateOrderItemSchema.parse(await c.req.json()));
137
+ return row ? c.json({ data: row }) : notFound(c, "Order item not found");
138
+ })
139
+ .delete("/order-items/:id", async (c) => {
140
+ const row = await transactionsService.deleteOrderItem(c.get("db"), c.req.param("id"));
141
+ return row ? c.json({ success: true }) : notFound(c, "Order item not found");
142
+ })
143
+ .get("/order-item-participants", async (c) => {
144
+ const query = orderItemParticipantListQuerySchema.parse(Object.fromEntries(new URL(c.req.url).searchParams));
145
+ return c.json(await transactionsService.listOrderItemParticipants(c.get("db"), query));
146
+ })
147
+ .post("/order-item-participants", async (c) => c.json({
148
+ data: await transactionsService.createOrderItemParticipant(c.get("db"), insertOrderItemParticipantSchema.parse(await c.req.json())),
149
+ }, 201))
150
+ .get("/order-item-participants/:id", async (c) => {
151
+ const row = await transactionsService.getOrderItemParticipantById(c.get("db"), c.req.param("id"));
152
+ return row ? c.json({ data: row }) : notFound(c, "Order item participant not found");
153
+ })
154
+ .patch("/order-item-participants/:id", async (c) => {
155
+ const row = await transactionsService.updateOrderItemParticipant(c.get("db"), c.req.param("id"), updateOrderItemParticipantSchema.parse(await c.req.json()));
156
+ return row ? c.json({ data: row }) : notFound(c, "Order item participant not found");
157
+ })
158
+ .delete("/order-item-participants/:id", async (c) => {
159
+ const row = await transactionsService.deleteOrderItemParticipant(c.get("db"), c.req.param("id"));
160
+ return row ? c.json({ success: true }) : notFound(c, "Order item participant not found");
161
+ })
162
+ .get("/order-terms", async (c) => {
163
+ const query = orderTermListQuerySchema.parse(Object.fromEntries(new URL(c.req.url).searchParams));
164
+ return c.json(await transactionsService.listOrderTerms(c.get("db"), query));
165
+ })
166
+ .post("/order-terms", async (c) => c.json({
167
+ data: await transactionsService.createOrderTerm(c.get("db"), insertOrderTermSchema.parse(await c.req.json())),
168
+ }, 201))
169
+ .get("/order-terms/:id", async (c) => {
170
+ const row = await transactionsService.getOrderTermById(c.get("db"), c.req.param("id"));
171
+ return row ? c.json({ data: row }) : notFound(c, "Order term not found");
172
+ })
173
+ .patch("/order-terms/:id", async (c) => {
174
+ const row = await transactionsService.updateOrderTerm(c.get("db"), c.req.param("id"), updateOrderTermSchema.parse(await c.req.json()));
175
+ return row ? c.json({ data: row }) : notFound(c, "Order term not found");
176
+ })
177
+ .delete("/order-terms/:id", async (c) => {
178
+ const row = await transactionsService.deleteOrderTerm(c.get("db"), c.req.param("id"));
179
+ return row ? c.json({ success: true }) : notFound(c, "Order term not found");
180
+ });
@@ -0,0 +1,103 @@
1
+ import type { PostgresJsDatabase } from "drizzle-orm/postgres-js";
2
+ import type { Context } from "hono";
3
+ export type Env = {
4
+ Bindings: Partial<{
5
+ KMS_PROVIDER: string;
6
+ KMS_ENV_KEY: string;
7
+ KMS_LOCAL_KEY: string;
8
+ GCP_PROJECT_ID: string;
9
+ GCP_SERVICE_ACCOUNT_EMAIL: string;
10
+ GCP_PRIVATE_KEY: string;
11
+ GCP_KMS_KEYRING: string;
12
+ GCP_KMS_LOCATION: string;
13
+ GCP_KMS_PEOPLE_KEY_NAME: string;
14
+ GCP_KMS_INTEGRATIONS_KEY_NAME: string;
15
+ AWS_REGION: string;
16
+ AWS_ACCESS_KEY_ID: string;
17
+ AWS_SECRET_ACCESS_KEY: string;
18
+ AWS_SESSION_TOKEN: string;
19
+ AWS_KMS_ENDPOINT: string;
20
+ AWS_KMS_PEOPLE_KEY_ID: string;
21
+ AWS_KMS_INTEGRATIONS_KEY_ID: string;
22
+ }>;
23
+ Variables: {
24
+ db: PostgresJsDatabase;
25
+ userId?: string;
26
+ actor?: "staff" | "customer" | "partner" | "supplier";
27
+ callerType?: "session" | "api_key" | "internal";
28
+ scopes?: string[] | null;
29
+ isInternalRequest?: boolean;
30
+ authorizeTransactionPii?: (args: {
31
+ db: PostgresJsDatabase;
32
+ userId?: string;
33
+ actor?: "staff" | "customer" | "partner" | "supplier";
34
+ callerType?: "session" | "api_key" | "internal";
35
+ scopes?: string[] | null;
36
+ isInternalRequest?: boolean;
37
+ participantKind: "offer" | "order";
38
+ participantId: string;
39
+ parentId: string;
40
+ action: "read" | "update" | "delete";
41
+ }) => boolean | Promise<boolean>;
42
+ };
43
+ };
44
+ export declare function getRuntimeEnv(c: Context<Env>): {
45
+ KMS_PROVIDER?: string | undefined;
46
+ KMS_ENV_KEY?: string | undefined;
47
+ KMS_LOCAL_KEY?: string | undefined;
48
+ GCP_PROJECT_ID?: string | undefined;
49
+ GCP_SERVICE_ACCOUNT_EMAIL?: string | undefined;
50
+ GCP_PRIVATE_KEY?: string | undefined;
51
+ GCP_KMS_KEYRING?: string | undefined;
52
+ GCP_KMS_LOCATION?: string | undefined;
53
+ GCP_KMS_PEOPLE_KEY_NAME?: string | undefined;
54
+ GCP_KMS_INTEGRATIONS_KEY_NAME?: string | undefined;
55
+ AWS_REGION?: string | undefined;
56
+ AWS_ACCESS_KEY_ID?: string | undefined;
57
+ AWS_SECRET_ACCESS_KEY?: string | undefined;
58
+ AWS_SESSION_TOKEN?: string | undefined;
59
+ AWS_KMS_ENDPOINT?: string | undefined;
60
+ AWS_KMS_PEOPLE_KEY_ID?: string | undefined;
61
+ AWS_KMS_INTEGRATIONS_KEY_ID?: string | undefined;
62
+ };
63
+ export declare function hasParticipantIdentityInput(body: Record<string, unknown>): boolean;
64
+ export declare function logTransactionPiiAccess(c: Context<Env>, input: {
65
+ participantKind: "offer" | "order";
66
+ parentId?: string;
67
+ participantId?: string;
68
+ action: "read" | "update" | "delete";
69
+ outcome: "allowed" | "denied";
70
+ reason?: string;
71
+ metadata?: Record<string, unknown>;
72
+ }): Promise<void>;
73
+ export declare function authorizeTransactionPiiAccess(c: Context<Env>, input: {
74
+ participantKind: "offer" | "order";
75
+ participantId: string;
76
+ parentId: string;
77
+ action: "read" | "update" | "delete";
78
+ }): Promise<{
79
+ allowed: true;
80
+ response?: undefined;
81
+ } | {
82
+ allowed: false;
83
+ response: Response & import("hono").TypedResponse<{
84
+ error: string;
85
+ }, 401, "json">;
86
+ } | {
87
+ allowed: false;
88
+ response: Response & import("hono").TypedResponse<{
89
+ error: string;
90
+ }, 403, "json">;
91
+ }>;
92
+ export declare function createPiiService(c: Context<Env>, _participantKind: "offer" | "order", parentId: string): {
93
+ getParticipantIdentity(db: PostgresJsDatabase, participantKind: "offer" | "order", participantId: string, actorId?: string | null): Promise<import("./index.js").DecryptedTransactionParticipantIdentity | null>;
94
+ upsertParticipantIdentity(db: PostgresJsDatabase, participantKind: "offer" | "order", participantId: string, input: import("./pii.js").UpsertTransactionParticipantIdentityInput, actorId?: string | null): Promise<import("./index.js").DecryptedTransactionParticipantIdentity | null>;
95
+ deleteParticipantIdentity(db: PostgresJsDatabase, participantKind: "offer" | "order", participantId: string, actorId?: string | null): Promise<{
96
+ participantId: string;
97
+ participantKind: "offer" | "order";
98
+ } | null>;
99
+ };
100
+ export declare function notFound(c: Context<Env>, message: string): Response & import("hono").TypedResponse<{
101
+ error: string;
102
+ }, 404, "json">;
103
+ //# sourceMappingURL=routes-shared.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes-shared.d.ts","sourceRoot":"","sources":["../src/routes-shared.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,97 @@
1
+ import { createKmsProviderFromEnv } from "@voyantjs/utils";
2
+ import { createTransactionPiiService } from "./pii.js";
3
+ import { transactionPiiAccessLog } from "./schema.js";
4
+ export function getRuntimeEnv(c) {
5
+ const processEnv = globalThis.process?.env ?? {};
6
+ return {
7
+ ...processEnv,
8
+ ...(c.env ?? {}),
9
+ };
10
+ }
11
+ function hasPiiScope(scopes, action) {
12
+ if (!scopes || scopes.length === 0) {
13
+ return false;
14
+ }
15
+ return (scopes.includes("*") ||
16
+ scopes.includes("transactions-pii:*") ||
17
+ scopes.includes(`transactions-pii:${action}`));
18
+ }
19
+ export function hasParticipantIdentityInput(body) {
20
+ return "dateOfBirth" in body || "nationality" in body;
21
+ }
22
+ export async function logTransactionPiiAccess(c, input) {
23
+ await c
24
+ .get("db")
25
+ .insert(transactionPiiAccessLog)
26
+ .values({
27
+ participantKind: input.participantKind,
28
+ parentId: input.parentId ?? null,
29
+ participantId: input.participantId ?? null,
30
+ actorId: c.get("userId") ?? null,
31
+ actorType: c.get("actor") ?? null,
32
+ callerType: c.get("callerType") ?? null,
33
+ action: input.action,
34
+ outcome: input.outcome,
35
+ reason: input.reason ?? null,
36
+ metadata: input.metadata ?? null,
37
+ });
38
+ }
39
+ export async function authorizeTransactionPiiAccess(c, input) {
40
+ if (c.get("isInternalRequest")) {
41
+ return { allowed: true };
42
+ }
43
+ const userId = c.get("userId");
44
+ if (!userId) {
45
+ await logTransactionPiiAccess(c, { ...input, outcome: "denied", reason: "missing_user" });
46
+ return { allowed: false, response: c.json({ error: "Unauthorized" }, 401) };
47
+ }
48
+ const customAuthorizer = c.get("authorizeTransactionPii");
49
+ if (customAuthorizer) {
50
+ const allowed = await customAuthorizer({
51
+ db: c.get("db"),
52
+ userId,
53
+ actor: c.get("actor"),
54
+ callerType: c.get("callerType"),
55
+ scopes: c.get("scopes"),
56
+ isInternalRequest: c.get("isInternalRequest"),
57
+ ...input,
58
+ });
59
+ if (!allowed) {
60
+ await logTransactionPiiAccess(c, {
61
+ ...input,
62
+ outcome: "denied",
63
+ reason: "custom_policy_denied",
64
+ });
65
+ return { allowed: false, response: c.json({ error: "Forbidden" }, 403) };
66
+ }
67
+ return { allowed: true };
68
+ }
69
+ const allowed = hasPiiScope(c.get("scopes"), input.action) || c.get("actor") === "staff";
70
+ if (!allowed) {
71
+ await logTransactionPiiAccess(c, {
72
+ ...input,
73
+ outcome: "denied",
74
+ reason: "insufficient_scope",
75
+ metadata: { actor: c.get("actor") ?? null },
76
+ });
77
+ return { allowed: false, response: c.json({ error: "Forbidden" }, 403) };
78
+ }
79
+ return { allowed: true };
80
+ }
81
+ export function createPiiService(c, _participantKind, parentId) {
82
+ return createTransactionPiiService({
83
+ kms: createKmsProviderFromEnv(getRuntimeEnv(c)),
84
+ onAudit: async (event) => {
85
+ await logTransactionPiiAccess(c, {
86
+ participantKind: event.participantKind,
87
+ parentId,
88
+ participantId: event.participantId,
89
+ action: event.action,
90
+ outcome: "allowed",
91
+ });
92
+ },
93
+ });
94
+ }
95
+ export function notFound(c, message) {
96
+ return c.json({ error: message }, 404);
97
+ }