authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-06-19 21:37:43-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-06-19 21:37:43-07:00
logb17b6d50c55e3eff4317163d31c4032423d3d2c1
treef36aeb549736bfda5e87fb5e29ceeba360f0a702
parentccf8331600f323939a796169dfe2598a4c1b59c4
signature Signed by PGP key B5690EEEBB952194

[codex] Complete relay agent activity Effect cleanup (#3210)

Co-authored-by: codex <codex@users.noreply.github.com>

12 files changed, 185 insertions(+), 139 deletions(-)

infra/relay/src/agentActivity/AgentActivityPublisher.ts+1-1
......@@ -39,7 +39,7 @@ export class AgentActivityPublisher extends Context.Service<
3939 }
4040>()("t3code-relay/agentActivity/AgentActivityPublisher") {}
4141
42const make = Effect.gen(function* () {
42export const make = Effect.gen(function* () {
4343 const rows = yield* AgentActivityRows.AgentActivityRows;
4444 const links = yield* EnvironmentLinks.EnvironmentLinks;
4545 const liveActivities = yield* LiveActivities.LiveActivities;
infra/relay/src/agentActivity/AgentActivityRows.ts+1-1
......@@ -71,7 +71,7 @@ const decodeRelayAgentActivityStateJson = Schema.decodeUnknownOption(
7171 Schema.fromJsonString(RelayAgentActivityStateSchema),
7272);
7373
74const make = Effect.gen(function* () {
74export const make = Effect.gen(function* () {
7575 const db = yield* RelayDb.RelayDb;
7676
7777 return AgentActivityRows.of({
infra/relay/src/agentActivity/ApnsClient.ts+26-14
......@@ -40,15 +40,21 @@ export interface ApnsDeliveryResult {
4040 readonly apnsId: string | null;
4141}
4242
43export class ApnsSigningError extends Schema.TaggedErrorClass<ApnsSigningError>()(
44 "ApnsSigningError",
45 {
46 phase: Schema.Literals(["encoding", "signing"]),
47 cause: Schema.Defect(),
48 },
43export class ApnsJwtEncodingError extends Schema.TaggedErrorClass<ApnsJwtEncodingError>()(
44 "ApnsJwtEncodingError",
45 { cause: Schema.Defect() },
46) {
47 override get message(): string {
48 return "Failed to encode APNs JWT.";
49 }
50}
51
52export class ApnsJwtSigningError extends Schema.TaggedErrorClass<ApnsJwtSigningError>()(
53 "ApnsJwtSigningError",
54 { cause: Schema.Defect() },
4955) {
5056 override get message(): string {
51 return `Failed during APNs JWT ${this.phase}`;
57 return "Failed to sign APNs JWT.";
5258 }
5359}
5460
......@@ -59,7 +65,7 @@ export class ApnsHttpRequestError extends Schema.TaggedErrorClass<ApnsHttpReques
5965 },
6066) {
6167 override get message(): string {
62 return "APNs HTTP request failed";
68 return "APNs HTTP request failed.";
6369 }
6470}
6571
......@@ -70,11 +76,17 @@ export class ApnsInvalidResponseError extends Schema.TaggedErrorClass<ApnsInvali
7076 },
7177) {
7278 override get message(): string {
73 return "APNs returned an invalid response";
79 return "APNs returned an invalid response.";
7480 }
7581}
7682
77export type ApnsError = ApnsSigningError | ApnsHttpRequestError | ApnsInvalidResponseError;
83export const ApnsError = Schema.Union([
84 ApnsJwtEncodingError,
85 ApnsJwtSigningError,
86 ApnsHttpRequestError,
87 ApnsInvalidResponseError,
88]);
89export type ApnsError = typeof ApnsError.Type;
7890
7991const decodeApnsErrorResponseJson = Schema.decodeUnknownOption(
8092 Schema.fromJsonString(
......@@ -107,12 +119,12 @@ const makeApnsJwt = Effect.fn("relay.apns.make_jwt")(function* (input: {
107119 readonly issuedAtUnixSeconds: number;
108120}) {
109121 const headerJson = yield* encodeApnsJwtHeaderJson({ alg: "ES256", kid: input.keyId }).pipe(
110 Effect.mapError((cause) => new ApnsSigningError({ cause, phase: "encoding" })),
122 Effect.mapError((cause) => new ApnsJwtEncodingError({ cause })),
111123 );
112124 const payloadJson = yield* encodeApnsJwtPayloadJson({
113125 iss: input.teamId,
114126 iat: input.issuedAtUnixSeconds,
115 }).pipe(Effect.mapError((cause) => new ApnsSigningError({ cause, phase: "encoding" })));
127 }).pipe(Effect.mapError((cause) => new ApnsJwtEncodingError({ cause })));
116128
117129 const privateKey = Redacted.value(input.privateKey);
118130 const header = Encoding.encodeBase64Url(headerJson);
......@@ -129,7 +141,7 @@ const makeApnsJwt = Effect.fn("relay.apns.make_jwt")(function* (input: {
129141 });
130142 return `${signingInput}.${Encoding.encodeBase64Url(signature)}`;
131143 },
132 catch: (cause) => new ApnsSigningError({ cause, phase: "signing" }),
144 catch: (cause) => new ApnsJwtSigningError({ cause }),
133145 });
134146});
135147
......@@ -251,7 +263,7 @@ export class ApnsClient extends Context.Service<
251263 }
252264>()("t3code-relay/agentActivity/ApnsClient") {}
253265
254const make = Effect.gen(function* () {
266export const make = Effect.gen(function* () {
255267 const httpClient = yield* HttpClient.HttpClient;
256268
257269 const sendLiveActivityRequest: ApnsClient["Service"]["sendLiveActivityRequest"] = Effect.fn(
infra/relay/src/agentActivity/ApnsDeliveries.ts+11-41
......@@ -21,9 +21,12 @@ import {
2121} from "./agentActivityPayloads.ts";
2222import * as Apns from "./ApnsClient.ts";
2323import {
24 ApnsDeliveryJobInvalid,
24 ApnsDeliveryJobLiveActivityAggregateMissing,
25 ApnsDeliveryJobPushNotificationMissing,
26 ApnsDeliveryJobQueuePayloadInvalid,
2527 type ApnsNotificationPayload,
2628 SignedApnsDeliveryJob,
29 isApnsDeliveryJobVerificationError,
2730 verifySignedApnsDeliveryJob,
2831 type ApnsDeliveryJobVerificationError,
2932} from "./apnsDeliveryJobs.ts";
......@@ -92,17 +95,6 @@ const decodeRelayAgentAwarenessPreferencesJson = Schema.decodeUnknownOption(
9295);
9396const decodeSignedApnsDeliveryJob = Schema.decodeUnknownEffect(SignedApnsDeliveryJob);
9497
95function apnsErrorMessage(error: Apns.ApnsError): string {
96 switch (error._tag) {
97 case "ApnsSigningError":
98 return "Failed to sign APNs request.";
99 case "ApnsHttpRequestError":
100 return "Failed to send APNs request.";
101 case "ApnsInvalidResponseError":
102 return "APNs returned an invalid response.";
103 }
104}
105
10698function parseAggregate(value: string | null): RelayAgentActivityAggregateState | null {
10799 if (!value) {
108100 return null;
......@@ -273,15 +265,6 @@ function isPermanentApnsTokenFailure(result: Apns.ApnsDeliveryResult): boolean {
273265 );
274266}
275267
276function isDeliveryJobVerificationError(value: unknown): value is ApnsDeliveryJobVerificationError {
277 return (
278 typeof value === "object" &&
279 value !== null &&
280 "_tag" in value &&
281 (value._tag === "ApnsDeliveryJobInvalid" || value._tag === "ApnsDeliveryJobExpired")
282 );
283}
284
285268function duplicateJobResult(input: {
286269 readonly deviceId: string;
287270 readonly kind: RelayDeliveryKind;
......@@ -418,7 +401,7 @@ export class ApnsDeliveries extends Context.Service<
418401 }
419402>()("t3code-relay/agentActivity/ApnsDeliveries") {}
420403
421const make = Effect.gen(function* () {
404export const make = Effect.gen(function* () {
422405 const attempts = yield* DeliveryAttempts.DeliveryAttempts;
423406 const liveActivities = yield* LiveActivities.LiveActivities;
424407 const deliveryQueue = yield* ApnsDeliveryQueue.ApnsDeliveryQueue;
......@@ -497,7 +480,7 @@ const make = Effect.gen(function* () {
497480 Effect.succeed({
498481 ok: false,
499482 status: 0,
500 reason: apnsErrorMessage(error),
483 reason: error.message,
501484 apnsId: null,
502485 }),
503486 ),
......@@ -614,7 +597,7 @@ const make = Effect.gen(function* () {
614597 Effect.succeed({
615598 ok: false,
616599 status: 0,
617 reason: apnsErrorMessage(error),
600 reason: error.message,
618601 apnsId: null,
619602 }),
620603 ),
......@@ -657,12 +640,7 @@ const make = Effect.gen(function* () {
657640 "relay.apns_deliveries.process_signed_job",
658641 )(function* (body) {
659642 const signedJob = yield* decodeSignedApnsDeliveryJob(body).pipe(
660 Effect.mapError(
661 () =>
662 new ApnsDeliveryJobInvalid({
663 reason: "invalid-queue-payload",
664 }),
665 ),
643 Effect.mapError(() => new ApnsDeliveryJobQueuePayloadInvalid()),
666644 );
667645 const now = yield* DateTime.now;
668646 const payload = verifySignedApnsDeliveryJob({
......@@ -670,7 +648,7 @@ const make = Effect.gen(function* () {
670648 job: signedJob,
671649 nowMs: now.epochMilliseconds,
672650 });
673 if (isDeliveryJobVerificationError(payload)) {
651 if (isApnsDeliveryJobVerificationError(payload)) {
674652 return yield* payload;
675653 }
676654 yield* Effect.annotateCurrentSpan({
......@@ -683,11 +661,7 @@ const make = Effect.gen(function* () {
683661 case "live_activity_start":
684662 case "live_activity_update":
685663 if (payload.aggregate === null) {
686 return Effect.fail(
687 new ApnsDeliveryJobInvalid({
688 reason: "missing-live-activity-aggregate",
689 }),
690 );
664 return Effect.fail(new ApnsDeliveryJobLiveActivityAggregateMissing());
691665 }
692666 return sendLiveActivity({
693667 target: {
......@@ -712,11 +686,7 @@ const make = Effect.gen(function* () {
712686 });
713687 case "push_notification":
714688 if (payload.notification === null) {
715 return Effect.fail(
716 new ApnsDeliveryJobInvalid({
717 reason: "missing-push-notification",
718 }),
719 );
689 return Effect.fail(new ApnsDeliveryJobPushNotificationMissing());
720690 }
721691 return sendPushNotification({
722692 target: {
infra/relay/src/agentActivity/ApnsDeliveryQueue.ts+1-1
......@@ -59,7 +59,7 @@ export class ApnsDeliveryQueue extends Context.Service<
5959 }
6060>()("t3code-relay/agentActivity/ApnsDeliveryQueue") {}
6161
62const make = Effect.gen(function* () {
62export const make = Effect.gen(function* () {
6363 const sender = yield* ApnsDeliveryQueueSender;
6464 const crypto = yield* Crypto.Crypto;
6565 const config = yield* RelayConfiguration.RelayConfiguration;
infra/relay/src/agentActivity/DeliveryAttempts.ts+1-1
......@@ -82,7 +82,7 @@ function insertValues(
8282 };
8383}
8484
85const make = Effect.gen(function* () {
85export const make = Effect.gen(function* () {
8686 const db = yield* RelayDb.RelayDb;
8787 const crypto = yield* Crypto.Crypto;
8888
infra/relay/src/agentActivity/Devices.ts+1-1
......@@ -57,7 +57,7 @@ export class Devices extends Context.Service<
5757 }
5858>()("t3code-relay/agentActivity/Devices") {}
5959
60const make = Effect.gen(function* () {
60export const make = Effect.gen(function* () {
6161 const db = yield* RelayDb.RelayDb;
6262
6363 return Devices.of({
infra/relay/src/agentActivity/LiveActivities.ts+1-1
......@@ -106,7 +106,7 @@ const encodeRelayAgentActivityAggregateStateJson = Schema.encodeEffect(
106106 Schema.fromJsonString(RelayAgentActivityAggregateStateSchema),
107107);
108108
109const make = Effect.gen(function* () {
109export const make = Effect.gen(function* () {
110110 const db = yield* RelayDb.RelayDb;
111111
112112 return LiveActivities.of({
infra/relay/src/agentActivity/MobileRegistrations.ts+1-1
......@@ -33,7 +33,7 @@ export class MobileRegistrations extends Context.Service<
3333 }
3434>()("t3code-relay/agentActivity/MobileRegistrations") {}
3535
36const make = Effect.gen(function* () {
36export const make = Effect.gen(function* () {
3737 const devices = yield* Devices.Devices;
3838 const liveActivities = yield* LiveActivities.LiveActivities;
3939 const publisher = yield* AgentActivityPublisher.AgentActivityPublisher;
infra/relay/src/agentActivity/apnsDeliveryJobs.test.ts+6-6
......@@ -69,7 +69,7 @@ describe("apnsDeliveryJobs", () => {
6969 });
7070
7171 expect(result).toMatchObject({
72 _tag: "ApnsDeliveryJobInvalid",
72 _tag: "ApnsDeliveryJobSignatureInvalid",
7373 });
7474 });
7575
......@@ -93,7 +93,7 @@ describe("apnsDeliveryJobs", () => {
9393 });
9494
9595 expect(result).toMatchObject({
96 _tag: "ApnsDeliveryJobInvalid",
96 _tag: "ApnsDeliveryJobLiveActivityAggregateMissing",
9797 message: "Live Activity start/update jobs require an aggregate.",
9898 });
9999 });
......@@ -119,7 +119,7 @@ describe("apnsDeliveryJobs", () => {
119119 });
120120
121121 expect(result).toMatchObject({
122 _tag: "ApnsDeliveryJobInvalid",
122 _tag: "ApnsDeliveryJobPushNotificationAggregateUnexpected",
123123 message: "Push notification jobs must not carry aggregate state.",
124124 });
125125 });
......@@ -194,7 +194,7 @@ describe("apnsDeliveryJobs", () => {
194194 nowMs: 0,
195195 }),
196196 ).toMatchObject({
197 _tag: "ApnsDeliveryJobInvalid",
197 _tag: "ApnsDeliveryJobCreatedAtInvalid",
198198 message: "Invalid APNs delivery job creation time.",
199199 });
200200 expect(
......@@ -204,7 +204,7 @@ describe("apnsDeliveryJobs", () => {
204204 nowMs: 0,
205205 }),
206206 ).toMatchObject({
207 _tag: "ApnsDeliveryJobInvalid",
207 _tag: "ApnsDeliveryJobTimeWindowInvalid",
208208 message: "Invalid APNs delivery job time window.",
209209 });
210210 expect(
......@@ -214,7 +214,7 @@ describe("apnsDeliveryJobs", () => {
214214 nowMs: 0,
215215 }),
216216 ).toMatchObject({
217 _tag: "ApnsDeliveryJobInvalid",
217 _tag: "ApnsDeliveryJobTimeWindowTooLong",
218218 message: "APNs delivery job time window is too long.",
219219 });
220220 });
infra/relay/src/agentActivity/apnsDeliveryJobs.ts+116-59
......@@ -49,49 +49,110 @@ export const SignedApnsDeliveryJob = Schema.Struct({
4949});
5050export type SignedApnsDeliveryJob = typeof SignedApnsDeliveryJob.Type;
5151
52export class ApnsDeliveryJobInvalid extends Schema.TaggedErrorClass<ApnsDeliveryJobInvalid>()(
53 "ApnsDeliveryJobInvalid",
54 {
55 reason: Schema.Literals([
56 "invalid-queue-payload",
57 "missing-live-activity-aggregate",
58 "unexpected-live-activity-notification",
59 "missing-push-notification",
60 "unexpected-push-notification-aggregate",
61 "invalid-created-at",
62 "invalid-expires-at",
63 "invalid-time-window",
64 "time-window-too-long",
65 "invalid-signature",
66 ]),
67 },
52export class ApnsDeliveryJobQueuePayloadInvalid extends Schema.TaggedErrorClass<ApnsDeliveryJobQueuePayloadInvalid>()(
53 "ApnsDeliveryJobQueuePayloadInvalid",
54 {},
55) {
56 override get message(): string {
57 return "Invalid APNs delivery queue job.";
58 }
59}
60
61export class ApnsDeliveryJobLiveActivityAggregateMissing extends Schema.TaggedErrorClass<ApnsDeliveryJobLiveActivityAggregateMissing>()(
62 "ApnsDeliveryJobLiveActivityAggregateMissing",
63 {},
64) {
65 override get message(): string {
66 return "Live Activity start/update jobs require an aggregate.";
67 }
68}
69
70export class ApnsDeliveryJobLiveActivityNotificationUnexpected extends Schema.TaggedErrorClass<ApnsDeliveryJobLiveActivityNotificationUnexpected>()(
71 "ApnsDeliveryJobLiveActivityNotificationUnexpected",
72 {},
73) {
74 override get message(): string {
75 return "Live Activity jobs must not carry push notification payloads.";
76 }
77}
78
79export class ApnsDeliveryJobPushNotificationMissing extends Schema.TaggedErrorClass<ApnsDeliveryJobPushNotificationMissing>()(
80 "ApnsDeliveryJobPushNotificationMissing",
81 {},
6882) {
6983 override get message(): string {
70 switch (this.reason) {
71 case "invalid-queue-payload":
72 return "Invalid APNs delivery queue job.";
73 case "missing-live-activity-aggregate":
74 return "Live Activity start/update jobs require an aggregate.";
75 case "unexpected-live-activity-notification":
76 return "Live Activity jobs must not carry push notification payloads.";
77 case "missing-push-notification":
78 return "Push notification jobs require a notification payload.";
79 case "unexpected-push-notification-aggregate":
80 return "Push notification jobs must not carry aggregate state.";
81 case "invalid-created-at":
82 return "Invalid APNs delivery job creation time.";
83 case "invalid-expires-at":
84 return "Invalid APNs delivery job expiry.";
85 case "invalid-time-window":
86 return "Invalid APNs delivery job time window.";
87 case "time-window-too-long":
88 return "APNs delivery job time window is too long.";
89 case "invalid-signature":
90 return "Invalid APNs delivery job signature.";
91 }
84 return "Push notification jobs require a notification payload.";
9285 }
9386}
9487
88export class ApnsDeliveryJobPushNotificationAggregateUnexpected extends Schema.TaggedErrorClass<ApnsDeliveryJobPushNotificationAggregateUnexpected>()(
89 "ApnsDeliveryJobPushNotificationAggregateUnexpected",
90 {},
91) {
92 override get message(): string {
93 return "Push notification jobs must not carry aggregate state.";
94 }
95}
96
97export class ApnsDeliveryJobCreatedAtInvalid extends Schema.TaggedErrorClass<ApnsDeliveryJobCreatedAtInvalid>()(
98 "ApnsDeliveryJobCreatedAtInvalid",
99 {},
100) {
101 override get message(): string {
102 return "Invalid APNs delivery job creation time.";
103 }
104}
105
106export class ApnsDeliveryJobExpiresAtInvalid extends Schema.TaggedErrorClass<ApnsDeliveryJobExpiresAtInvalid>()(
107 "ApnsDeliveryJobExpiresAtInvalid",
108 {},
109) {
110 override get message(): string {
111 return "Invalid APNs delivery job expiry.";
112 }
113}
114
115export class ApnsDeliveryJobTimeWindowInvalid extends Schema.TaggedErrorClass<ApnsDeliveryJobTimeWindowInvalid>()(
116 "ApnsDeliveryJobTimeWindowInvalid",
117 {},
118) {
119 override get message(): string {
120 return "Invalid APNs delivery job time window.";
121 }
122}
123
124export class ApnsDeliveryJobTimeWindowTooLong extends Schema.TaggedErrorClass<ApnsDeliveryJobTimeWindowTooLong>()(
125 "ApnsDeliveryJobTimeWindowTooLong",
126 {},
127) {
128 override get message(): string {
129 return "APNs delivery job time window is too long.";
130 }
131}
132
133export class ApnsDeliveryJobSignatureInvalid extends Schema.TaggedErrorClass<ApnsDeliveryJobSignatureInvalid>()(
134 "ApnsDeliveryJobSignatureInvalid",
135 {},
136) {
137 override get message(): string {
138 return "Invalid APNs delivery job signature.";
139 }
140}
141
142export const ApnsDeliveryJobInvalid = Schema.Union([
143 ApnsDeliveryJobQueuePayloadInvalid,
144 ApnsDeliveryJobLiveActivityAggregateMissing,
145 ApnsDeliveryJobLiveActivityNotificationUnexpected,
146 ApnsDeliveryJobPushNotificationMissing,
147 ApnsDeliveryJobPushNotificationAggregateUnexpected,
148 ApnsDeliveryJobCreatedAtInvalid,
149 ApnsDeliveryJobExpiresAtInvalid,
150 ApnsDeliveryJobTimeWindowInvalid,
151 ApnsDeliveryJobTimeWindowTooLong,
152 ApnsDeliveryJobSignatureInvalid,
153]);
154export type ApnsDeliveryJobInvalid = typeof ApnsDeliveryJobInvalid.Type;
155
95156export class ApnsDeliveryJobExpired extends Schema.TaggedErrorClass<ApnsDeliveryJobExpired>()(
96157 "ApnsDeliveryJobExpired",
97158 {
......@@ -103,7 +164,13 @@ export class ApnsDeliveryJobExpired extends Schema.TaggedErrorClass<ApnsDelivery
103164 }
104165}
105166
106export type ApnsDeliveryJobVerificationError = ApnsDeliveryJobInvalid | ApnsDeliveryJobExpired;
167export const ApnsDeliveryJobVerificationError = Schema.Union([
168 ApnsDeliveryJobInvalid,
169 ApnsDeliveryJobExpired,
170]);
171export type ApnsDeliveryJobVerificationError = typeof ApnsDeliveryJobVerificationError.Type;
172
173export const isApnsDeliveryJobVerificationError = Schema.is(ApnsDeliveryJobVerificationError);
107174
108175export function makeApnsDeliveryJobPayload(input: {
109176 readonly kind: RelayDeliveryKind;
......@@ -141,33 +208,23 @@ function validatePayloadShape(payload: ApnsDeliveryJobPayload): ApnsDeliveryJobI
141208 case "live_activity_start":
142209 case "live_activity_update":
143210 if (payload.aggregate === null) {
144 return new ApnsDeliveryJobInvalid({
145 reason: "missing-live-activity-aggregate",
146 });
211 return new ApnsDeliveryJobLiveActivityAggregateMissing();
147212 }
148213 if (payload.notification !== null) {
149 return new ApnsDeliveryJobInvalid({
150 reason: "unexpected-live-activity-notification",
151 });
214 return new ApnsDeliveryJobLiveActivityNotificationUnexpected();
152215 }
153216 return null;
154217 case "live_activity_end":
155218 if (payload.notification !== null) {
156 return new ApnsDeliveryJobInvalid({
157 reason: "unexpected-live-activity-notification",
158 });
219 return new ApnsDeliveryJobLiveActivityNotificationUnexpected();
159220 }
160221 return null;
161222 case "push_notification":
162223 if (payload.notification === null) {
163 return new ApnsDeliveryJobInvalid({
164 reason: "missing-push-notification",
165 });
224 return new ApnsDeliveryJobPushNotificationMissing();
166225 }
167226 if (payload.aggregate !== null) {
168 return new ApnsDeliveryJobInvalid({
169 reason: "unexpected-push-notification-aggregate",
170 });
227 return new ApnsDeliveryJobPushNotificationAggregateUnexpected();
171228 }
172229 return null;
173230 }
......@@ -213,19 +270,19 @@ export function verifySignedApnsDeliveryJob(input: {
213270 }
214271 const createdAt = DateTime.make(input.job.payload.createdAt);
215272 if (Option.isNone(createdAt)) {
216 return new ApnsDeliveryJobInvalid({ reason: "invalid-created-at" });
273 return new ApnsDeliveryJobCreatedAtInvalid();
217274 }
218275 const expiresAt = DateTime.make(input.job.payload.expiresAt);
219276 if (Option.isNone(expiresAt)) {
220 return new ApnsDeliveryJobInvalid({ reason: "invalid-expires-at" });
277 return new ApnsDeliveryJobExpiresAtInvalid();
221278 }
222279 const createdAtMs = createdAt.value.epochMilliseconds;
223280 const expiresAtMs = expiresAt.value.epochMilliseconds;
224281 if (expiresAtMs <= createdAtMs) {
225 return new ApnsDeliveryJobInvalid({ reason: "invalid-time-window" });
282 return new ApnsDeliveryJobTimeWindowInvalid();
226283 }
227284 if (expiresAtMs - createdAtMs > MAX_JOB_AGE_MS) {
228 return new ApnsDeliveryJobInvalid({ reason: "time-window-too-long" });
285 return new ApnsDeliveryJobTimeWindowTooLong();
229286 }
230287 if (expiresAtMs <= input.nowMs) {
231288 return new ApnsDeliveryJobExpired({ expiresAt: input.job.payload.expiresAt });
......@@ -235,7 +292,7 @@ export function verifySignedApnsDeliveryJob(input: {
235292 payload: input.job.payload,
236293 });
237294 if (!timingSafeEqualBase64Url(input.job.signature, expected)) {
238 return new ApnsDeliveryJobInvalid({ reason: "invalid-signature" });
295 return new ApnsDeliveryJobSignatureInvalid();
239296 }
240297 return input.job.payload;
241298}
infra/relay/src/http/Api.ts+19-12
......@@ -777,18 +777,17 @@ export const serverApi = HttpApiBuilder.group(
777777 reason: "persistence_failed",
778778 traceId,
779779 }),
780 ApnsDeliveryJobInvalid: (_error, traceId) =>
781 new RelayInternalError({
782 code: "internal_error",
783 reason: "internal_error",
784 traceId,
785 }),
786 ApnsDeliveryJobExpired: (_error, traceId) =>
787 new RelayInternalError({
788 code: "internal_error",
789 reason: "internal_error",
790 traceId,
791 }),
780 ApnsDeliveryJobQueuePayloadInvalid: mapApnsDeliveryJobInternalError,
781 ApnsDeliveryJobLiveActivityAggregateMissing: mapApnsDeliveryJobInternalError,
782 ApnsDeliveryJobLiveActivityNotificationUnexpected: mapApnsDeliveryJobInternalError,
783 ApnsDeliveryJobPushNotificationMissing: mapApnsDeliveryJobInternalError,
784 ApnsDeliveryJobPushNotificationAggregateUnexpected: mapApnsDeliveryJobInternalError,
785 ApnsDeliveryJobCreatedAtInvalid: mapApnsDeliveryJobInternalError,
786 ApnsDeliveryJobExpiresAtInvalid: mapApnsDeliveryJobInternalError,
787 ApnsDeliveryJobTimeWindowInvalid: mapApnsDeliveryJobInternalError,
788 ApnsDeliveryJobTimeWindowTooLong: mapApnsDeliveryJobInternalError,
789 ApnsDeliveryJobSignatureInvalid: mapApnsDeliveryJobInternalError,
790 ApnsDeliveryJobExpired: mapApnsDeliveryJobInternalError,
792791 ApnsDeliveryJobClaimInFlight: (_error, traceId) =>
793792 new RelayInternalError({
794793 code: "internal_error",
......@@ -893,6 +892,14 @@ function mapRelayCommonApiErrors(authReason: RelayAuthInvalidReason) {
893892 ): Effect.Effect<A, MapRelayCommonApiError<E>, R> => effect.pipe(Effect.catch(mapError));
894893}
895894
895function mapApnsDeliveryJobInternalError(_error: unknown, traceId: string) {
896 return new RelayInternalError({
897 code: "internal_error",
898 reason: "internal_error",
899 traceId,
900 });
901}
902
896903type TaggedErrorTag<E> = Extract<E, { readonly _tag: string }>["_tag"];
897904
898905type MapErrorTagCases<E> = {