ci
Deploy db-migrations / db-migrations (push) Failing after 44s
Deploy service message-delivery-method-web-push / service message-delivery-method-web-push (push) Successful in 47s

This commit is contained in:
2024-12-03 13:56:24 +02:00
parent 920d7f089f
commit 2f5c5922e0
7 changed files with 73 additions and 290 deletions
@@ -5,8 +5,8 @@ on:
branches: branches:
- main - main
paths: paths:
- 'services/db-migrate/**' - 'services/db-migrations/**'
- '.gitea/workflows/deploy-db-migrate.yaml' - '.gitea/workflows/deploy-service-db-migrations.yaml'
jobs: jobs:
db-migrations: db-migrations:
@@ -1,24 +1,11 @@
import type { BaseDocument } from "./BaseDocument"; import type { BaseDocument } from "./BaseDocument";
type MessageDeliveryMethodWebPushVapid = { export interface MessageDeliveryMethodWebPushVapid extends BaseDocument {
name: "message_delivery_method_web_push_vapid"; _id: "server_settings:message_delivery_method_web_push_vapid_keys"; // Формат: "server_settings:<name>"
type: "server_settings";
name: "message_delivery_method_web_push_vapid_keys";
value: { value: {
subject: string;
keys: {
publicKey: string; publicKey: string;
privateKey: string; privateKey: string;
}; };
}; }
};
type MessageDeliveryMethodWebPushGcmApiKey = {
name: "message_delivery_method_web_push_gcm_api_key";
value: string;
};
export type ServerSettingsDocument = (
| MessageDeliveryMethodWebPushVapid
| MessageDeliveryMethodWebPushGcmApiKey
) & {
_id: string; // Формат: "server_settings:<name>"
type: "server_settings";
} & BaseDocument;
@@ -1,10 +1,11 @@
import PouchDB from "./PouchDB"; import PouchDB from "./PouchDB";
import type { import {
MessageDocument, MessageDocument,
QRCodeDocument, QRCodeDocument,
ServerSettingsDocument, MessageDeliveryMethodWebPushVapid,
UserChatSettingsDocument, UserChatSettingsDocument,
WebPushSubscriptionDocument, WebPushSubscriptionDocument,
PublicSettingsDocument,
} from "@hereconnect/types"; } from "@hereconnect/types";
import { PushSubscription } from "web-push"; import { PushSubscription } from "web-push";
@@ -18,12 +19,20 @@ if (!process.env.API_URL) {
export const messagesDbUrl = `${process.env.API_URL}/messages`; export const messagesDbUrl = `${process.env.API_URL}/messages`;
export const serverSettingsDb = new PouchDB<ServerSettingsDocument>( export const serverSettingsDb = new PouchDB<MessageDeliveryMethodWebPushVapid>(
`${process.env.API_URL}/server_settings`, `${process.env.API_URL}/server_settings`,
{ {
skip_setup: true, skip_setup: true,
}, },
); );
export const publicSettingsDb = new PouchDB<PublicSettingsDocument>(
`${process.env.API_URL}/public_settings`,
{
skip_setup: true,
},
);
export const qrDb = new PouchDB<QRCodeDocument>(`${process.env.API_URL}/qr`, { export const qrDb = new PouchDB<QRCodeDocument>(`${process.env.API_URL}/qr`, {
skip_setup: true, skip_setup: true,
}); });
@@ -57,6 +66,7 @@ export const checkDatabases = async () => {
const entries = await Promise.all( const entries = await Promise.all(
Object.entries({ Object.entries({
serverSettingsDb, serverSettingsDb,
publicSettingsDb,
qrDb, qrDb,
webPushSubscriptionsDb, webPushSubscriptionsDb,
messagesDb, messagesDb,
@@ -1,45 +0,0 @@
import webpush from "web-push";
import PouchDB from "./PouchDB";
import { ServerSettingsDocument } from "@hereconnect/types";
if (!process.env.VAPID_SUBJECT) {
throw new Error("VAPID_SUBJECT env is empty!");
}
const vapidKeys = webpush.generateVAPIDKeys();
await new PouchDB<ServerSettingsDocument>(
`${process.env.API_URL}/server_settings`,
{ skip_setup: true },
).put({
_id: "server_settings:message_delivery_method_web_push_vapid",
type: "server_settings",
name: "message_delivery_method_web_push_vapid",
value: {
subject: process.env.VAPID_SUBJECT,
keys: vapidKeys,
},
created_at: new Date().toISOString(),
});
await new PouchDB(`${process.env.API_URL}/public_settings`, {
skip_setup: true,
}).put({
_id: "public_settings:message_delivery_method_web_push_vapid_public_key",
type: "public_settings",
name: "message_delivery_method_web_push_vapid_public_key",
value: vapidKeys.publicKey,
created_at: new Date().toISOString(),
});
if (process.env.GCM_API_KEY) {
await new PouchDB(`${process.env.API_URL}/public_settings`, {
skip_setup: true,
}).put({
_id: "server_settings:message_delivery_method_web_push_gcm_api_key",
type: "server_settings",
name: "message_delivery_method_web_push_gcm_api_key",
value: process.env.GCM_API_KEY,
created_at: new Date().toISOString(),
});
}
@@ -1,78 +1,71 @@
import webpush from "web-push"; import webpush from "web-push";
import { serverSettingsDb } from "./dbs"; import { publicSettingsDb, serverSettingsDb } from "./dbs";
import { ServerSettingsDocument } from "@hereconnect/types"; import PouchDB from "./PouchDB";
function isType<N extends ServerSettingsDocument["name"]>(
doc: ServerSettingsDocument | null | undefined,
name: N,
): doc is ServerSettingsDocument & { name: N } {
return !!(doc && doc.name === name);
}
export const setupVapidDetails = async () => { export const setupVapidDetails = async () => {
const subject = process.env.VAPID_SUBJECT;
if (!subject) {
throw new Error("VAPID_SUBJECT env is empty!");
}
const changes = serverSettingsDb.changes({ const changes = serverSettingsDb.changes({
since: "now", since: "now",
live: true, live: true,
include_docs: true, include_docs: true,
doc_ids: [ doc_ids: ["server_settings:message_delivery_method_web_push_vapid_keys"],
"server_settings:message_delivery_method_web_push_vapid",
"public_settings:message_delivery_method_web_push_gcm_api_key",
],
}); });
const { const vapidDetailsKeysDoc = await serverSettingsDb
rows: [vapidDetailsRow, gcmApiRow], .get("server_settings:message_delivery_method_web_push_vapid_keys")
} = await serverSettingsDb.allDocs({ .catch(async (error) => {
keys: [ if (error.name !== "not_found") {
"server_settings:message_delivery_method_web_push_vapid",
"public_settings:message_delivery_method_web_push_gcm_api_key",
],
include_docs: true,
});
if (
!("doc" in vapidDetailsRow) ||
!isType(vapidDetailsRow.doc, "message_delivery_method_web_push_vapid")
) {
throw new Error( throw new Error(
`VAPID details ${"error" in vapidDetailsRow ? vapidDetailsRow.error : "not exists"}`, `server_settings:message_delivery_method_web_push_vapid_keys not found: ${error}`,
); );
} }
if ( const vapidKeys = webpush.generateVAPIDKeys();
"doc" in gcmApiRow && const doc = {
isType(gcmApiRow.doc, "message_delivery_method_web_push_gcm_api_key") _id: "server_settings:message_delivery_method_web_push_vapid_keys",
) { type: "server_settings",
webpush.setGCMAPIKey(gcmApiRow.doc.value); name: "message_delivery_method_web_push_vapid_keys",
} value: vapidKeys,
created_at: new Date().toISOString(),
} as const;
await Promise.all([
serverSettingsDb.put(doc),
publicSettingsDb.put({
_id: "public_settings:message_delivery_method_web_push_vapid_public_key",
type: "public_settings",
name: "message_delivery_method_web_push_vapid_public_key",
value: vapidKeys.publicKey,
created_at: new Date().toISOString(),
}),
]);
console.log("created new vapid keys");
return doc;
});
webpush.setVapidDetails( webpush.setVapidDetails(
vapidDetailsRow.doc.value.subject, subject,
vapidDetailsRow.doc.value.keys.publicKey, vapidDetailsKeysDoc.value.publicKey,
vapidDetailsRow.doc.value.keys.privateKey, vapidDetailsKeysDoc.value.privateKey,
); );
if (process.env.GCM_API_KEY) {
webpush.setGCMAPIKey(process.env.GCM_API_KEY);
}
changes.on("change", (change) => { changes.on("change", (change) => {
if (!change.doc) { const doc = change.doc;
if (!doc) {
return; return;
} }
if (isType(change.doc, "message_delivery_method_web_push_vapid")) {
console.log("update vapid details"); console.log("update vapid details");
webpush.setVapidDetails( webpush.setVapidDetails(subject, doc.value.publicKey, doc.value.privateKey);
change.doc.value.subject,
change.doc.value.keys.publicKey,
change.doc.value.keys.privateKey,
);
return; return;
}
if (
"doc" in change &&
isType(change.doc, "message_delivery_method_web_push_gcm_api_key")
) {
console.log("update gcm api key");
webpush.setGCMAPIKey(change.doc.value);
}
}); });
}; };
@@ -1,65 +0,0 @@
import webpush from "web-push";
import PouchDB from "./PouchDB.js";
if (!process.env.API_URL) {
throw new Error(`env API_URL is not set`);
}
const settings = await new PouchDB(`${process.env.API_URL}/server_settings`, {
skip_setup: true,
}).get("server_settings:message_delivery_method_web_push:vapid");
webpush.setVapidDetails(
settings.vapid.subject,
settings.vapid.keys.publicKey,
settings.vapid.keys.privateKey,
);
const notification = {
type: "cancelNotification",
notification: {
title: "test",
options: {
icon: "/images/qr-code-logo-200x200.jpg",
body: "Test push message from DevTools.",
tag: "2105ef6f-3ca6-4f99-b258-0ca23f014309",
// requireInteraction: true,
// silent: false,
// vibrate: [200, 100, 200, 100, 200, 100, 200],
data: {
url: "/chat/6ilhxu/13trvu",
payload: { tag: "2105ef6f-3ca6-4f99-b258-0ca23f014309" },
},
},
},
};
const { rows } = await new PouchDB(
`${process.env.API_URL}/web_push_subscriptions`,
{ skip_setup: true },
).allDocs(
process.env.WEB_PUSH_SUBSCRIPTION_ID
? {
key: process.env.WEB_PUSH_SUBSCRIPTION_ID,
include_docs: true,
}
: {
startkey: "web_push_subscription:",
endkey: "web_push_subscription:\uFFFF",
include_docs: true,
},
);
await Promise.allSettled(
rows.map(async ({ doc }) => {
await webpush.sendNotification(
doc.subscription,
JSON.stringify(notification),
);
console.log("sent ", doc._id);
}),
);
// issues:
// 1. NO SOUND
// 2. SAFARI NO OPEN PAGE
@@ -1,97 +0,0 @@
import webpush from "web-push";
import PouchDB from "./PouchDB.js";
if (!process.env.API_URL) {
throw new Error(`env API_URL is not set`);
}
const {
rows: [
{
doc: { value: vapidDetails },
},
{ doc: gcmApiSettingsDoc },
],
} = await new PouchDB(`${process.env.API_URL}/server_settings`, {
skip_setup: true,
}).allDocs({
keys: [
"server_settings:message_delivery_method_web_push_vapid",
"public_settings:message_delivery_method_web_push_gcm_api_key",
],
include_docs: true,
});
webpush.setVapidDetails(
vapidDetails.subject,
vapidDetails.keys.publicKey,
vapidDetails.keys.privateKey,
);
if (gcmApiSettingsDoc) {
console.log(gcmApiSettingsDoc.value);
webpush.setGCMAPIKey(gcmApiSettingsDoc.value);
}
// optionally provide an identifier that the push service uses to coalesce notifications
const topic = "chat-6ilhxu-13trvu";
const notification = {
type: "notification",
notification: {
title: "test",
options: {
icon: "/images/qr-code-logo-200x200.jpg",
body: "Test push message from server.",
tag: "01936062-d10f-7f2f-8ae3-dd7f9fbc5c7f",
requireInteraction: true,
// silent: false,
// vibrate: [200, 100, 200, 100, 200, 100, 200],
// actions: [
// {
// action: "open_url",
// title: "Открыть",
// },
// ],
data: {
url: "/chat/6ilhxu/13trvu",
payload: { tag: "01936062-d10f-7f2f-8ae3-dd7f9fbc5c7f" },
},
},
},
};
const { rows } = await new PouchDB(
`${process.env.API_URL}/web_push_subscriptions`,
{ skip_setup: true },
).allDocs(
process.env.WEB_PUSH_SUBSCRIPTION_ID
? {
key: process.env.WEB_PUSH_SUBSCRIPTION_ID,
include_docs: true,
}
: {
startkey: "web_push_subscription:",
endkey: "web_push_subscription:\uFFFF",
include_docs: true,
},
);
const result = await Promise.allSettled(
rows.map(async ({ doc }) => {
await webpush.sendNotification(
doc.subscription,
JSON.stringify(notification),
{
urgency: "high",
topic,
},
);
console.log("sent ", doc._id);
}),
);
console.log(result);
// issues:
// 1. NO SOUND
// 2. SAFARI NO OPEN PAGE