update
This commit is contained in:
parent
f91e50fe6a
commit
21f2636c3e
20
.env-example
20
.env-example
@ -1,20 +0,0 @@
|
|||||||
PORT=3000
|
|
||||||
WEB_PORT=3700
|
|
||||||
IP=0.0.0.0
|
|
||||||
ROOT_PATH=/root/newmail/api
|
|
||||||
NODE_ENV=production
|
|
||||||
DB_HOST=localhost
|
|
||||||
DB_USER=postfix
|
|
||||||
DB_PASS=CHANGEME
|
|
||||||
DB_NAME=CHANGEME
|
|
||||||
JWT_SECRET=CHANGEME
|
|
||||||
SMTP_HOST=postfix
|
|
||||||
SMTP_PORT=587
|
|
||||||
SMTP_SECURE=false
|
|
||||||
SMTP_USER=admin@localhost
|
|
||||||
SMTP_PASS=CHANGEME
|
|
||||||
CF_API_TOKEN=CHANGEME
|
|
||||||
CF_EMAIL=CHANGEME
|
|
||||||
SERVER_IP=CHANGEME
|
|
||||||
MAILSERVER_CONTAINER=mailserver_postfix
|
|
||||||
CONFIG_PATH=/tmp/docker-mailserver/config/
|
|
||||||
@ -1,8 +1,5 @@
|
|||||||
# Roundcube Webmail
|
# Roundcube Webmail
|
||||||
server {
|
server {
|
||||||
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
|
|
||||||
|
|
||||||
listen 80;
|
|
||||||
server_name webmail.2weekmail.fyi;
|
server_name webmail.2weekmail.fyi;
|
||||||
|
|
||||||
# Security headers
|
# Security headers
|
||||||
@ -13,7 +10,6 @@ server {
|
|||||||
|
|
||||||
# Proxy settings
|
# Proxy settings
|
||||||
location / {
|
location / {
|
||||||
limit_req zone=api_limit burst=20 nodelay;
|
|
||||||
proxy_pass http://localhost:8081; # Roundcube container port
|
proxy_pass http://localhost:8081; # Roundcube container port
|
||||||
proxy_set_header Host $host;
|
proxy_set_header Host $host;
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
@ -30,13 +26,17 @@ server {
|
|||||||
proxy_send_timeout 60s;
|
proxy_send_timeout 60s;
|
||||||
proxy_read_timeout 60s;
|
proxy_read_timeout 60s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
listen 443 ssl; # managed by Certbot
|
||||||
|
ssl_certificate /etc/letsencrypt/live/2weekmail.fyi/fullchain.pem; # managed by Certbot
|
||||||
|
ssl_certificate_key /etc/letsencrypt/live/2weekmail.fyi/privkey.pem; # managed by Certbot
|
||||||
|
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
|
||||||
|
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# PostfixAdmin
|
# PostfixAdmin
|
||||||
server {
|
server {
|
||||||
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
|
|
||||||
|
|
||||||
listen 80;
|
|
||||||
server_name admin.2weekmail.fyi;
|
server_name admin.2weekmail.fyi;
|
||||||
|
|
||||||
# Security headers
|
# Security headers
|
||||||
@ -47,7 +47,6 @@ server {
|
|||||||
|
|
||||||
# Proxy settings
|
# Proxy settings
|
||||||
location / {
|
location / {
|
||||||
limit_req zone=api_limit burst=20 nodelay;
|
|
||||||
proxy_pass http://localhost:8080; # PostfixAdmin container port
|
proxy_pass http://localhost:8080; # PostfixAdmin container port
|
||||||
proxy_set_header Host $host;
|
proxy_set_header Host $host;
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
@ -59,13 +58,17 @@ server {
|
|||||||
proxy_send_timeout 60s;
|
proxy_send_timeout 60s;
|
||||||
proxy_read_timeout 60s;
|
proxy_read_timeout 60s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
listen 443 ssl; # managed by Certbot
|
||||||
|
ssl_certificate /etc/letsencrypt/live/2weekmail.fyi/fullchain.pem; # managed by Certbot
|
||||||
|
ssl_certificate_key /etc/letsencrypt/live/2weekmail.fyi/privkey.pem; # managed by Certbot
|
||||||
|
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
|
||||||
|
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# API Service
|
# API Service
|
||||||
server {
|
server {
|
||||||
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
|
|
||||||
|
|
||||||
listen 80;
|
|
||||||
server_name api.2weekmail.fyi;
|
server_name api.2weekmail.fyi;
|
||||||
|
|
||||||
# Security headers
|
# Security headers
|
||||||
@ -76,8 +79,7 @@ server {
|
|||||||
|
|
||||||
# Proxy settings
|
# Proxy settings
|
||||||
location / {
|
location / {
|
||||||
limit_req zone=api_limit burst=20 nodelay;
|
proxy_pass http://localhost:3000/api/; # API container port
|
||||||
proxy_pass http://localhost:3000; # API container port
|
|
||||||
proxy_set_header Host $host;
|
proxy_set_header Host $host;
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
@ -93,13 +95,17 @@ server {
|
|||||||
proxy_send_timeout 60s;
|
proxy_send_timeout 60s;
|
||||||
proxy_read_timeout 300s; # Longer timeout for API calls
|
proxy_read_timeout 300s; # Longer timeout for API calls
|
||||||
}
|
}
|
||||||
|
|
||||||
|
listen 443 ssl; # managed by Certbot
|
||||||
|
ssl_certificate /etc/letsencrypt/live/2weekmail.fyi/fullchain.pem; # managed by Certbot
|
||||||
|
ssl_certificate_key /etc/letsencrypt/live/2weekmail.fyi/privkey.pem; # managed by Certbot
|
||||||
|
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
|
||||||
|
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Home page
|
# Home page
|
||||||
server {
|
server {
|
||||||
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
|
|
||||||
|
|
||||||
listen 80;
|
|
||||||
server_name 2weekmail.fyi;
|
server_name 2weekmail.fyi;
|
||||||
|
|
||||||
# Security headers
|
# Security headers
|
||||||
@ -110,8 +116,7 @@ server {
|
|||||||
|
|
||||||
# Proxy settings
|
# Proxy settings
|
||||||
location / {
|
location / {
|
||||||
limit_req zone=api_limit burst=20 nodelay;
|
proxy_pass http://localhost:3700; # API container port
|
||||||
proxy_pass http://localhost:3350; # API container port
|
|
||||||
proxy_set_header Host $host;
|
proxy_set_header Host $host;
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
@ -127,4 +132,59 @@ server {
|
|||||||
proxy_send_timeout 60s;
|
proxy_send_timeout 60s;
|
||||||
proxy_read_timeout 300s; # Longer timeout for API calls
|
proxy_read_timeout 300s; # Longer timeout for API calls
|
||||||
}
|
}
|
||||||
|
|
||||||
|
listen 443 ssl; # managed by Certbot
|
||||||
|
ssl_certificate /etc/letsencrypt/live/2weekmail.fyi/fullchain.pem; # managed by Certbot
|
||||||
|
ssl_certificate_key /etc/letsencrypt/live/2weekmail.fyi/privkey.pem; # managed by Certbot
|
||||||
|
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
|
||||||
|
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
|
||||||
|
|
||||||
|
}
|
||||||
|
server {
|
||||||
|
if ($host = 2weekmail.fyi) {
|
||||||
|
return 301 https://$host$request_uri;
|
||||||
|
} # managed by Certbot
|
||||||
|
|
||||||
|
|
||||||
|
listen 80;
|
||||||
|
server_name 2weekmail.fyi;
|
||||||
|
return 404; # managed by Certbot
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
server {
|
||||||
|
if ($host = webmail.2weekmail.fyi) {
|
||||||
|
return 301 https://$host$request_uri;
|
||||||
|
} # managed by Certbot
|
||||||
|
|
||||||
|
|
||||||
|
listen 80;
|
||||||
|
server_name webmail.2weekmail.fyi;
|
||||||
|
return 404; # managed by Certbot
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
server {
|
||||||
|
if ($host = admin.2weekmail.fyi) {
|
||||||
|
return 301 https://$host$request_uri;
|
||||||
|
} # managed by Certbot
|
||||||
|
|
||||||
|
|
||||||
|
listen 80;
|
||||||
|
server_name admin.2weekmail.fyi;
|
||||||
|
return 404; # managed by Certbot
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
server {
|
||||||
|
if ($host = api.2weekmail.fyi) {
|
||||||
|
return 301 https://$host$request_uri;
|
||||||
|
} # managed by Certbot
|
||||||
|
|
||||||
|
|
||||||
|
listen 80;
|
||||||
|
server_name api.2weekmail.fyi;
|
||||||
|
return 404; # managed by Certbot
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -1,20 +0,0 @@
|
|||||||
PORT=3000
|
|
||||||
WEB_PORT=3700
|
|
||||||
IP=0.0.0.0
|
|
||||||
ROOT_PATH=/root/newmail/api
|
|
||||||
NODE_ENV=production
|
|
||||||
DB_HOST=localhost
|
|
||||||
DB_USER=postfix
|
|
||||||
DB_PASS=CHANGEME
|
|
||||||
DB_NAME=CHANGEME
|
|
||||||
JWT_SECRET=CHANGEME
|
|
||||||
SMTP_HOST=postfix
|
|
||||||
SMTP_PORT=587
|
|
||||||
SMTP_SECURE=false
|
|
||||||
SMTP_USER=admin@localhost
|
|
||||||
SMTP_PASS=CHANGEME
|
|
||||||
CF_API_TOKEN=CHANGEME
|
|
||||||
CF_EMAIL=CHANGEME
|
|
||||||
SERVER_IP=CHANGEME
|
|
||||||
MAILSERVER_CONTAINER=mailserver_postfix
|
|
||||||
CONFIG_PATH=/tmp/docker-mailserver/config/
|
|
||||||
@ -5,6 +5,11 @@ WORKDIR /app
|
|||||||
COPY package*.json ./
|
COPY package*.json ./
|
||||||
|
|
||||||
RUN npm install
|
RUN npm install
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
opendkim \
|
||||||
|
opendkim-tools \
|
||||||
|
openssl \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
|
|||||||
21
api/db/migrations/20250320064614_add_id_to_mailboxes.js
Normal file
21
api/db/migrations/20250320064614_add_id_to_mailboxes.js
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* @param { import("knex").Knex } knex
|
||||||
|
* @returns { Promise<void> }
|
||||||
|
*/
|
||||||
|
exports.up = function(knex) {
|
||||||
|
return knex.schema.alterTable('mailbox', function (table) {
|
||||||
|
table.bigInteger('id');
|
||||||
|
|
||||||
|
table.index('id');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param { import("knex").Knex } knex
|
||||||
|
* @returns { Promise<void> }
|
||||||
|
*/
|
||||||
|
exports.down = function(knex) {
|
||||||
|
return knex.schema.alterTable('mailbox', function (table) {
|
||||||
|
table.dropColumn('id');
|
||||||
|
});
|
||||||
|
};
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
/**
|
||||||
|
* @param { import("knex").Knex } knex
|
||||||
|
* @returns { Promise<void> }
|
||||||
|
*/
|
||||||
|
exports.up = function(knex) {
|
||||||
|
return knex.schema.alterTable('mailbox', function (table) {
|
||||||
|
table.uuid('id').alter();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param { import("knex").Knex } knex
|
||||||
|
* @returns { Promise<void> }
|
||||||
|
*/
|
||||||
|
exports.down = function(knex) {
|
||||||
|
return knex.schema.alterTable('mailbox', function (table) {
|
||||||
|
table.bigInteger('id').alter();
|
||||||
|
});
|
||||||
|
};
|
||||||
@ -1,5 +1,6 @@
|
|||||||
const BaseModel = require('./BaseModel');
|
const BaseModel = require('./BaseModel');
|
||||||
const { Model } = require('objection');
|
const { Model } = require('objection');
|
||||||
|
const { v4: uuidv4 } = require('uuid');
|
||||||
|
|
||||||
class Mailbox extends BaseModel {
|
class Mailbox extends BaseModel {
|
||||||
static get tableName() {
|
static get tableName() {
|
||||||
@ -16,6 +17,7 @@ class Mailbox extends BaseModel {
|
|||||||
required: ['username', 'password', 'name', 'domain', 'local_part'],
|
required: ['username', 'password', 'name', 'domain', 'local_part'],
|
||||||
|
|
||||||
properties: {
|
properties: {
|
||||||
|
id: { type: 'string', format: 'uuid', maxLength: 36, default: uuidv4() },
|
||||||
username: { type: 'string', minLength: 1, maxLength: 255 },
|
username: { type: 'string', minLength: 1, maxLength: 255 },
|
||||||
password: { type: 'string', minLength: 1, maxLength: 255 },
|
password: { type: 'string', minLength: 1, maxLength: 255 },
|
||||||
name: { type: 'string', maxLength: 255 },
|
name: { type: 'string', maxLength: 255 },
|
||||||
|
|||||||
@ -5,6 +5,9 @@ const execPromise = util.promisify(exec);
|
|||||||
require('dotenv').config();
|
require('dotenv').config();
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const { models } = require(path.resolve(process.env.ROOT_PATH, './db/db.js'));
|
const { models } = require(path.resolve(process.env.ROOT_PATH, './db/db.js'));
|
||||||
|
const net = require('net');
|
||||||
|
const fs = require('fs').promises;
|
||||||
|
const { execSync } = require('child_process');
|
||||||
|
|
||||||
// Create Cloudflare API client
|
// Create Cloudflare API client
|
||||||
const createCloudflareClient = () => {
|
const createCloudflareClient = () => {
|
||||||
@ -37,20 +40,21 @@ const createCloudflareClient = () => {
|
|||||||
|
|
||||||
async function generateDkimKey(domain) {
|
async function generateDkimKey(domain) {
|
||||||
try {
|
try {
|
||||||
// Ensure the domain directory exists in the container
|
// Create directory if it doesn't exist
|
||||||
await execPromise(`docker exec mailserver_opendkim mkdir -p /etc/opendkim/keys/${domain}`);
|
const keyPath = `/etc/opendkim/keys/${domain}`;
|
||||||
|
await fs.mkdir(keyPath, { recursive: true });
|
||||||
|
|
||||||
// Generate the DKIM key
|
// Generate the DKIM key using opendkim-genkey directly
|
||||||
await execPromise(`docker exec mailserver_opendkim opendkim-genkey -D /etc/opendkim/keys/${domain} -d ${domain} -s mail`);
|
execSync(`opendkim-genkey -D ${keyPath} -d ${domain} -s mail`);
|
||||||
|
|
||||||
// Set proper permissions
|
// Set proper permissions (if needed)
|
||||||
await execPromise(`docker exec mailserver_opendkim chown -R opendkim:opendkim /etc/opendkim/keys/${domain}`);
|
execSync(`chown -R opendkim:opendkim ${keyPath}`);
|
||||||
|
|
||||||
// Read the generated public key
|
// Read the generated public key
|
||||||
const { stdout } = await execPromise(`docker exec mailserver_opendkim cat /etc/opendkim/keys/${domain}/mail.txt`);
|
const publicKeyContent = await fs.readFile(`${keyPath}/mail.txt`, 'utf8');
|
||||||
|
|
||||||
// Extract the DKIM record value
|
// Extract the DKIM record value
|
||||||
const dkimMatch = stdout.match(/p=([^)]+)/);
|
const dkimMatch = publicKeyContent.match(/p=([^)]+)/);
|
||||||
const dkimValue = dkimMatch ? dkimMatch[1] : null;
|
const dkimValue = dkimMatch ? dkimMatch[1] : null;
|
||||||
|
|
||||||
if (!dkimValue) {
|
if (!dkimValue) {
|
||||||
@ -181,14 +185,26 @@ async function configureDNS(domain) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function checkOpenDkimAvailability() {
|
function checkOpenDKIM() {
|
||||||
try {
|
return new Promise((resolve, reject) => {
|
||||||
await execPromise('docker exec mailserver_opendkim echo "OpenDKIM is available"');
|
const client = new net.Socket();
|
||||||
return true;
|
|
||||||
} catch (error) {
|
client.connect(8891, 'opendkim', () => {
|
||||||
console.error('OpenDKIM container is not available:', error.message);
|
client.destroy();
|
||||||
return false;
|
resolve(true);
|
||||||
}
|
});
|
||||||
|
|
||||||
|
client.on('error', (err) => {
|
||||||
|
client.destroy();
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add timeout
|
||||||
|
setTimeout(() => {
|
||||||
|
client.destroy();
|
||||||
|
reject(new Error('Connection timeout'));
|
||||||
|
}, 5000);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function configure2WeekMailDNS() {
|
async function configure2WeekMailDNS() {
|
||||||
@ -328,7 +344,7 @@ async function configure2WeekMailDNS() {
|
|||||||
async function exportAllDomains() {
|
async function exportAllDomains() {
|
||||||
try {
|
try {
|
||||||
// Check if OpenDKIM is available
|
// Check if OpenDKIM is available
|
||||||
const isOpenDkimAvailable = await checkOpenDkimAvailability();
|
const isOpenDkimAvailable = await checkOpenDKIM();
|
||||||
if (!isOpenDkimAvailable) {
|
if (!isOpenDkimAvailable) {
|
||||||
console.error('Cannot proceed: OpenDKIM container is not available');
|
console.error('Cannot proceed: OpenDKIM container is not available');
|
||||||
return;
|
return;
|
||||||
@ -354,7 +370,7 @@ async function exportAllDomains() {
|
|||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
// Update the domain record to mark it as configured in Cloudflare
|
// Update the domain record to mark it as configured in Cloudflare
|
||||||
await models.Domain.query().findById(domain.id).patch({ in_cloudflare: 1 });
|
await models.Domain.query().where('domain', domain.domain).patch({ in_cloudflare: 1 });
|
||||||
successCount++;
|
successCount++;
|
||||||
} else {
|
} else {
|
||||||
failCount++;
|
failCount++;
|
||||||
|
|||||||
@ -7,8 +7,8 @@ require("dotenv").config();
|
|||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
const password = crypto.randomBytes(32).toString("hex");
|
const password = crypto.randomBytes(32).toString("hex");
|
||||||
const email = "admin@2weekmail.fyi";
|
const email = process.argv[2];
|
||||||
const username = "admin";
|
const username = process.argv[3];
|
||||||
|
|
||||||
const salt = await bcrypt.genSalt(10);
|
const salt = await bcrypt.genSalt(10);
|
||||||
const passwordEncrypted = await bcrypt.hash(password, salt);
|
const passwordEncrypted = await bcrypt.hash(password, salt);
|
||||||
|
|||||||
3
api/test.js
Normal file
3
api/test.js
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
const uuid = require('uuid');
|
||||||
|
|
||||||
|
console.log(uuid.v4());
|
||||||
@ -154,6 +154,7 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- ./api:/app
|
- ./api:/app
|
||||||
- /app/node_modules
|
- /app/node_modules
|
||||||
|
- opendkim_data:/etc/opendkim
|
||||||
environment:
|
environment:
|
||||||
- PORT=${PORT}
|
- PORT=${PORT}
|
||||||
- WEB_PORT=${WEB_PORT}
|
- WEB_PORT=${WEB_PORT}
|
||||||
@ -165,7 +166,7 @@ services:
|
|||||||
- DB_PASS=${DB_PASS}
|
- DB_PASS=${DB_PASS}
|
||||||
- DB_NAME=${DB_NAME}
|
- DB_NAME=${DB_NAME}
|
||||||
- JWT_SECRET=${JWT_SECRET}
|
- JWT_SECRET=${JWT_SECRET}
|
||||||
- SMTP_HOST=${SMTP_HOST}
|
- SMTP_HOST=mailserver
|
||||||
- SMTP_PORT=${SMTP_PORT}
|
- SMTP_PORT=${SMTP_PORT}
|
||||||
- SMTP_SECURE=${SMTP_SECURE}
|
- SMTP_SECURE=${SMTP_SECURE}
|
||||||
- SMTP_USER=${SMTP_USER}
|
- SMTP_USER=${SMTP_USER}
|
||||||
|
|||||||
6
package-lock.json
generated
Normal file
6
package-lock.json
generated
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"name": "2weekmail",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user