diff --git a/subscriptions.db b/push_server similarity index 100% rename from subscriptions.db rename to push_server diff --git a/src/db.ts b/src/db.ts index d3139d4..59aeb6e 100644 --- a/src/db.ts +++ b/src/db.ts @@ -1,51 +1,71 @@ import { DataSource, Repository } from "typeorm"; -import {Subscription} from './Subscription.js' +import { Subscription } from './Subscription.js' import { VapidKeys } from './VapidKeys.js'; -export class DBService { +class DBService { + private static instance: DBService; private dataSource: DataSource; - private vapidSource: DataSource; private subscriptionRepository: Repository; private vapidRepository: Repository; + public isReady = false; - constructor() { + + private constructor() { + console.log("DBService constructor"); this.dataSource = new DataSource({ type: "sqlite", - database: "subscriptions", - entities: [Subscription], + database: "push_server", + entities: [VapidKeys, Subscription] }); - this.subscriptionRepository = this.dataSource.getRepository(Subscription) + this.dataSource.initialize().then(()=>{ + console.log("Initialized"); + this.subscriptionRepository = this.dataSource.getRepository(Subscription); + this.vapidRepository = this.dataSource.getRepository(VapidKeys); + this.isReady = true; + }).catch((err)=>{console.error(err);}); + } - this.vapidSource = new DataSource({ - type: "sqlite", - database: "vapidKeys", - entities: [VapidKeys], - }); - - this.vapidRepository = this.vapidSource.getRepository(VapidKeys); + + public static getInstance(): DBService { + if (!DBService.instance) { + DBService.instance = new DBService(); + } + return DBService.instance; } - + + async saveSubscription(endpoint: string, keys_p256dh: string, keys_auth: string) { const subscription = new Subscription(); subscription.endpoint = endpoint; subscription.keys_auth = keys_auth; subscription.keys_p256dh = keys_p256dh; - return this.subscriptionRepository.save(subscription); + return await this.dataSource.manager.save(subscription); } + async getSubscriptions(): Promise { - return this.subscriptionRepository.find(); + return await this.subscriptionRepository.find(); } + async getVapidKeys(): Promise { - return this.vapidRepository.find(); + let result = [ new VapidKeys() ]; + if ( this.vapidRepository && this.isReady) { + result = await this.vapidRepository.find(); + } else { + console.log("vapidRepository is null or db is not ready"); + } + return result } + async saveVapidKeys(publicKey: string, privateKey: string) { const vapidkeys = new VapidKeys(); vapidkeys.privateKey = privateKey; vapidkeys.publicKey = publicKey; - return this.vapidRepository.save(vapidkeys); + return await this.dataSource.manager.save(vapidkeys); } } + +export default DBService; \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index 85b5560..600149c 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,10 +1,13 @@ -import { SubscriptionService } from './subscriptionService.js'; +import SubscriptionService from './subscriptionService.js'; import { Message, NotificationService } from './notificationService.js'; +import { VapidKeys } from './VapidKeys.js'; +import VapidService from './vapidService.js'; + import express, { Express, Request, Response } from 'express'; + import { Worker } from 'worker_threads'; import { fileURLToPath } from 'url'; import { dirname, join } from 'path'; -import { networkInterfaces } from 'os'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); @@ -21,14 +24,14 @@ class Server { private app: Express; private port: number; private worker?: Worker; - private subscriptionService: SubscriptionService; + private subscriptionService: SubscriptionService = SubscriptionService.getInstance(); private notificationService: NotificationService; + private vapidService: VapidService = VapidService.getInstance(); private message: Message; constructor(port: number) { this.app = express(); this.port = port; - this.subscriptionService = new SubscriptionService(); this.notificationService = new NotificationService(); this.setupRoutes(); @@ -36,15 +39,22 @@ class Server { this.setupWorkerListeners(); } + private setupRoutes(): void { this.app.post('/subscribe', async (req: Request, res: Response) => { const subscription = req.body as Subscription; - const subscriptionService = new SubscriptionService(); - await subscriptionService.addSubscription(subscription); + await this.subscriptionService.addSubscription(subscription); res.status(201).send(); }); + + this.app.get('/vapid', async (_: Request, res: Response) => { + const vapidkeys: VapidKeys = await this.vapidService.getVapidKeys()[0]; + console.log(vapidkeys); + res.send({}); + }); } + private startWorker(): void { const workerPath = join(__dirname,'./worker.js'); diff --git a/src/notificationService.ts b/src/notificationService.ts index d06e3a7..151454f 100644 --- a/src/notificationService.ts +++ b/src/notificationService.ts @@ -1,5 +1,5 @@ -import { SubscriptionService } from './subscriptionService.js'; -import { VapidService } 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'; @@ -14,12 +14,12 @@ export interface Message { export class NotificationService { - private subscriptionService: SubscriptionService; - private vapidService: VapidService; + private subscriptionService: SubscriptionService = SubscriptionService.getInstance(); + private vapidService: VapidService = VapidService.getInstance(); constructor() { - this.subscriptionService = new SubscriptionService(); - this.vapidService = new VapidService(); + console.log("NotificationService constructor"); + } private generateSalt(length = 16): Buffer { diff --git a/src/subscriptionService.ts b/src/subscriptionService.ts index 5755aa1..8178b19 100644 --- a/src/subscriptionService.ts +++ b/src/subscriptionService.ts @@ -1,4 +1,4 @@ -import { DBService } from './db.js'; +import DBService from './db.js'; import { Subscription } from './Subscription.js'; export interface SubscriptionData { @@ -9,13 +9,23 @@ export interface SubscriptionData { }; } -export class SubscriptionService { - private dbService: DBService; +class SubscriptionService { + private static instance: SubscriptionService; + private dbService: DBService = DBService.getInstance(); - constructor() { - this.dbService = new DBService(); + private constructor() { + console.log("SubscriptionService constructor"); } + + public static getInstance(): SubscriptionService { + if (!SubscriptionService.instance) { + SubscriptionService.instance = new SubscriptionService(); + } + return SubscriptionService.instance; + } + + async addSubscription(subscription: SubscriptionData): Promise { await this.dbService.saveSubscription( subscription.endpoint, @@ -24,7 +34,10 @@ export class SubscriptionService { ); } + async fetchSubscriptions(): Promise { return this.dbService.getSubscriptions(); } } + +export default SubscriptionService; \ No newline at end of file diff --git a/src/vapidService.ts b/src/vapidService.ts index 5fcfcd9..b4881d0 100644 --- a/src/vapidService.ts +++ b/src/vapidService.ts @@ -1,44 +1,63 @@ +import { VapidKeys } from './VapidKeys.js'; import jwt from 'jsonwebtoken'; import crypto from 'crypto'; -import { DBService } from './db.js'; -import { VapidKeys } from './VapidKeys.js'; +import DBService from './db.js'; export interface VapidKeyData { publicKey: string; privateKey: string; } -export class VapidService { - private dbService: DBService; +class VapidService { + private static instance: VapidService; + private dbService: DBService = DBService.getInstance(); - constructor() { - this.dbService = new DBService(); - const keys = this.generateVAPIDKeys(); - this.addVapidKeys(keys); + private constructor() { + if (this.dbService.isReady) { + const keys = this.generateVAPIDKeys(); + this.addVapidKeys(keys); + + } else { + console.log("Not ready."); + } + } + + + public static getInstance(): VapidService { + if (!VapidService.instance) { + VapidService.instance = new VapidService(); + } + return VapidService.instance; } + private generateVAPIDKeys(): VapidKeyData { const ecdh = crypto.createECDH('prime256v1'); ecdh.generateKeys(); - - return { + const result = { publicKey: ecdh.getPublicKey().toString('base64'), privateKey: ecdh.getPrivateKey().toString('base64') }; + console.log(result); + return result; } - async addVapidKeys(vapidkeys: VapidKeyData) { + + private async addVapidKeys(vapidkeys: VapidKeyData) { const keys = await this.getVapidKeys(); - if (keys.length == 0) { + console.log(keys); + if (keys.length == 1 && typeof(keys[0].publicKey) == "undefined" ) { this.dbService.saveVapidKeys(vapidkeys.publicKey, vapidkeys.privateKey); } } + async getVapidKeys(): Promise { return this.dbService.getVapidKeys(); } + async createVapidAuthHeader(endpoint: string, expiration: number, subject: string): Promise<{ 'Authorization': string, 'Crypto-Key': string }> { const { publicKey, privateKey } = await this.getVapidKeys()[0]; @@ -56,3 +75,5 @@ export class VapidService { }; } } + +export default VapidService; \ No newline at end of file