Browse Source

Checkpoint: Problem with worker metdata for VapidKeys

pull/1/head
Matthew Raymer 1 year ago
parent
commit
5b689125a6
  1. 24
      package-lock.json
  2. 14
      src/VapidKeys.ts
  3. 24
      src/db.ts
  4. 10
      src/main.ts
  5. 15
      src/notificationService.ts
  6. 5
      src/subscriptionService.ts
  7. 44
      src/vapidService.ts
  8. 3
      src/worker.ts

24
package-lock.json

@ -2991,9 +2991,9 @@
}
},
"node_modules/caniuse-lite": {
"version": "1.0.30001520",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001520.tgz",
"integrity": "sha512-tahF5O9EiiTzwTUqAeFjIZbn4Dnqxzz7ktrgGlMYNLH43Ul26IgTMH/zvL3DG0lZxBYnlT04axvInszUsZULdA==",
"version": "1.0.30001521",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001521.tgz",
"integrity": "sha512-fnx1grfpEOvDGH+V17eccmNjucGUnCbP6KL+l5KqBIerp26WK/+RQ7CIDE37KGJjaPyqWXXlFUyKiWmvdNNKmQ==",
"dev": true,
"funding": [
{
@ -3596,9 +3596,9 @@
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
},
"node_modules/electron-to-chromium": {
"version": "1.4.491",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.491.tgz",
"integrity": "sha512-ZzPqGKghdVzlQJ+qpfE+r6EB321zed7e5JsvHIlMM4zPFF8okXUkF5Of7h7F3l3cltPL0rG7YVmlp5Qro7RQLA==",
"version": "1.4.492",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.492.tgz",
"integrity": "sha512-36K9b/6skMVwAIEsC7GiQ8I8N3soCALVSHqWHzNDtGemAcI9Xu8hP02cywWM0A794rTHm0b0zHPeLJHtgFVamQ==",
"dev": true
},
"node_modules/elliptic": {
@ -5279,9 +5279,9 @@
}
},
"node_modules/jackspeak": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.2.3.tgz",
"integrity": "sha512-pF0kfjmg8DJLxDrizHoCZGUFz4P4czQ3HyfW4BU0ffebYkzAVlBywp5zaxW/TM+r0sGbmrQdi8EQQVTJFxnGsQ==",
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.0.tgz",
"integrity": "sha512-uKmsITSsF4rUWQHzqaRUuyAir3fZfW3f202Ee34lz/gZCi970CPZwyQXLGNgWJvvZbvFyzeyGq0+4fcG/mBKZg==",
"dependencies": {
"@isaacs/cliui": "^8.0.2"
},
@ -7297,9 +7297,9 @@
}
},
"node_modules/prettier": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.1.tgz",
"integrity": "sha512-fcOWSnnpCrovBsmFZIGIy9UqK2FaI7Hqax+DIO0A9UxeVoY4iweyaFjS5TavZN97Hfehph0nhsZnjlVKzEQSrQ==",
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.2.tgz",
"integrity": "sha512-o2YR9qtniXvwEZlOKbveKfDQVyqxbEIWn48Z8m3ZJjBjcCmUy3xZGIv+7AkaeuaTr6yPXJjwv07ZWlsWbEy1rQ==",
"dev": true,
"bin": {
"prettier": "bin/prettier.cjs"

14
src/VapidKeys.ts

@ -0,0 +1,14 @@
// Subscription.ts
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
@Entity()
export class VapidKeys {
@PrimaryGeneratedColumn()
id: number;
@Column()
publicKey: string;
@Column()
privateKey: string;
}

24
src/db.ts

@ -1,10 +1,13 @@
import { DataSource, Repository } from "typeorm";
import {Subscription} from './Subscription.js'
import { VapidKeys } from './VapidKeys.js';
export class DBService {
private dataSource: DataSource;
private vapidSource: DataSource;
private subscriptionRepository: Repository<Subscription>;
private vapidRepository: Repository<VapidKeys>;
constructor() {
this.dataSource = new DataSource({
@ -12,7 +15,15 @@ export class DBService {
database: "subscriptions",
entities: [Subscription],
});
this.dataSource.getRepository(Subscription)
this.subscriptionRepository = this.dataSource.getRepository(Subscription)
this.vapidSource = new DataSource({
type: "sqlite",
database: "vapidKeys",
entities: [VapidKeys],
});
this.vapidRepository = this.vapidSource.getRepository(VapidKeys);
}
async saveSubscription(endpoint: string, keys_p256dh: string, keys_auth: string) {
@ -26,4 +37,15 @@ export class DBService {
async getSubscriptions(): Promise<Subscription[]> {
return this.subscriptionRepository.find();
}
async getVapidKeys(): Promise<VapidKeys[]> {
return this.vapidRepository.find();
}
async saveVapidKeys(publicKey: string, privateKey: string) {
const vapidkeys = new VapidKeys();
vapidkeys.privateKey = privateKey;
vapidkeys.publicKey = publicKey;
return this.vapidRepository.save(vapidkeys);
}
}

10
src/main.ts

@ -1,4 +1,4 @@
import { Subscription, SubscriptionService } from './subscriptionService.js';
import { SubscriptionService } from './subscriptionService.js';
import express, { Express, Request, Response } from 'express';
import { Worker } from 'worker_threads';
import { fileURLToPath } from 'url';
@ -7,6 +7,14 @@ import { dirname, join } from 'path';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
export interface Subscription {
endpoint: string;
keys: {
p256dh: string;
auth: string;
};
}
class Server {
private app: Express;
private port: number;

15
src/notificationService.ts

@ -1,8 +1,10 @@
import { SubscriptionService, Subscription } from './subscriptionService.js';
import { VapidService, VapidKeys } from './vapidService.js';
import { SubscriptionService } from './subscriptionService.js';
import { VapidService } from './vapidService.js';
import { VapidKeys } from './VapidKeys.js';
import * as https from 'https';
import * as http_ece from 'http_ece';
import crypto from 'crypto';
import { Subscription } from "./Subscription.js"
export interface Message {
title: string;
@ -14,7 +16,6 @@ export class NotificationService {
private subscriptionService: SubscriptionService;
private vapidService: VapidService;
private vapidKeys: VapidKeys;
constructor() {
this.subscriptionService = new SubscriptionService();
@ -27,7 +28,6 @@ export class NotificationService {
async broadcast(message: Message): Promise<void> {
const subscriptions = await this.subscriptionService.fetchSubscriptions();
this.vapidKeys = await this.vapidService.getVapidKeys();
for (const subscription of subscriptions) {
await this.pushToEndpoint(subscription, message);
@ -35,14 +35,13 @@ export class NotificationService {
}
async sendNotification(subscription: Subscription, message: Message) {
this.vapidKeys = await this.vapidService.getVapidKeys();
await this.pushToEndpoint(subscription, message);
}
private async pushToEndpoint(subscription: Subscription, message: Message): Promise<void> {
const payload = JSON.stringify(message);
const encrypted = this.encrypt(subscription.keys.p256dh, subscription.keys.auth, payload);
const encrypted = this.encrypt(subscription.keys_p256dh, subscription.keys_auth, payload);
const endpoint = subscription.endpoint;
const vapidHeaders = await this.vapidService.createVapidAuthHeader(endpoint, 12 * 60 * 60, 'mailto:example@example.com');
@ -86,12 +85,12 @@ export class NotificationService {
'auth': auth
};
const vapidKeys: VapidKeys = this.vapidService.getVapidKeys()[0];
return http_ece.encrypt(payload, {
'salt': this.generateSalt(),
'dh': this.vapidKeys.publicKey,
'dh': vapidKeys.publicKey,
'keyid': 'p256dh',
'contentEncoding': 'aes128gcm'
});
}
}

5
src/subscriptionService.ts

@ -1,6 +1,7 @@
import { DBService } from './db.js';
import { Subscription } from './Subscription.js';
export interface Subscription {
export interface SubscriptionData {
endpoint: string;
keys: {
p256dh: string;
@ -15,7 +16,7 @@ export class SubscriptionService {
this.dbService = new DBService();
}
async addSubscription(subscription: Subscription): Promise<void> {
async addSubscription(subscription: SubscriptionData): Promise<void> {
await this.dbService.saveSubscription(
subscription.endpoint,
subscription.keys.p256dh,

44
src/vapidService.ts

@ -1,25 +1,24 @@
import { Database } from 'sqlite3';
import jwt from 'jsonwebtoken';
import crypto from 'crypto';
import { DBService } from './db.js';
import { VapidKeys } from './VapidKeys.js';
export interface VapidKeys {
export interface VapidKeyData {
publicKey: string;
privateKey: string;
}
export class VapidService {
private db: Database;
private dbService: DBService;
constructor() {
this.db = new Database('vapidKeys.db');
this.initializeDatabase();
}
private initializeDatabase(): void {
this.db.run(`CREATE TABLE IF NOT EXISTS vapid (id INTEGER PRIMARY KEY, publicKey TEXT, privateKey TEXT)`);
this.dbService = new DBService();
const keys = this.generateVAPIDKeys();
this.addVapidKeys(keys);
}
private generateVAPIDKeys(): VapidKeys {
private generateVAPIDKeys(): VapidKeyData {
const ecdh = crypto.createECDH('prime256v1');
ecdh.generateKeys();
@ -29,26 +28,19 @@ export class VapidService {
};
}
async getVapidKeys(): Promise<VapidKeys> {
return new Promise((resolve, reject) => {
this.db.get('SELECT publicKey, privateKey FROM vapid WHERE id = ?', [1], (err, row: VapidKeys) => {
if (err) reject(err);
async addVapidKeys(vapidkeys: VapidKeyData) {
const keys = await this.getVapidKeys();
if (keys.length == 0) {
this.dbService.saveVapidKeys(vapidkeys.publicKey, vapidkeys.privateKey);
}
}
if (row) {
resolve({ publicKey: row.publicKey, privateKey: row.privateKey });
} else {
const keys = this.generateVAPIDKeys();
this.db.run('INSERT INTO vapid (publicKey, privateKey) VALUES (?, ?)', [keys.publicKey, keys.privateKey], (err) => {
if (err) reject(err);
resolve(keys);
});
}
});
});
async getVapidKeys(): Promise<VapidKeys[]> {
return this.dbService.getVapidKeys();
}
async createVapidAuthHeader(endpoint: string, expiration: number, subject: string): Promise<{ 'Authorization': string, 'Crypto-Key': string }> {
const { publicKey, privateKey } = await this.getVapidKeys();
const { publicKey, privateKey } = await this.getVapidKeys()[0];
const jwtInfo = {
aud: new URL(endpoint).origin,

3
src/worker.ts

@ -8,8 +8,9 @@ class WorkerThread {
constructor(interval: number) {
this.interval = interval;
this.notificationService = new NotificationService();
this.message.title = "Check the app.";
this.message = { "title": "Check the app." };
this.startPeriodicTask();
}

Loading…
Cancel
Save