Compare commits

...

8 Commits

10 changed files with 160 additions and 273 deletions

3
.env Normal file
View File

@@ -0,0 +1,3 @@
NEXTJS_WEBHOOK_URL=https://webmail.ayris.tech/api/webhooks/mail-signal
WEBHOOK_SECRET=besiktasK1903*

2
.env.example Normal file
View File

@@ -0,0 +1,2 @@
NEXTJS_WEBHOOK_URL=
WEBHOOK_SECRET=

View File

@@ -1,16 +1,18 @@
FROM node:20-alpine # Hafif bir Node.js imajı kullanalım
FROM node:20-slim
# Install docker cli to be able to run 'docker logs' from within the container # Docker CLI'ı içine kuruyoruz (Çünkü Worker, Dovecot konteynerine komut gönderecek)
# Note: You must mount /var/run/docker.sock when running this container RUN apt-get update && apt-get install -y docker.io && rm -rf /var/lib/apt/lists/*
RUN apk add --no-cache docker-cli
# Çalışma dizini
WORKDIR /app WORKDIR /app
# Paket listelerini kopyala ve bağımlılıkları kur
COPY package*.json ./ COPY package*.json ./
RUN npm install RUN npm install --production
# Tüm kodları ve .env dosyasını kopyala
COPY . . COPY . .
RUN npm run build # Uygulamayı başlat
CMD ["node", "worker.js"]
CMD ["npm", "start"]

View File

@@ -1,14 +1,61 @@
# AyrisTech Worker # AyrisTech Mail Worker (V10.3)
This worker listens to Mailcow Dovecot logs and publishes incoming mail events to Redis. Mailcow Dovecot loglarını ve dosya sistemini izleyerek yeni gelen mailleri Next.js webhook'una ileten servis.
## Features ## Kurulum (Türkçe)
- Real-time mail event tracking
- Redis Pub/Sub integration
- Dockerized deployment
## Setup ### 1. Dosyaları Hazırlayın
1. Clone the repository Örnek dosyayı kopyalayın ve bilgilerinizi girin:
2. Install dependencies: `npm install`
3. Configure `.env` file ```bash
4. Run the worker: `npm start` cp .env.example .env
```
### 2. Çalıştırma (Docker Compose)
En kolay ve tavsiye edilen yöntem:
```bash
docker compose up -d --build
```
### 3. Manuel Çalıştırma (Geliştirme)
```bash
npm install
npm start
```
---
# AyrisTech Mail Worker (V10.3) - English
Service that monitors Mailcow Dovecot logs and filesystem to forward new incoming emails to a Next.js webhook.
## Installation (English)
### 1. Prepare Files
Copy the example file and enter your details:
```bash
cp .env.example .env
```
### 2. Execution (Docker Compose)
The easiest and recommended method:
```bash
docker compose up -d --build
```
### 3. Manual Run (Development)
```bash
npm install
npm start
```
---
## Önemli Notlar / Important Notes
- **TR**: Servis `docker exec` komutu kullandığı için `/var/run/docker.sock` erişimine ihtiyaç duyar.
- **EN**: The service requires access to `/var/run/docker.sock` to execute `docker exec` commands.
- **TR**: Mailcow mail dosyaları `/var/lib/docker/volumes/mailcowdockerized_vmail-vol-1/_data/` yolunda olmalıdır.
- **EN**: Mailcow email files must be located at `/var/lib/docker/volumes/mailcowdockerized_vmail-vol-1/_data/`.

View File

@@ -1,35 +0,0 @@
import { Redis } from 'ioredis';
import { spawn } from 'child_process';
import dotenv from 'dotenv';
dotenv.config();
const REDIS_URL = process.env.REDIS_URL || 'redis://localhost:6379';
const CONTAINER_NAME = process.env.MAILCOW_CONTAINER_NAME || 'mailcowdockerized-dovecot-mailcow-1';
const redis = new Redis(REDIS_URL);
console.log("🚀 AyrisTech Worker Bağlanıyor...");
// Dovecot loglarını dinliyoruz
const logProcess = spawn('docker', ['logs', '-f', CONTAINER_NAME]);
logProcess.stdout.on('data', async (data) => {
const line = data.toString();
// Mailcow'un mail teslimat logunu yakala
if (line.includes('saved mail to INBOX')) {
const match = line.match(/user=<([^>]+)>/);
if (match) {
const email = match[1];
console.log(`📩 Mail Geldi: ${email}`);
// Olayı Redis'e fırlat (Pub/Sub)
// Bu sayede Next.js veya diğer servisler bu haberi alabilir
await redis.publish('NEW_MAIL_EVENT', JSON.stringify({
to: email,
time: new Date().toISOString()
}));
}
}
});

14
docker-compose.yml Normal file
View File

@@ -0,0 +1,14 @@
version: '3.8'
services:
ayristech-worker:
build: .
container_name: ayristech-worker
restart: always
volumes:
# Mail dosyalarını okumak için vmail volume'unu bağlıyoruz
- /var/lib/docker/volumes/mailcowdockerized_vmail-vol-1/_data:/var/lib/docker/volumes/mailcowdockerized_vmail-vol-1/_data:ro
# Docker içinde docker komutu çalıştırabilmek için socket'i bağlıyoruz
- /var/run/docker.sock:/var/run/docker.sock
env_file:
- .env

161
package-lock.json generated
View File

@@ -1,161 +0,0 @@
{
"name": "ayristech-worker",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "ayristech-worker",
"version": "1.0.0",
"license": "ISC",
"dependencies": {
"ioredis": "^5.10.1"
},
"devDependencies": {
"@types/node": "^25.7.0",
"typescript": "^6.0.3"
}
},
"node_modules/@ioredis/commands": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.5.1.tgz",
"integrity": "sha512-JH8ZL/ywcJyR9MmJ5BNqZllXNZQqQbnVZOqpPQqE1vHiFgAw4NHbvE0FOduNU8IX9babitBT46571OnPTT0Zcw==",
"license": "MIT"
},
"node_modules/@types/node": {
"version": "25.7.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.7.0.tgz",
"integrity": "sha512-z+pdZyxE+RTQE9AcboAZCb4otwcrvgHD+GlBpPgn0emDVt0ohrTMhAwlr2Wd9nZ+nihhYFxO2pThz3C5qSu2Eg==",
"dev": true,
"license": "MIT",
"dependencies": {
"undici-types": "~7.21.0"
}
},
"node_modules/cluster-key-slot": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz",
"integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==",
"license": "Apache-2.0",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/debug": {
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/denque": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz",
"integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==",
"license": "Apache-2.0",
"engines": {
"node": ">=0.10"
}
},
"node_modules/ioredis": {
"version": "5.10.1",
"resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.10.1.tgz",
"integrity": "sha512-HuEDBTI70aYdx1v6U97SbNx9F1+svQKBDo30o0b9fw055LMepzpOOd0Ccg9Q6tbqmBSJaMuY0fB7yw9/vjBYCA==",
"license": "MIT",
"dependencies": {
"@ioredis/commands": "1.5.1",
"cluster-key-slot": "^1.1.0",
"debug": "^4.3.4",
"denque": "^2.1.0",
"lodash.defaults": "^4.2.0",
"lodash.isarguments": "^3.1.0",
"redis-errors": "^1.2.0",
"redis-parser": "^3.0.0",
"standard-as-callback": "^2.1.0"
},
"engines": {
"node": ">=12.22.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/ioredis"
}
},
"node_modules/lodash.defaults": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
"integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==",
"license": "MIT"
},
"node_modules/lodash.isarguments": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz",
"integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==",
"license": "MIT"
},
"node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"license": "MIT"
},
"node_modules/redis-errors": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz",
"integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==",
"license": "MIT",
"engines": {
"node": ">=4"
}
},
"node_modules/redis-parser": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz",
"integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==",
"license": "MIT",
"dependencies": {
"redis-errors": "^1.0.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/standard-as-callback": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz",
"integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==",
"license": "MIT"
},
"node_modules/typescript": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz",
"integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==",
"dev": true,
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=14.17"
}
},
"node_modules/undici-types": {
"version": "7.21.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.21.0.tgz",
"integrity": "sha512-w9IMgQrz4O0YN1LtB7K5P63vhlIOvC7opSmouCJ+ZywlPAlO9gIkJ+otk6LvGpAs2wg4econaCz3TvQ9xPoyuQ==",
"dev": true,
"license": "MIT"
}
}
}

View File

@@ -1,22 +1,16 @@
{ {
"name": "ayristech-worker", "name": "ayristech-worker",
"version": "1.0.0", "version": "1.0.0",
"description": "", "description": "AyrisTech Mailcow Worker",
"main": "dist/apps/worker/main.js", "main": "worker.js",
"scripts": { "scripts": {
"build": "tsc", "start": "node worker.js",
"start": "node dist/apps/worker/main.js", "dev": "node --watch worker.js"
"dev": "tsx apps/worker/main.ts",
"test": "echo \"Error: no test specified\" && exit 1"
}, },
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": { "dependencies": {
"axios": "^1.16.1",
"chokidar": "^5.0.0",
"dotenv": "^17.4.2",
"ioredis": "^5.10.1" "ioredis": "^5.10.1"
},
"devDependencies": {
"@types/node": "^25.7.0",
"typescript": "^6.0.3"
} }
} }

View File

@@ -1,44 +0,0 @@
{
// Visit https://aka.ms/tsconfig to read more about this file
"compilerOptions": {
// File Layout
"rootDir": "./",
"outDir": "./dist",
// Environment Settings
// See also https://aka.ms/tsconfig/module
"module": "nodenext",
"target": "esnext",
"types": [],
// For nodejs:
// "lib": ["esnext"],
// "types": ["node"],
// and npm install -D @types/node
// Other Outputs
"sourceMap": true,
"declaration": true,
"declarationMap": true,
// Stricter Typechecking Options
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true,
// Style Options
// "noImplicitReturns": true,
// "noImplicitOverride": true,
// "noUnusedLocals": true,
// "noUnusedParameters": true,
// "noFallthroughCasesInSwitch": true,
// "noPropertyAccessFromIndexSignature": true,
// Recommended Options
"strict": true,
"jsx": "react-jsx",
"verbatimModuleSyntax": true,
"isolatedModules": true,
"noUncheckedSideEffectImports": true,
"moduleDetection": "force",
"skipLibCheck": true,
}
}

65
worker.js Normal file
View File

@@ -0,0 +1,65 @@
require('dotenv').config();
const chokidar = require('chokidar');
const axios = require('axios');
const { exec } = require('child_process');
const NEXTJS_WEBHOOK_URL = process.env.NEXTJS_WEBHOOK_URL;
const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET;
const vmailPath = '/var/lib/docker/volumes/mailcowdockerized_vmail-vol-1/_data/';
console.log("🚀 AyrisTech Güvenli Dekoder (V10.3) Başlatıldı...");
const watcher = chokidar.watch(vmailPath, {
ignored: /(^|[\/\\])\../,
persistent: true,
ignoreInitial: true,
depth: 5
});
watcher.on('add', (filePath) => {
if (filePath.includes('/new/')) {
const parts = filePath.split('/');
const fullEmail = `${parts[8]}@${parts[7]}`;
console.log(`📩 Yeni şifreli mail: ${fullEmail}. Çözülüyor...`);
// Komutu daha garantici bir hale getirdik
const cmd = `docker exec mailcowdockerized-dovecot-mailcow-1 doveadm fetch -u "${fullEmail}" "hdr.from hdr.subject body.snippet" ALL`;
setTimeout(() => {
exec(cmd, async (error, stdout, stderr) => {
if (error || stderr) {
console.error("❌ Doveadm Hatası:", error ? error.message : stderr);
return;
}
if (!stdout.trim()) {
console.log("⚠️ Mail içeriği henüz hazır değil (Stdout boş).");
return;
}
let mailData = { to: fullEmail, from: 'Bilinmiyor', subject: 'Konu Yok', snippet: '' };
// Satır satır parse etme
const lines = stdout.split('\n');
lines.forEach(line => {
const l = line.trim();
if (l.toLowerCase().startsWith('hdr.from:')) mailData.from = l.split(':').slice(1).join(':').trim();
if (l.toLowerCase().startsWith('hdr.subject:')) mailData.subject = l.split(':').slice(1).join(':').trim();
if (l.toLowerCase().startsWith('body.snippet:')) mailData.snippet = l.split(':').slice(1).join(':').trim();
});
console.log(`✨ İçerik Çözüldü: ${mailData.subject}`);
try {
await axios.post(NEXTJS_WEBHOOK_URL, mailData, {
headers: { 'x-ayristech-secret': WEBHOOK_SECRET }
});
console.log(`✅ Webhook Next.js'e başarıyla ulaştı.`);
} catch (err) {
console.error("❌ Webhook Hatası:", err.message);
}
});
}, 2500); // Mailin DB'ye tam işlenmesi için süreyi biraz artırdık
}
});