create qr-codes by tg bot
This commit is contained in:
@@ -5,4 +5,4 @@ COPY . ./
|
||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=10s --retries=1 \
|
||||
CMD wget --no-verbose --tries=1 --spider http://127.0.0.1:8000/health || exit 1
|
||||
|
||||
CMD ["bun", "dist/server.js"]
|
||||
CMD ["bun", "dist/service.js"]
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "@hereconnect/service-message-delivery-method-web-push",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "dist/server.ts",
|
||||
"main": "dist/service.ts",
|
||||
"type": "module",
|
||||
"license": "@hereconnect/license",
|
||||
"private": true,
|
||||
@@ -27,7 +27,7 @@
|
||||
},
|
||||
"contributors": [],
|
||||
"scripts": {
|
||||
"start": "node dist/server.js",
|
||||
"start": "node dist/service.js",
|
||||
"build": "tsc",
|
||||
"lint": "tsc --noEmit",
|
||||
"generate-keys": "node src/generate-keys.ts",
|
||||
@@ -36,6 +36,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@hereconnect/couchdb-changes-stream": "workspace:^",
|
||||
"@hereconnect/lib-service-health-checkserver": "workspace:^",
|
||||
"@hereconnect/lib-service-seq-tracker": "workspace:^",
|
||||
"pouchdb-adapter-http": "^9.0.0",
|
||||
"pouchdb-core": "^9.0.0",
|
||||
"pouchdb-find": "^9.0.0",
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
import http from "http";
|
||||
|
||||
/**
|
||||
* Запускает HTTP-сервер для проверки здоровья.
|
||||
* @param controller - AbortController для управления состоянием здоровья.
|
||||
* @param defaultPort - Порт для сервера по умолчанию (если не указан в ENV HEALTH_PORT).
|
||||
* @param defaultHostName - Имя хоста для сервера по умолчанию (если не указан в ENV HEALTH_HOST).
|
||||
*/
|
||||
export function startHealthServer(
|
||||
controller: AbortController,
|
||||
defaultPort: number = 8000,
|
||||
defaultHostName: string = "127.0.0.1",
|
||||
) {
|
||||
const port = parseInt(process.env.HEALTH_PORT || `${defaultPort}`, 10);
|
||||
const hostname = process.env.HEALTH_HOST || defaultHostName;
|
||||
|
||||
const server = http.createServer((req, res) => {
|
||||
if (req.url === "/health") {
|
||||
const signal = controller.signal;
|
||||
|
||||
// Если сигнал отмены активирован, сервер помечается как "unhealthy"
|
||||
if (signal.aborted) {
|
||||
res.writeHead(500, { "Content-Type": "application/json" });
|
||||
res.end(
|
||||
JSON.stringify({
|
||||
status: "unhealthy",
|
||||
reason: "Service aborted",
|
||||
}),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Возвращаем стандартный статус "ok", если всё нормально
|
||||
res.writeHead(200, { "Content-Type": "application/json" });
|
||||
res.end(
|
||||
JSON.stringify({
|
||||
status: "ok",
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
res.writeHead(404, { "Content-Type": "text/plain" });
|
||||
res.end("Not Found");
|
||||
}
|
||||
});
|
||||
|
||||
const { promise, resolve, reject } = Promise.withResolvers<void>();
|
||||
server.once("error", reject);
|
||||
server.listen(port, hostname, resolve);
|
||||
|
||||
promise.then(() => {
|
||||
console.log(
|
||||
`Healthcheck server running on http://${hostname}:${port}/health`,
|
||||
);
|
||||
});
|
||||
|
||||
// Закрываем сервер при активации AbortController
|
||||
controller.signal.addEventListener("abort", () => {
|
||||
console.log("Healthcheck server shutting down due to abort signal.");
|
||||
reject();
|
||||
server.close();
|
||||
});
|
||||
|
||||
return promise;
|
||||
}
|
||||
+5
-2
@@ -10,11 +10,12 @@ import {
|
||||
webPushSubscriptionsDb,
|
||||
userChatSettingsDb,
|
||||
checkDatabases,
|
||||
serviceSeqTrackerDb,
|
||||
} from "./dbs";
|
||||
import { setupVapidDetails } from "./setupVapidDetails";
|
||||
import type { MessageDocument } from "@hereconnect/types";
|
||||
import { ServiceSeqTracker } from "./serviceSeqTracker";
|
||||
import { startHealthServer } from "./health-server";
|
||||
import { ServiceSeqTracker } from "@hereconnect/lib-service-seq-tracker";
|
||||
import { startHealthServer } from "@hereconnect/lib-service-health-checkserver";
|
||||
|
||||
const abortController = new AbortController();
|
||||
|
||||
@@ -33,6 +34,7 @@ const start = async () => {
|
||||
|
||||
const serviceSeqTracker = await ServiceSeqTracker.Init(
|
||||
"_local/service:message-delivery-method-web-push",
|
||||
serviceSeqTrackerDb,
|
||||
);
|
||||
|
||||
const changesStream = new CouchDBChangesStream<MessageDocument>(
|
||||
@@ -56,6 +58,7 @@ const start = async () => {
|
||||
// Gracefully handle SIGINT
|
||||
process.on("SIGINT", async () => {
|
||||
changesStream?.stop();
|
||||
setTimeout(() => process.exit(1), 5_000).unref();
|
||||
});
|
||||
|
||||
console.info("Service started from sequence", serviceSeqTracker.lastSeq);
|
||||
@@ -1,43 +0,0 @@
|
||||
import { serviceSeqTrackerDb, type ServiceSeqTrackerDocument } from "./dbs";
|
||||
|
||||
export class ServiceSeqTracker {
|
||||
/**
|
||||
* @param _id
|
||||
* @constructor
|
||||
*/
|
||||
static async Init(_id: string) {
|
||||
const doc = await serviceSeqTrackerDb.get(_id).catch((error) => {
|
||||
if (error.name === "not_found") {
|
||||
return {
|
||||
_id,
|
||||
lastSeq: "0",
|
||||
};
|
||||
}
|
||||
throw error; // Re-throw other errors
|
||||
});
|
||||
return new ServiceSeqTracker(doc);
|
||||
}
|
||||
|
||||
private constructor(private doc: ServiceSeqTrackerDocument) {}
|
||||
|
||||
get lastSeq() {
|
||||
return this.doc.lastSeq;
|
||||
}
|
||||
|
||||
checkNextSeq(seq: number | string) {
|
||||
if (!this.doc) {
|
||||
}
|
||||
const next = parseInt(seq 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})`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
saveSeq(seq: number | string) {
|
||||
this.doc.lastSeq = seq;
|
||||
return serviceSeqTrackerDb.put(this.doc);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"addExtension": "js"
|
||||
}
|
||||
@@ -1,14 +1,20 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
"strict": true,
|
||||
"moduleResolution": "node",
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"types": ["node"]
|
||||
"target": "ESNext", // Указываем современный стандарт
|
||||
"module": "ESNext", // Используем ES модули
|
||||
"moduleResolution": "node", // Разрешение модулей как в Node.js
|
||||
"strict": true, // Включаем строгий режим
|
||||
"outDir": "./dist", // Папка для вывода скомпилированных файлов
|
||||
"rootDir": "./src", // Корневая папка исходников
|
||||
"esModuleInterop": true, // Совместимость с CommonJS модулями
|
||||
"skipLibCheck": true, // Пропуск проверки типов в библиотечных файлах
|
||||
"declaration": true, // Генерация `.d.ts` файлов
|
||||
"sourceMap": true, // Генерация `.map` файлов
|
||||
"baseUrl": "./", // Базовая директория для путей
|
||||
"paths": {
|
||||
"*": ["src/*"] // Настройка алиасов (сопоставление путей)
|
||||
}
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
"include": ["src"], // Указываем папку с исходниками
|
||||
"exclude": ["node_modules", "dist"] // Исключаем ненужные директории
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user