Merge pull request #20 from brendan-ch/hotfix/update-apns-key-for-deployment

hotfix/update-apns-key-for-deployment
This commit is contained in:
2025-02-11 12:18:24 -08:00
committed by GitHub
3 changed files with 21 additions and 24 deletions

View File

@@ -1,4 +1,9 @@
# Set to "1" for true
APNS_IS_PRODUCTION=
APNS_KEY_ID= APNS_KEY_ID=
APNS_TEAM_ID= APNS_TEAM_ID=
APNS_BUNDLE_ID= APNS_BUNDLE_ID=
APNS_KEY_PATH=
# base64-encoded APNs private key
APNS_PRIVATE_KEY=

View File

@@ -18,7 +18,7 @@ interface APNsUrl {
} }
export class NotificationService { export class NotificationService {
public readonly secondsThresholdForNotificationToFire = 300; public readonly secondsThresholdForNotificationToFire = 180;
private apnsToken: string | undefined = undefined; private apnsToken: string | undefined = undefined;
@@ -52,9 +52,10 @@ export class NotificationService {
const keyId = process.env.APNS_KEY_ID; const keyId = process.env.APNS_KEY_ID;
const teamId = process.env.APNS_TEAM_ID; const teamId = process.env.APNS_TEAM_ID;
const privateKeyPath = process.env.APNS_KEY_PATH;
if (!privateKeyPath) return; const privateKeyBase64 = process.env.APNS_PRIVATE_KEY;
const privateKey = fs.readFileSync(privateKeyPath); if (!privateKeyBase64) return;
const privateKey = Buffer.from(privateKeyBase64, 'base64').toString('utf-8');
const tokenHeader = { const tokenHeader = {
alg: "ES256", alg: "ES256",
@@ -154,9 +155,9 @@ export class NotificationService {
const devBaseUrl = "https://api.development.push.apple.com" const devBaseUrl = "https://api.development.push.apple.com"
const prodBaseUrl = "https://api.push.apple.com" const prodBaseUrl = "https://api.push.apple.com"
let hostToUse = prodBaseUrl; let hostToUse = devBaseUrl;
if (process.env.NODE_ENV !== "production") { if (process.env.APNS_IS_PRODUCTION === "1") {
hostToUse = devBaseUrl; hostToUse = prodBaseUrl;
} }
const path = "/3/device/" + deviceId; const path = "/3/device/" + deviceId;

View File

@@ -1,21 +1,14 @@
import { beforeEach, describe, expect, it, jest } from "@jest/globals"; import { beforeEach, describe, expect, it, jest } from "@jest/globals";
import { NotificationService } from "../../src/services/NotificationService"; import { NotificationService } from "../../src/services/NotificationService";
import { UnoptimizedInMemoryRepository } from "../../src/repositories/UnoptimizedInMemoryRepository"; import { UnoptimizedInMemoryRepository } from "../../src/repositories/UnoptimizedInMemoryRepository";
import fs from "fs";
import http2 from "http2"; import http2 from "http2";
import { IEta, IShuttle, IStop } from "../../src/entities/entities"; import { IEta, IShuttle, IStop } from "../../src/entities/entities";
import { addMockShuttleToRepository, addMockStopToRepository } from "../testHelpers/repositorySetupHelpers"; import { addMockShuttleToRepository, addMockStopToRepository } from "../testHelpers/repositorySetupHelpers";
import EventEmitter = require("node:events"); import EventEmitter = require("node:events");
jest.mock("fs");
jest.mock("http2"); jest.mock("http2");
const sampleKey = `-----BEGIN PRIVATE KEY----- const sampleKeyBase64 = "LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JR1RBZ0VBTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEJIa3dkd0lCQVFRZ3NybVNBWklhZ09mQ1A4c0IKV2kyQ0JYRzFPbzd2MWJpc3BJWkN3SXI0UkRlZ0NnWUlLb1pJemowREFRZWhSQU5DQUFUWkh4VjJ3UUpMTUJxKwp5YSt5ZkdpM2cyWlV2NmhyZmUrajA4eXRla1BIalhTMHF6Sm9WRUx6S0hhNkVMOVlBb1pEWEJ0QjZoK2ZHaFhlClNPY09OYmFmCi0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K";
MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgsrmSAZIagOfCP8sB
Wi2CBXG1Oo7v1bispIZCwIr4RDegCgYIKoZIzj0DAQehRANCAATZHxV2wQJLMBq+
ya+yfGi3g2ZUv6hrfe+j08ytekPHjXS0qzJoVELzKHa6EL9YAoZDXBtB6h+fGhXe
SOcONbaf
-----END PRIVATE KEY-----`
/** /**
* Wait for a condition to become true until the timeout * Wait for a condition to become true until the timeout
@@ -75,11 +68,9 @@ describe("NotificationService", () => {
...process.env, ...process.env,
APNS_KEY_ID: "1", APNS_KEY_ID: "1",
APNS_TEAM_ID: "1", APNS_TEAM_ID: "1",
APNS_KEY_PATH: "./dummy-path.p8", APNS_BUNDLE_ID: "dev.bchen.ProjectInter",
APNS_BUNDLE_ID: "dev.bchen.ProjectInter" APNS_PRIVATE_KEY: sampleKeyBase64,
}; };
(fs.readFileSync as jest.Mock).mockReturnValue(sampleKey);
}); });
beforeEach(() => { beforeEach(() => {
@@ -198,8 +189,8 @@ describe("NotificationService", () => {
}); });
describe('getAPNsFullUrlToUse', () => { describe('getAPNsFullUrlToUse', () => {
it('should return the production URL when NODE_ENV is set to "production"', () => { it('should return the production URL when APNS_IS_PRODUCTION is set to "1"', () => {
process.env.NODE_ENV = 'production'; process.env.APNS_IS_PRODUCTION = "1";
const deviceId = 'testDeviceId'; const deviceId = 'testDeviceId';
const result = NotificationService.getAPNsFullUrlToUse(deviceId); const result = NotificationService.getAPNsFullUrlToUse(deviceId);
@@ -209,8 +200,8 @@ describe("NotificationService", () => {
expect(path).toBe(`/3/device/${deviceId}`); expect(path).toBe(`/3/device/${deviceId}`);
}); });
it('should return the sandbox URL when NODE_ENV is not set to "production"', () => { it('should return the sandbox URL when APNS_IS_PRODUCTION is set to something other than 1', () => {
process.env.NODE_ENV = 'development'; process.env.APNS_IS_PRODUCTION = "0";
const deviceId = 'testDeviceId'; const deviceId = 'testDeviceId';
const result = NotificationService.getAPNsFullUrlToUse(deviceId); const result = NotificationService.getAPNsFullUrlToUse(deviceId);