From c8b164e7d782215d509f8995fca7d15171638412 Mon Sep 17 00:00:00 2001 From: Vasilii Mikhailovskii Date: Tue, 3 Dec 2024 11:11:44 +0200 Subject: [PATCH] build service --- ...vice-message-delivery-method-web-push.yaml | 43 +++++++++++++++++++ packages/types/package.json | 16 +++---- .../{ => src}/DBDocuments/BaseDocument.ts | 0 .../{ => src}/DBDocuments/MessageDocument.ts | 0 .../DBDocuments/PublicSettingsDocument.ts | 0 .../{ => src}/DBDocuments/QRCodeDocument.ts | 0 packages/types/{ => src}/DBDocuments/Role.ts | 0 .../DBDocuments/ServerSettingsDocument.ts | 0 .../DBDocuments/UserChatSettingsDocument.ts | 0 .../{ => src}/DBDocuments/UserDocument.ts | 0 .../WebPushSubscriptionDocument.ts | 0 packages/types/{ => src}/DBDocuments/index.ts | 0 packages/types/{ => src}/index.ts | 0 pnpm-lock.yaml | 22 ++++------ .../Dockerfile | 8 ++++ .../package.json | 11 +++-- .../src/dbs.ts | 38 ++++++++++++++-- .../src/health-server.ts | 11 ++++- .../src/server.ts | 26 +++++++++-- .../src/serviceSeqTracker.ts | 18 ++++---- 20 files changed, 147 insertions(+), 46 deletions(-) create mode 100644 .gitea/workflows/deploy-service-message-delivery-method-web-push.yaml rename packages/types/{ => src}/DBDocuments/BaseDocument.ts (100%) rename packages/types/{ => src}/DBDocuments/MessageDocument.ts (100%) rename packages/types/{ => src}/DBDocuments/PublicSettingsDocument.ts (100%) rename packages/types/{ => src}/DBDocuments/QRCodeDocument.ts (100%) rename packages/types/{ => src}/DBDocuments/Role.ts (100%) rename packages/types/{ => src}/DBDocuments/ServerSettingsDocument.ts (100%) rename packages/types/{ => src}/DBDocuments/UserChatSettingsDocument.ts (100%) rename packages/types/{ => src}/DBDocuments/UserDocument.ts (100%) rename packages/types/{ => src}/DBDocuments/WebPushSubscriptionDocument.ts (100%) rename packages/types/{ => src}/DBDocuments/index.ts (100%) rename packages/types/{ => src}/index.ts (100%) create mode 100644 services/message-delivery-method-web-push/Dockerfile diff --git a/.gitea/workflows/deploy-service-message-delivery-method-web-push.yaml b/.gitea/workflows/deploy-service-message-delivery-method-web-push.yaml new file mode 100644 index 0000000..8a14c97 --- /dev/null +++ b/.gitea/workflows/deploy-service-message-delivery-method-web-push.yaml @@ -0,0 +1,43 @@ +name: Deploy frontend +run-name: ${{ github.actor }} deployed to build branch +on: + push: + branches: + - main + paths: + - 'services/message-delivery-method-web-push/**' + - 'packages/couchdb-changes-stream/**' + - '.gitea/workflows/deploy-service-message-delivery-method-web-push.yaml' + +jobs: + frontend: + runs-on: ubuntu-latest + + container: + volumes: + - /www/hereconnect.condev.ru:/www/hereconnect.condev.ru + + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: '22' + + - name: install dependencies + run: | + corepack enable pnpm && corepack use pnpm@8.15.9 + pnpm install --filter ./services/message-delivery-method-web-push + + - name: build production + run: | + pnpm build -w services/message-delivery-method-web-push + pnpm --filter ./services/message-delivery-method-web-push deploy --prod build/service-message-delivery-method-web-push + cp services/message-delivery-method-web-push/Dockerfile build/service-message-delivery-method-web-push/ + cd build/service-message-delivery-method-web-push + docker build --file services/message-delivery-method-web-push/Dockerfile -t hereconnect/service-message-delivery-method-web-push:${{env.GITHUB_SHA}} build/service-message-delivery-method-web-push + docker tag hereconnect/service-message-delivery-method-web-push:${{env.GITHUB_SHA}} hereconnect/service-message-delivery-method-web-push:latest + + - name: cleanup build directories + if: always() + run: | + docker rmi hereconnect/service-message-delivery-method-web-push:${{env.GITHUB_SHA}} \ No newline at end of file diff --git a/packages/types/package.json b/packages/types/package.json index 407f854..514a299 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,22 +1,18 @@ { "name": "@hereconnect/types", "version": "1.0.0", - "main": "index.ts", - "types": "index.ts", + "main": "src/index.ts", + "types": "src/index.ts", "exports": { ".": { - "import": "./index.ts", - "default": "./index.ts" + "import": "./src/index.ts", + "default": "./src/index.ts" } }, "files": ["src/**/*"], "private": true, - "scripts": { - }, - "dependencies": { - "typescript": "~5.6.3" - }, - "devDependencies": { + "peerDependencies": { + "typescript": "~5.6.3", "@types/web-push": "^3.6.4" } } \ No newline at end of file diff --git a/packages/types/DBDocuments/BaseDocument.ts b/packages/types/src/DBDocuments/BaseDocument.ts similarity index 100% rename from packages/types/DBDocuments/BaseDocument.ts rename to packages/types/src/DBDocuments/BaseDocument.ts diff --git a/packages/types/DBDocuments/MessageDocument.ts b/packages/types/src/DBDocuments/MessageDocument.ts similarity index 100% rename from packages/types/DBDocuments/MessageDocument.ts rename to packages/types/src/DBDocuments/MessageDocument.ts diff --git a/packages/types/DBDocuments/PublicSettingsDocument.ts b/packages/types/src/DBDocuments/PublicSettingsDocument.ts similarity index 100% rename from packages/types/DBDocuments/PublicSettingsDocument.ts rename to packages/types/src/DBDocuments/PublicSettingsDocument.ts diff --git a/packages/types/DBDocuments/QRCodeDocument.ts b/packages/types/src/DBDocuments/QRCodeDocument.ts similarity index 100% rename from packages/types/DBDocuments/QRCodeDocument.ts rename to packages/types/src/DBDocuments/QRCodeDocument.ts diff --git a/packages/types/DBDocuments/Role.ts b/packages/types/src/DBDocuments/Role.ts similarity index 100% rename from packages/types/DBDocuments/Role.ts rename to packages/types/src/DBDocuments/Role.ts diff --git a/packages/types/DBDocuments/ServerSettingsDocument.ts b/packages/types/src/DBDocuments/ServerSettingsDocument.ts similarity index 100% rename from packages/types/DBDocuments/ServerSettingsDocument.ts rename to packages/types/src/DBDocuments/ServerSettingsDocument.ts diff --git a/packages/types/DBDocuments/UserChatSettingsDocument.ts b/packages/types/src/DBDocuments/UserChatSettingsDocument.ts similarity index 100% rename from packages/types/DBDocuments/UserChatSettingsDocument.ts rename to packages/types/src/DBDocuments/UserChatSettingsDocument.ts diff --git a/packages/types/DBDocuments/UserDocument.ts b/packages/types/src/DBDocuments/UserDocument.ts similarity index 100% rename from packages/types/DBDocuments/UserDocument.ts rename to packages/types/src/DBDocuments/UserDocument.ts diff --git a/packages/types/DBDocuments/WebPushSubscriptionDocument.ts b/packages/types/src/DBDocuments/WebPushSubscriptionDocument.ts similarity index 100% rename from packages/types/DBDocuments/WebPushSubscriptionDocument.ts rename to packages/types/src/DBDocuments/WebPushSubscriptionDocument.ts diff --git a/packages/types/DBDocuments/index.ts b/packages/types/src/DBDocuments/index.ts similarity index 100% rename from packages/types/DBDocuments/index.ts rename to packages/types/src/DBDocuments/index.ts diff --git a/packages/types/index.ts b/packages/types/src/index.ts similarity index 100% rename from packages/types/index.ts rename to packages/types/src/index.ts diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 612fcbe..0ec1ca1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -209,13 +209,12 @@ importers: packages/types: dependencies: - typescript: - specifier: ~5.6.3 - version: 5.6.3 - devDependencies: '@types/web-push': specifier: ^3.6.4 version: 3.6.4 + typescript: + specifier: ~5.6.3 + version: 5.6.3 services/db-migrations: dependencies: @@ -240,9 +239,6 @@ importers: '@hereconnect/couchdb-changes-stream': specifier: workspace:^ version: link:../../packages/couchdb-changes-stream - '@hereconnect/types': - specifier: ^1.0.0 - version: link:../../packages/types pouchdb-adapter-http: specifier: ^9.0.0 version: 9.0.0 @@ -252,13 +248,13 @@ importers: pouchdb-find: specifier: ^9.0.0 version: 9.0.0 - typescript: - specifier: ~5.6.3 - version: 5.6.3 web-push: specifier: ^3.6.7 version: 3.6.7 devDependencies: + '@hereconnect/types': + specifier: ^1.0.0 + version: link:../../packages/types '@types/node': specifier: ^20.17.6 version: 20.17.9 @@ -274,6 +270,9 @@ importers: '@types/web-push': specifier: ^3.6.4 version: 3.6.4 + typescript: + specifier: ~5.6.3 + version: 5.6.3 packages: @@ -1776,7 +1775,6 @@ packages: resolution: {integrity: sha512-0JOXkRyLanfGPE2QRCwgxhzlBAvaRdCNMcvbd7jFfpmD4eEXll7LRwy5ymJmyeZqk7Nh7eD2LeUyQ68BbndmXw==} dependencies: undici-types: 6.19.8 - dev: true /@types/pouchdb-adapter-http@6.1.6: resolution: {integrity: sha512-DJur1mt07GJXwGb5K+MOILoCOSgoQpsi7hybcTzRLeR3IO8Y8eq7TnhTkftAJdx9VHJGOiOXFjO+8BYM69j5yA==} @@ -1851,7 +1849,6 @@ packages: resolution: {integrity: sha512-GnJmSr40H3RAnj0s34FNTcJi1hmWFV5KXugE0mYWnYhgTAHLJ/dJKAwDmvPJYMke0RplY2XE9LnM4hqSqKIjhQ==} dependencies: '@types/node': 20.17.9 - dev: true /@types/yargs-parser@21.0.3: resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} @@ -6329,7 +6326,6 @@ packages: /undici-types@6.19.8: resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} - dev: true /unhead@1.11.13: resolution: {integrity: sha512-I7yyvqRfpPPzXuCG7HKZkgAWJDbzXDDEVyib4C/78HREqhNGHVSyo4TqX1h1xB5cx7WYc21HHDRT2/8YkqOy2w==} diff --git a/services/message-delivery-method-web-push/Dockerfile b/services/message-delivery-method-web-push/Dockerfile new file mode 100644 index 0000000..00e9134 --- /dev/null +++ b/services/message-delivery-method-web-push/Dockerfile @@ -0,0 +1,8 @@ +FROM oven/bun:1.1.38-alpine + +COPY . ./ + +HEALTHCHECK --interval=30s --timeout=10s --start-period=10s --retries=1 \ + CMD curl -f http://127.0.0.1:8000/health || exit 1 + +CMD ["bun", "dist/server.js"] diff --git a/services/message-delivery-method-web-push/package.json b/services/message-delivery-method-web-push/package.json index dceae93..95829e3 100644 --- a/services/message-delivery-method-web-push/package.json +++ b/services/message-delivery-method-web-push/package.json @@ -2,7 +2,7 @@ "name": "@hereconnect/service-message-delivery-method-web-push", "version": "1.0.0", "description": "", - "main": "src/server.ts", + "main": "dist/server.ts", "type": "module", "license": "@hereconnect/license", "private": true, @@ -15,6 +15,9 @@ "type": "git", "url": "" }, + "files": [ + "dist/**/*" + ], "bugs": "", "keywords": [], "author": { @@ -32,18 +35,18 @@ }, "dependencies": { "@hereconnect/couchdb-changes-stream": "workspace:^", - "@hereconnect/types": "^1.0.0", "pouchdb-adapter-http": "^9.0.0", "pouchdb-core": "^9.0.0", "pouchdb-find": "^9.0.0", - "typescript": "~5.6.3", "web-push": "^3.6.7" }, "devDependencies": { + "@hereconnect/types": "^1.0.0", "@types/node": "^20.17.6", "@types/pouchdb-adapter-http": "^6.1.6", "@types/pouchdb-core": "^7.0.15", "@types/pouchdb-find": "^7.3.3", - "@types/web-push": "^3.6.4" + "@types/web-push": "^3.6.4", + "typescript": "~5.6.3" } } diff --git a/services/message-delivery-method-web-push/src/dbs.ts b/services/message-delivery-method-web-push/src/dbs.ts index 81ae014..404aa39 100644 --- a/services/message-delivery-method-web-push/src/dbs.ts +++ b/services/message-delivery-method-web-push/src/dbs.ts @@ -8,8 +8,7 @@ import type { } from "@hereconnect/types"; import { PushSubscription } from "web-push"; -export interface ServiceDoc { - _id: string; +export interface ServiceSeqTrackerDocument { lastSeq: string | number; } @@ -35,14 +34,15 @@ export const webPushSubscriptionsDb = new PouchDB< skip_setup: true, }); -export const { messagesDb, serviceDb } = (() => { +export const { messagesDb, serviceSeqTrackerDb } = (() => { const messagesDB = new PouchDB(messagesDbUrl, { skip_setup: true, }); return { messagesDb: messagesDB as PouchDB.Database, - serviceDb: messagesDB as PouchDB.Database, + serviceSeqTrackerDb: + messagesDB as PouchDB.Database, } as const; })(); @@ -52,3 +52,33 @@ export const userChatSettingsDb = new PouchDB( skip_setup: true, }, ); + +export const checkDatabases = async () => { + const entries = await Promise.all( + Object.entries({ + serverSettingsDb, + qrDb, + webPushSubscriptionsDb, + messagesDb, + userChatSettingsDb, + } as const).map( + async ([name, db]): Promise< + [string, Awaited>] + > => { + const info = await db.info(); + return [name, info]; + }, + ), + ); + + for (const [name, info] of entries) { + if ("error" in info && "reason" in info) { + throw new Error(`[${name}] ${info.error}: ${info.reason}`); + } + if ("error" in info) { + throw new Error(`[${name}] ${info.error}`); + } + } + + return Object.fromEntries(entries); +}; diff --git a/services/message-delivery-method-web-push/src/health-server.ts b/services/message-delivery-method-web-push/src/health-server.ts index 7c2cb04..cbd6950 100644 --- a/services/message-delivery-method-web-push/src/health-server.ts +++ b/services/message-delivery-method-web-push/src/health-server.ts @@ -10,7 +10,7 @@ export function startHealthServer( controller: AbortController, defaultPort: number = 8000, defaultHostName: string = "127.0.0.1", -): void { +) { const port = parseInt(process.env.HEALTH_PORT || `${defaultPort}`, 10); const hostname = process.env.HEALTH_HOST || defaultHostName; @@ -43,7 +43,11 @@ export function startHealthServer( } }); - server.listen(port, hostname, () => { + const { promise, resolve, reject } = Promise.withResolvers(); + server.once("error", reject); + server.listen(port, hostname, resolve); + + promise.then(() => { console.log( `Healthcheck server running on http://${hostname}:${port}/health`, ); @@ -52,6 +56,9 @@ export function startHealthServer( // Закрываем сервер при активации AbortController controller.signal.addEventListener("abort", () => { console.log("Healthcheck server shutting down due to abort signal."); + reject(); server.close(); }); + + return promise; } diff --git a/services/message-delivery-method-web-push/src/server.ts b/services/message-delivery-method-web-push/src/server.ts index 9ba60c4..c21dcc1 100644 --- a/services/message-delivery-method-web-push/src/server.ts +++ b/services/message-delivery-method-web-push/src/server.ts @@ -1,11 +1,14 @@ import webpush from "web-push"; +const heartbeat = Number(process.env.HEARTBEAT_INTERVAL ?? 30_000); + import { CouchDBChangesStream } from "@hereconnect/couchdb-changes-stream"; import { qrDb, messagesDbUrl, webPushSubscriptionsDb, userChatSettingsDb, + checkDatabases, } from "./dbs"; import { setupVapidDetails } from "./setupVapidDetails"; import type { MessageDocument } from "@hereconnect/types"; @@ -14,8 +17,6 @@ import { startHealthServer } from "./health-server"; const abortController = new AbortController(); -startHealthServer(abortController); - function limitStringLengthWithEllipsis(str: string, maxLength: number): string { return str.length > maxLength ? str.substring(0, maxLength) + "…" : str; } @@ -33,10 +34,13 @@ const start = async () => { since: serviceSeqTracker.lastSeq, feed: "continuous", include_docs: true, - heartbeat: 1000, + heartbeat, filter: "_selector", selector: { - type: "message", + $or: [ + { type: "message" }, + { type: "service", service: "message-delivery-method-web-push" }, + ], }, }, ); @@ -50,6 +54,20 @@ const start = async () => { console.info("Service started from sequence", serviceSeqTracker.lastSeq); + // check dbs + await checkDatabases().then(({ messagesDb }) => { + if ( + parseInt(serviceSeqTracker.lastSeq.toString(), 10) === + parseInt(messagesDb.update_seq.toString(), 10) + ) { + console.log("Since is the last seq"); + } else { + console.log("Last seq is", messagesDb.update_seq); + } + }); + + await startHealthServer(abortController); + for await (const change of changesStream) { const { seq, doc: msgDoc } = change; diff --git a/services/message-delivery-method-web-push/src/serviceSeqTracker.ts b/services/message-delivery-method-web-push/src/serviceSeqTracker.ts index b956605..0e725fa 100644 --- a/services/message-delivery-method-web-push/src/serviceSeqTracker.ts +++ b/services/message-delivery-method-web-push/src/serviceSeqTracker.ts @@ -1,4 +1,4 @@ -import { serviceDb, type ServiceDoc } from "./dbs"; +import { serviceSeqTrackerDb, type ServiceSeqTrackerDocument } from "./dbs"; export class ServiceSeqTracker { /** @@ -6,7 +6,7 @@ export class ServiceSeqTracker { * @constructor */ static async Init(_id: string) { - const serviceDoc = await serviceDb.get(_id).catch((error) => { + const doc = await serviceSeqTrackerDb.get(_id).catch((error) => { if (error.name === "not_found") { return { _id, @@ -15,20 +15,20 @@ export class ServiceSeqTracker { } throw error; // Re-throw other errors }); - return new ServiceSeqTracker(serviceDoc); + return new ServiceSeqTracker(doc); } - private constructor(private serviceDoc: ServiceDoc) {} + private constructor(private doc: ServiceSeqTrackerDocument) {} get lastSeq() { - return this.serviceDoc.lastSeq; + return this.doc.lastSeq; } checkNextSeq(seq: number | string) { - if (!this.serviceDoc) { + if (!this.doc) { } const next = parseInt(seq as string, 10); - const current = parseInt(this.serviceDoc.lastSeq as string, 10); + const current = parseInt(this.doc.lastSeq as string, 10); if (next < current) { throw new Error( `Sequence number is too old: ${seq} (${next} < ${current})`, @@ -37,7 +37,7 @@ export class ServiceSeqTracker { } saveSeq(seq: number | string) { - this.serviceDoc.lastSeq = seq; - return serviceDb.put(this.serviceDoc); + this.doc.lastSeq = seq; + return serviceSeqTrackerDb.put(this.doc); } }