diff --git a/Dockerfile b/Dockerfile index 7b45307..51f43ef 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,36 +1,47 @@ +FROM node:22-alpine AS builder + +WORKDIR /app + +# Install build dependencies for native modules (sqlite3, better-sqlite3) +RUN apk add --no-cache python3 make g++ + +COPY package*.json ./ +RUN npm install && npm cache clean --force + +# --- Runtime image --- FROM node:22-alpine -# Устанавливаем необходимые пакеты -RUN apk update && \ - apk add --no-cache \ +# Install runtime dependencies +# WireGuard needs community repo on some alpine versions +RUN apk add --no-cache --repository https://dl-cdn.alpinelinux.org/alpine/edge/community \ wireguard-tools \ + && apk add --no-cache \ iptables \ iproute2 \ openresolv \ bash \ - curl && \ - rm -rf /var/cache/apk/* + curl \ + && rm -rf /var/cache/apk/* -# Создаём непривилегированного пользователя -RUN addgroup -S appgroup && \ - adduser -S appuser -G appgroup - -# Рабочая директория WORKDIR /app -# Копируем зависимости и устанавливаем их +# Copy node_modules from builder (with native addons pre-compiled) +COPY --from=builder /app/node_modules ./node_modules COPY package*.json ./ -RUN npm install -# Копируем исходный код с правильным владельцем -COPY --chown=appuser:appgroup ./src ./src +# Copy application source +COPY ./src ./src -# Копируем скрипт запуска -COPY --chown=appuser:appgroup ./wg/start.sh /app/start.sh +# Copy startup script +COPY ./wg/start.sh /app/start.sh RUN chmod +x /app/start.sh -# Переключаемся на непривилегированного пользователя -USER appuser +# Create db directory +RUN mkdir -p /app/db -# Команда для запуска -CMD ["/bin/bash", "/app/start.sh"] +# Health check: bot responds to /health on port 3000 +HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \ + CMD curl -sf http://localhost:3000/health || exit 1 + +# start.sh runs as root (needed for wg-quick), then drops to node +CMD ["/bin/bash", "/app/start.sh"] \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 574e2df..2776998 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "archiver": "^7.0.1", "axios": "^1.7.7", + "better-sqlite3": "^11.10.0", "bip39": "^3.1.0", "bitcoinjs-lib": "^6.1.6", "csv-writer": "^1.6.0", @@ -750,6 +751,17 @@ "resolved": "https://registry.npmjs.org/bech32/-/bech32-2.0.0.tgz", "integrity": "sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg==" }, + "node_modules/better-sqlite3": { + "version": "11.10.0", + "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-11.10.0.tgz", + "integrity": "sha512-EwhOpyXiOEL/lKzHz9AW1msWFNzGc/z+LzeB3/jnFJpxu+th2yqvzsSWas1v9jgs9+xiXJcD5A8CJxAG2TaghQ==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "bindings": "^1.5.0", + "prebuild-install": "^7.1.1" + } + }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", diff --git a/package.json b/package.json index 801542c..07038b0 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "dependencies": { "archiver": "^7.0.1", "axios": "^1.7.7", + "better-sqlite3": "^11.10.0", "bip39": "^3.1.0", "bitcoinjs-lib": "^6.1.6", "csv-writer": "^1.6.0", diff --git a/src/handlers/adminHandlers/product/productValidator.js b/src/handlers/adminHandlers/product/productValidator.js index fdb2930..98be552 100644 --- a/src/handlers/adminHandlers/product/productValidator.js +++ b/src/handlers/adminHandlers/product/productValidator.js @@ -1,7 +1,23 @@ import Validators from '../../../utils/validators.js'; +import logger from '../../../utils/logger.js'; + +export function validateProductName(name, chatId) { + if (!Validators.isValidString(name, 255)) { + logger.warn({ chatId, name }, 'Invalid product name'); + return false; + } + return true; +} + +export function validateProductPrice(price, chatId) { + if (!Validators.isValidPrice(price)) { + logger.warn({ chatId, price }, 'Invalid product price'); + return false; + } + return true; +} export default class ProductValidator { - static validateProduct(product) { if (!Validators.isValidString(product.name, 255)) { return `Ошибка: недопустимое название товара "${product.name}"`; @@ -14,4 +30,4 @@ export default class ProductValidator { } return null; } -} +} \ No newline at end of file diff --git a/src/index.js b/src/index.js index f3935c1..597d762 100644 --- a/src/index.js +++ b/src/index.js @@ -70,3 +70,18 @@ process.on('unhandledRejection', (error) => { }); logger.info('Bot is running...'); + +// Health check endpoint for Docker +import http from 'http'; +const healthServer = http.createServer((req, res) => { + if (req.url === '/health') { + res.writeHead(200, { 'Content-Type': 'application/json' }); + res.end(JSON.stringify({ status: 'ok', uptime: process.uptime() })); + } else { + res.writeHead(404); + res.end('Not found'); + } +}); +healthServer.listen(3000, () => { + logger.info({ port: 3000 }, 'Health check server started'); +});