lots of updates

This commit is contained in:
Ryahn 2025-02-23 16:30:22 -06:00
parent 59c7245010
commit 31cf4524bf
11 changed files with 347 additions and 42 deletions

3
.cron Normal file
View File

@ -0,0 +1,3 @@
0 * * * * /root/temp_mail/scripts/delete_messages_24h.sh >> /var/log/temp_mail/cleanup.log 2>&1
0 0 * * * /root/temp_mail/scripts/delete_emails_14d.sh >> /var/log/temp_mail/cleanup.log 2>&1

2
.env-example Normal file
View File

@ -0,0 +1,2 @@
MYSQL_ADMIN_USER=admin
MYSQL_ADMIN_PASSWORD=

View File

@ -0,0 +1,15 @@
module.exports = {
development: {
client: 'mysql2',
connection: {
database: 'email_api',
user: 'email_api',
password: '',
host: 'localhost',
port: 3306
},
migrations: {
directory: '../db/migrations'
}
}
};

View File

@ -3,30 +3,43 @@
"version": "1.0.0",
"main": "app.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node app.js",
"dev": "nodemon app.js"
"start": "npm run setup && node app.js",
"dev": "npm run setup && nodemon app.js",
"lint": "eslint .",
"lint:fix": "eslint --fix .",
"setup": "bash setup.sh"
},
"keywords": [],
"author": "",
"author": "Ryahn",
"license": "ISC",
"description": "",
"dependencies": {
"bcrypt": "^5.1.1",
"chokidar": "^4.0.3",
"cors": "^2.8.5",
"express": "^4.21.2",
"favicon": "^0.0.2",
"haraka": "^0.0.33",
"haraka-plugin-dkim": "^1.0.9",
"html-to-text": "^9.0.5",
"jsonwebtoken": "^9.0.2",
"knex": "^3.1.0",
"moniker": "^0.1.2",
"mysql2": "^3.12.0",
"node-cron": "^3.0.3",
"nodemailer": "^6.10.0",
"objection": "^3.1.5",
"sanitize-html": "^2.14.0",
"serve-favicon": "^2.5.0",
"swagger-jsdoc": "^6.2.8",
"swagger-ui-express": "^5.0.1",
"unique-names-generator": "^4.7.1"
},
"repository": {
"type": "git",
"url": "git@git.zonies.xyz:Ryahn/Temp-Email-Service.git"
},
"description": "",
"devDependencies": {
"eslint": "^9.21.0"
}
}

View File

@ -1,27 +1,28 @@
const User = require('../src/db/models/User');
const { Model } = require('objection');
const Knex = require('knex');
const knexConfig = require('../src/config/database');
const User = require("../src/db/models/User");
const { Model } = require("objection");
const Knex = require("knex");
const knexConfig = require("../src/config/database");
const knex = Knex(knexConfig.development);
Model.knex(knex);
const Logger = require("../src/utils/logger");
const log = new Logger();
const email = process.argv[2];
const id = process.argv[3];
if (!email && !id) {
console.error('Usage: node delete_user.js <email> || <id>');
process.exit(1);
log.error("Usage: node delete_user.js <email> || <id>");
process.exit(1);
}
async function main() {
let user;
if (id) {
user = await User.query().where('id', id).delete();
} else {
user = await User.query().where('email', email).delete();
}
console.log(`User deleted`);
process.exit(0);
if (id) {
await User.query().where("id", id).delete();
} else {
await User.query().where("email", email).delete();
}
log.info(`User deleted`);
process.exit(0);
}
main();

View File

@ -29,7 +29,7 @@ async function main() {
);
};
token = generateToken();
const token = generateToken();
await User.query().where("id", user.id).update({ api_key: token });
console.log("Token generated and updated for user", user.email);
console.log("Token:", token);

231
setup.sh
View File

@ -1,21 +1,242 @@
#! /bin/bash
# Color definitions
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
NC='\033[0m' # No Color
# Logger functions
log_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
CURRENT_DIR=$(pwd)
SERVICE_FILE="haraka.service"
SERVICE_PATH="/etc/systemd/system/$SERVICE_FILE"
LOCAL_SERVICE_PATH="$CURRENT_DIR/$SERVICE_FILE"
# Load environment variables
if [ -f "$CURRENT_DIR/.env" ]; then
export $(cat "$CURRENT_DIR/.env" | grep -v '^#' | xargs)
else
log_error ".env file not found"
exit 1
fi
# Check if MYSQL_ADMIN_PASSWORD is set
if [ -z "$MYSQL_ADMIN_PASSWORD" ]; then
log_error "MYSQL_ADMIN_PASSWORD not found in .env file"
exit 1
fi
if [ -z "$MYSQL_ADMIN_USER" ]; then
log_error "MYSQL_ADMIN_USER not found in .env file"
exit 1
fi
# Database configuration
DB_NAME="email_api1"
DB_USER="email_api1"
DB_PASS=$(openssl rand -base64 12) # Generate random password
# Check if mysql-server is installed
if ! command -v mysql &> /dev/null; then
log_warn "MySQL Server not found. Installing..."
sudo apt-get update >> /dev/null 2>&1
sudo apt-get install -y mysql-server >> /dev/null 2>&1
else
log_info "MySQL Server is already installed"
fi
# Wait for MySQL to be ready
log_info "Waiting for MySQL to be ready..."
while ! mysqladmin ping -h "localhost" -u admin -p"${MYSQL_ADMIN_PASSWORD}" --silent; do
sleep 1
done
# Create database and user
log_info "Setting up MySQL database and user..."
mysql -u admin -p"${MYSQL_ADMIN_PASSWORD}" -e "CREATE DATABASE IF NOT EXISTS ${DB_NAME};"
# Check if user exists
USER_EXISTS=$(mysql -u "${MYSQL_ADMIN_USER}" -p"${MYSQL_ADMIN_PASSWORD}" -s -N -e "SELECT COUNT(*) FROM mysql.user WHERE User = '${DB_USER}' AND Host = 'localhost';")
if [ "$USER_EXISTS" -eq 0 ]; then
log_info "Creating new database user..."
mysql -u "${MYSQL_ADMIN_USER}" -p"${MYSQL_ADMIN_PASSWORD}" -e "CREATE USER '${DB_USER}'@'localhost' IDENTIFIED BY '${DB_PASS}';"
mysql -u "${MYSQL_ADMIN_USER}" -p"${MYSQL_ADMIN_PASSWORD}" -e "GRANT ALL PRIVILEGES ON ${DB_NAME}.* TO '${DB_USER}'@'localhost';"
mysql -u "${MYSQL_ADMIN_USER}" -p"${MYSQL_ADMIN_PASSWORD}" -e "FLUSH PRIVILEGES;"
# Update database config file with new values
log_info "Updating database configuration..."
cp "$CURRENT_DIR/src/config/database-example.js" "$CURRENT_DIR/src/config/database.js"
CONFIG_FILE="$CURRENT_DIR/src/config/database.js"
sed -i "s/database: \"\"/database: '${DB_NAME}'/" "$CONFIG_FILE"
sed -i "s/user: \"\"/user: '${DB_USER}'/" "$CONFIG_FILE"
sed -i "s/password: \"\"/password: '${DB_PASS}'/" "$CONFIG_FILE"
log_info "Database password has been updated in the configuration file"
else
log_warn "Database user already exists. Skipping user creation and configuration update."
fi
# Create group and user
sudo groupadd smtp
sudo useradd -r -g smtp smtp
log_info "Creating group and user..."
sudo groupadd smtp 2>/dev/null || true
sudo useradd -r -g smtp smtp 2>/dev/null || true
# Create required directories
log_info "Creating required directories..."
sudo mkdir -p /var/spool/haraka /var/log/haraka
sudo chown -R smtp:smtp /var/spool/haraka /var/log/haraka
# Set permissions
log_info "Setting permissions..."
sudo chmod 755 /var/spool/haraka
sudo chmod 644 /var/log/haraka
# Install Haraka
npm install -g haraka
log_info "Installing Haraka..."
if ! command -v haraka &> /dev/null; then
npm install -g Haraka
else
log_warn "Haraka is already installed"
fi
# Check if service file exists and handle accordingly
log_info "Checking if service file exists..."
if [ -f "$SERVICE_PATH" ]; then
# Calculate checksums
REMOTE_CHECKSUM=$(sha1sum "$SERVICE_PATH" | awk '{print $1}')
LOCAL_CHECKSUM=$(sha1sum "$LOCAL_SERVICE_PATH" | awk '{print $1}')
if [ "$REMOTE_CHECKSUM" != "$LOCAL_CHECKSUM" ]; then
log_warn "Service file differs from local version. Updating..."
sudo cp "$LOCAL_SERVICE_PATH" "$SERVICE_PATH"
sudo systemctl daemon-reload
sudo systemctl enable haraka
sudo systemctl restart haraka
else
log_info "Service file is up to date"
fi
else
log_info "Service file not found. Installing..."
sudo cp "$LOCAL_SERVICE_PATH" "$SERVICE_PATH"
sudo systemctl daemon-reload
log_info "Enabling and starting Haraka..."
sudo systemctl enable haraka
fi
# Install Haraka plugins
mkdir -p $CURRENT_DIR/src/email_server/plugins/queue
ln -s $CURRENT_DIR/src/haraka-plugins/queue/store_message.js $CURRENT_DIR/src/email_server/plugins/queue/store_message.js
log_info "Installing Haraka plugins..."
mkdir -p "$CURRENT_DIR/src/email_server/plugins/queue"
rm -f "$CURRENT_DIR/src/email_server/plugins/queue/store_message.js"
ln -s "$CURRENT_DIR/src/haraka-plugins/queue/store_message.js" "$CURRENT_DIR/src/email_server/plugins/queue/store_message.js"
log_info "Installing dependencies..."
npm install >> /dev/null 2>&1
log_info "Running migrations..."
npx knex migrate:latest
CRON_FILE="$CURRENT_DIR/.cron"
echo "" > "$CRON_FILE"
echo "0 * * * * $CURRENT_DIR/scripts/delete_messages_24h.sh >> /var/log/temp_mail/cleanup.log 2>&1" >> "$CRON_FILE"
echo "0 0 * * * $CURRENT_DIR/scripts/delete_emails_14d.sh >> /var/log/temp_mail/cleanup.log 2>&1" >> "$CRON_FILE"
TEMP_CRON="/tmp/temp_cron"
mkdir -p /var/log/temp_mail
# Check if .cron file exists
if [ ! -f "$CRON_FILE" ]; then
log_error "Cron file not found at $CRON_FILE"
exit 1
fi
# Function to normalize cron entry (remove extra spaces)
normalize_cron() {
echo "$1" | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//' -e 's/[ \t]\+/ /g'
}
# Function to check if a cron job exists
check_cron_job() {
local cron_entry="$1"
local normalized_entry=$(normalize_cron "$cron_entry")
# Get current user's crontab
crontab -l > "$TEMP_CRON" 2>/dev/null
# Check if the normalized entry exists in current crontab
if grep -Fq "$normalized_entry" "$TEMP_CRON"; then
return 0 # Found
else
return 1 # Not found
fi
}
# Function to install cron jobs
install_cron_jobs() {
log_info "Installing cron jobs..."
# Get current crontab
crontab -l > "$TEMP_CRON" 2>/dev/null
# Read each line from .cron file
while IFS= read -r line || [ -n "$line" ]; do
# Skip comments and empty lines
[[ $line =~ ^#.*$ ]] || [ -z "$line" ] && continue
normalized_line=$(normalize_cron "$line")
if ! grep -Fq "$normalized_line" "$TEMP_CRON"; then
log_warn "Cron job not found, adding: $normalized_line"
echo "$normalized_line" >> "$TEMP_CRON"
else
log_info "Cron job already exists: $normalized_line"
fi
done < "$CRON_FILE"
# Install new crontab
crontab "$TEMP_CRON"
# Clean up
rm -f "$TEMP_CRON"
}
# Main execution
log_info "Checking cron jobs..."
# Read .cron file and check each entry
missing_jobs=0
while IFS= read -r line || [ -n "$line" ]; do
# Skip comments and empty lines
[[ $line =~ ^#.*$ ]] || [ -z "$line" ] && continue
if ! check_cron_job "$line"; then
log_warn "Missing cron job: $line"
missing_jobs=$((missing_jobs + 1))
else
log_info "Found cron job: $line"
fi
done < "$CRON_FILE"
# If any jobs are missing, offer to install them
if [ $missing_jobs -gt 0 ]; then
log_warn "Found $missing_jobs missing cron job(s)"
install_cron_jobs
log_info "Cron jobs have been updated"
else
log_info "All cron jobs are up to date"
fi
log_info "Installation complete!"
log_info "You can now start the email server with: sudo systemctl start haraka"

View File

@ -1,15 +1,15 @@
module.exports = {
development: {
client: 'mysql2',
connection: {
database: 'email_api',
user: 'email_api',
password: '',
host: 'localhost',
port: 3306
},
migrations: {
directory: '../db/migrations'
}
}
};
development: {
client: "mysql2",
connection: {
database: "",
user: "",
password: "",
host: "localhost",
port: 3306,
},
migrations: {
directory: "../db/migrations",
},
},
};

View File

@ -3,7 +3,6 @@ const router = express.Router();
const { authenticateToken } = require('../middleware/auth');
const TempEmail = require('../db/models/TempEmail');
router.use(authenticateToken);
const Domain = require('../db/models/Domain');
const DailyStats = require('../db/models/DailyStats');
const { generateUniqueName, getRandomDomain, mysqlSafeTimestamp } = require('../utils/functions');

View File

@ -2,7 +2,7 @@ const express = require('express');
const router = express.Router();
const { authenticateToken } = require('../middleware/auth');
const Message = require('../db/models/Message');
const TempEmail = require("../db/models/TempEmail");
router.use(authenticateToken);
/**

51
src/utils/logger.js Normal file
View File

@ -0,0 +1,51 @@
class Logger {
constructor() {
this.levels = {
INFO: "\x1b[36m", // Cyan
WARNING: "\x1b[33m", // Yellow
ERROR: "\x1b[31m", // Red
CRITICAL: "\x1b[31m\x1b[1m", // Bold Red
DEBUG: "\x1b[32m", // Green
RESET: "\x1b[0m", // Reset color
};
}
_getTimestamp() {
const now = new Date();
return now.toISOString().replace(/T/, " ").replace(/\..+/, "");
}
_log(level, message) {
const timestamp = this._getTimestamp();
const color = this.levels[level] || "";
console.log(
`${color}[${timestamp}] [${level}] ${message}${this.levels.RESET}`
);
}
info(message) {
this._log("INFO", message);
}
warning(message) {
this._log("WARNING", message);
}
error(message) {
this._log("ERROR", message);
}
critical(message) {
this._log("CRITICAL", message);
}
debug(message) {
this._log("DEBUG", message);
}
}
// Usage
// const log = new Logger();
// log.info("This is an info message");
module.exports = Logger;