build service
Deploy frontend / frontend (push) Failing after 46s

This commit is contained in:
2024-12-03 11:11:44 +02:00
parent 94bd0a6a62
commit c8b164e7d7
20 changed files with 147 additions and 46 deletions
@@ -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}}
+6 -10
View File
@@ -1,22 +1,18 @@
{ {
"name": "@hereconnect/types", "name": "@hereconnect/types",
"version": "1.0.0", "version": "1.0.0",
"main": "index.ts", "main": "src/index.ts",
"types": "index.ts", "types": "src/index.ts",
"exports": { "exports": {
".": { ".": {
"import": "./index.ts", "import": "./src/index.ts",
"default": "./index.ts" "default": "./src/index.ts"
} }
}, },
"files": ["src/**/*"], "files": ["src/**/*"],
"private": true, "private": true,
"scripts": { "peerDependencies": {
}, "typescript": "~5.6.3",
"dependencies": {
"typescript": "~5.6.3"
},
"devDependencies": {
"@types/web-push": "^3.6.4" "@types/web-push": "^3.6.4"
} }
} }
+9 -13
View File
@@ -209,13 +209,12 @@ importers:
packages/types: packages/types:
dependencies: dependencies:
typescript:
specifier: ~5.6.3
version: 5.6.3
devDependencies:
'@types/web-push': '@types/web-push':
specifier: ^3.6.4 specifier: ^3.6.4
version: 3.6.4 version: 3.6.4
typescript:
specifier: ~5.6.3
version: 5.6.3
services/db-migrations: services/db-migrations:
dependencies: dependencies:
@@ -240,9 +239,6 @@ importers:
'@hereconnect/couchdb-changes-stream': '@hereconnect/couchdb-changes-stream':
specifier: workspace:^ specifier: workspace:^
version: link:../../packages/couchdb-changes-stream version: link:../../packages/couchdb-changes-stream
'@hereconnect/types':
specifier: ^1.0.0
version: link:../../packages/types
pouchdb-adapter-http: pouchdb-adapter-http:
specifier: ^9.0.0 specifier: ^9.0.0
version: 9.0.0 version: 9.0.0
@@ -252,13 +248,13 @@ importers:
pouchdb-find: pouchdb-find:
specifier: ^9.0.0 specifier: ^9.0.0
version: 9.0.0 version: 9.0.0
typescript:
specifier: ~5.6.3
version: 5.6.3
web-push: web-push:
specifier: ^3.6.7 specifier: ^3.6.7
version: 3.6.7 version: 3.6.7
devDependencies: devDependencies:
'@hereconnect/types':
specifier: ^1.0.0
version: link:../../packages/types
'@types/node': '@types/node':
specifier: ^20.17.6 specifier: ^20.17.6
version: 20.17.9 version: 20.17.9
@@ -274,6 +270,9 @@ importers:
'@types/web-push': '@types/web-push':
specifier: ^3.6.4 specifier: ^3.6.4
version: 3.6.4 version: 3.6.4
typescript:
specifier: ~5.6.3
version: 5.6.3
packages: packages:
@@ -1776,7 +1775,6 @@ packages:
resolution: {integrity: sha512-0JOXkRyLanfGPE2QRCwgxhzlBAvaRdCNMcvbd7jFfpmD4eEXll7LRwy5ymJmyeZqk7Nh7eD2LeUyQ68BbndmXw==} resolution: {integrity: sha512-0JOXkRyLanfGPE2QRCwgxhzlBAvaRdCNMcvbd7jFfpmD4eEXll7LRwy5ymJmyeZqk7Nh7eD2LeUyQ68BbndmXw==}
dependencies: dependencies:
undici-types: 6.19.8 undici-types: 6.19.8
dev: true
/@types/pouchdb-adapter-http@6.1.6: /@types/pouchdb-adapter-http@6.1.6:
resolution: {integrity: sha512-DJur1mt07GJXwGb5K+MOILoCOSgoQpsi7hybcTzRLeR3IO8Y8eq7TnhTkftAJdx9VHJGOiOXFjO+8BYM69j5yA==} resolution: {integrity: sha512-DJur1mt07GJXwGb5K+MOILoCOSgoQpsi7hybcTzRLeR3IO8Y8eq7TnhTkftAJdx9VHJGOiOXFjO+8BYM69j5yA==}
@@ -1851,7 +1849,6 @@ packages:
resolution: {integrity: sha512-GnJmSr40H3RAnj0s34FNTcJi1hmWFV5KXugE0mYWnYhgTAHLJ/dJKAwDmvPJYMke0RplY2XE9LnM4hqSqKIjhQ==} resolution: {integrity: sha512-GnJmSr40H3RAnj0s34FNTcJi1hmWFV5KXugE0mYWnYhgTAHLJ/dJKAwDmvPJYMke0RplY2XE9LnM4hqSqKIjhQ==}
dependencies: dependencies:
'@types/node': 20.17.9 '@types/node': 20.17.9
dev: true
/@types/yargs-parser@21.0.3: /@types/yargs-parser@21.0.3:
resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==}
@@ -6329,7 +6326,6 @@ packages:
/undici-types@6.19.8: /undici-types@6.19.8:
resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==}
dev: true
/unhead@1.11.13: /unhead@1.11.13:
resolution: {integrity: sha512-I7yyvqRfpPPzXuCG7HKZkgAWJDbzXDDEVyib4C/78HREqhNGHVSyo4TqX1h1xB5cx7WYc21HHDRT2/8YkqOy2w==} resolution: {integrity: sha512-I7yyvqRfpPPzXuCG7HKZkgAWJDbzXDDEVyib4C/78HREqhNGHVSyo4TqX1h1xB5cx7WYc21HHDRT2/8YkqOy2w==}
@@ -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"]
@@ -2,7 +2,7 @@
"name": "@hereconnect/service-message-delivery-method-web-push", "name": "@hereconnect/service-message-delivery-method-web-push",
"version": "1.0.0", "version": "1.0.0",
"description": "", "description": "",
"main": "src/server.ts", "main": "dist/server.ts",
"type": "module", "type": "module",
"license": "@hereconnect/license", "license": "@hereconnect/license",
"private": true, "private": true,
@@ -15,6 +15,9 @@
"type": "git", "type": "git",
"url": "" "url": ""
}, },
"files": [
"dist/**/*"
],
"bugs": "", "bugs": "",
"keywords": [], "keywords": [],
"author": { "author": {
@@ -32,18 +35,18 @@
}, },
"dependencies": { "dependencies": {
"@hereconnect/couchdb-changes-stream": "workspace:^", "@hereconnect/couchdb-changes-stream": "workspace:^",
"@hereconnect/types": "^1.0.0",
"pouchdb-adapter-http": "^9.0.0", "pouchdb-adapter-http": "^9.0.0",
"pouchdb-core": "^9.0.0", "pouchdb-core": "^9.0.0",
"pouchdb-find": "^9.0.0", "pouchdb-find": "^9.0.0",
"typescript": "~5.6.3",
"web-push": "^3.6.7" "web-push": "^3.6.7"
}, },
"devDependencies": { "devDependencies": {
"@hereconnect/types": "^1.0.0",
"@types/node": "^20.17.6", "@types/node": "^20.17.6",
"@types/pouchdb-adapter-http": "^6.1.6", "@types/pouchdb-adapter-http": "^6.1.6",
"@types/pouchdb-core": "^7.0.15", "@types/pouchdb-core": "^7.0.15",
"@types/pouchdb-find": "^7.3.3", "@types/pouchdb-find": "^7.3.3",
"@types/web-push": "^3.6.4" "@types/web-push": "^3.6.4",
"typescript": "~5.6.3"
} }
} }
@@ -8,8 +8,7 @@ import type {
} from "@hereconnect/types"; } from "@hereconnect/types";
import { PushSubscription } from "web-push"; import { PushSubscription } from "web-push";
export interface ServiceDoc { export interface ServiceSeqTrackerDocument {
_id: string;
lastSeq: string | number; lastSeq: string | number;
} }
@@ -35,14 +34,15 @@ export const webPushSubscriptionsDb = new PouchDB<
skip_setup: true, skip_setup: true,
}); });
export const { messagesDb, serviceDb } = (() => { export const { messagesDb, serviceSeqTrackerDb } = (() => {
const messagesDB = new PouchDB(messagesDbUrl, { const messagesDB = new PouchDB(messagesDbUrl, {
skip_setup: true, skip_setup: true,
}); });
return { return {
messagesDb: messagesDB as PouchDB.Database<MessageDocument>, messagesDb: messagesDB as PouchDB.Database<MessageDocument>,
serviceDb: messagesDB as PouchDB.Database<ServiceDoc>, serviceSeqTrackerDb:
messagesDB as PouchDB.Database<ServiceSeqTrackerDocument>,
} as const; } as const;
})(); })();
@@ -52,3 +52,33 @@ export const userChatSettingsDb = new PouchDB<UserChatSettingsDocument>(
skip_setup: true, 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<ReturnType<typeof db.info>>]
> => {
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);
};
@@ -10,7 +10,7 @@ export function startHealthServer(
controller: AbortController, controller: AbortController,
defaultPort: number = 8000, defaultPort: number = 8000,
defaultHostName: string = "127.0.0.1", defaultHostName: string = "127.0.0.1",
): void { ) {
const port = parseInt(process.env.HEALTH_PORT || `${defaultPort}`, 10); const port = parseInt(process.env.HEALTH_PORT || `${defaultPort}`, 10);
const hostname = process.env.HEALTH_HOST || defaultHostName; const hostname = process.env.HEALTH_HOST || defaultHostName;
@@ -43,7 +43,11 @@ export function startHealthServer(
} }
}); });
server.listen(port, hostname, () => { const { promise, resolve, reject } = Promise.withResolvers<void>();
server.once("error", reject);
server.listen(port, hostname, resolve);
promise.then(() => {
console.log( console.log(
`Healthcheck server running on http://${hostname}:${port}/health`, `Healthcheck server running on http://${hostname}:${port}/health`,
); );
@@ -52,6 +56,9 @@ export function startHealthServer(
// Закрываем сервер при активации AbortController // Закрываем сервер при активации AbortController
controller.signal.addEventListener("abort", () => { controller.signal.addEventListener("abort", () => {
console.log("Healthcheck server shutting down due to abort signal."); console.log("Healthcheck server shutting down due to abort signal.");
reject();
server.close(); server.close();
}); });
return promise;
} }
@@ -1,11 +1,14 @@
import webpush from "web-push"; import webpush from "web-push";
const heartbeat = Number(process.env.HEARTBEAT_INTERVAL ?? 30_000);
import { CouchDBChangesStream } from "@hereconnect/couchdb-changes-stream"; import { CouchDBChangesStream } from "@hereconnect/couchdb-changes-stream";
import { import {
qrDb, qrDb,
messagesDbUrl, messagesDbUrl,
webPushSubscriptionsDb, webPushSubscriptionsDb,
userChatSettingsDb, userChatSettingsDb,
checkDatabases,
} from "./dbs"; } from "./dbs";
import { setupVapidDetails } from "./setupVapidDetails"; import { setupVapidDetails } from "./setupVapidDetails";
import type { MessageDocument } from "@hereconnect/types"; import type { MessageDocument } from "@hereconnect/types";
@@ -14,8 +17,6 @@ import { startHealthServer } from "./health-server";
const abortController = new AbortController(); const abortController = new AbortController();
startHealthServer(abortController);
function limitStringLengthWithEllipsis(str: string, maxLength: number): string { function limitStringLengthWithEllipsis(str: string, maxLength: number): string {
return str.length > maxLength ? str.substring(0, maxLength) + "…" : str; return str.length > maxLength ? str.substring(0, maxLength) + "…" : str;
} }
@@ -33,10 +34,13 @@ const start = async () => {
since: serviceSeqTracker.lastSeq, since: serviceSeqTracker.lastSeq,
feed: "continuous", feed: "continuous",
include_docs: true, include_docs: true,
heartbeat: 1000, heartbeat,
filter: "_selector", filter: "_selector",
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); 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) { for await (const change of changesStream) {
const { seq, doc: msgDoc } = change; const { seq, doc: msgDoc } = change;
@@ -1,4 +1,4 @@
import { serviceDb, type ServiceDoc } from "./dbs"; import { serviceSeqTrackerDb, type ServiceSeqTrackerDocument } from "./dbs";
export class ServiceSeqTracker { export class ServiceSeqTracker {
/** /**
@@ -6,7 +6,7 @@ export class ServiceSeqTracker {
* @constructor * @constructor
*/ */
static async Init(_id: string) { 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") { if (error.name === "not_found") {
return { return {
_id, _id,
@@ -15,20 +15,20 @@ export class ServiceSeqTracker {
} }
throw error; // Re-throw other errors 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() { get lastSeq() {
return this.serviceDoc.lastSeq; return this.doc.lastSeq;
} }
checkNextSeq(seq: number | string) { checkNextSeq(seq: number | string) {
if (!this.serviceDoc) { if (!this.doc) {
} }
const next = parseInt(seq as string, 10); 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) { if (next < current) {
throw new Error( throw new Error(
`Sequence number is too old: ${seq} (${next} < ${current})`, `Sequence number is too old: ${seq} (${next} < ${current})`,
@@ -37,7 +37,7 @@ export class ServiceSeqTracker {
} }
saveSeq(seq: number | string) { saveSeq(seq: number | string) {
this.serviceDoc.lastSeq = seq; this.doc.lastSeq = seq;
return serviceDb.put(this.serviceDoc); return serviceSeqTrackerDb.put(this.doc);
} }
} }