diff --git a/public/index.html b/public/index.html
index 2df5589..aaabee7 100644
--- a/public/index.html
+++ b/public/index.html
@@ -43,6 +43,13 @@
+
+
diff --git a/public/script.js b/public/script.js
index 80c1d6f..967a07e 100644
--- a/public/script.js
+++ b/public/script.js
@@ -4,21 +4,28 @@ document.addEventListener('DOMContentLoaded', () => {
const submitBtn = document.getElementById('submitBtn');
const historyList = document.getElementById('historyList');
const clearHistoryBtn = document.getElementById('clearHistory');
+ const assetGrid = document.getElementById('assetGrid');
- // Load history on page load
+ // Load history and assets on page load
loadHistory();
+ loadAssets();
// Form submission
form.addEventListener('submit', async (e) => {
e.preventDefault();
const formData = new FormData(form);
+ const selectedAssets = Array.from(
+ document.querySelectorAll('input[name="footerAssets"]:checked')
+ ).map(cb => cb.value);
+
const data = {
to: formData.get('to'),
cc: formData.get('cc') || undefined,
subject: formData.get('subject'),
body: formData.get('body'),
- isHtml: formData.get('format') === 'html'
+ isHtml: formData.get('format') === 'html',
+ footerAssets: selectedAssets
};
submitBtn.disabled = true;
@@ -80,6 +87,42 @@ document.addEventListener('DOMContentLoaded', () => {
}
}
+ // Load assets from server
+ async function loadAssets() {
+ try {
+ const response = await fetch('/api/assets');
+ const result = await response.json();
+
+ if (result.success) {
+ renderAssetGrid(result.assets);
+ } else {
+ assetGrid.innerHTML = 'Fehler beim Laden der Assets
';
+ }
+ } catch (error) {
+ console.error('Error loading assets:', error);
+ assetGrid.innerHTML = 'Fehler beim Laden der Assets
';
+ }
+ }
+
+ // Render asset grid
+ function renderAssetGrid(assets) {
+ if (!assets || assets.length === 0) {
+ assetGrid.innerHTML = 'Keine Assets verfügbar
';
+ return;
+ }
+
+ assetGrid.innerHTML = assets.map(filename => {
+ const name = filename.replace(/\.[^.]+$/, '');
+ return `
+
+ `;
+ }).join('');
+ }
+
// Render history list
function renderHistory(history) {
if (!history || history.length === 0) {
diff --git a/public/style.css b/public/style.css
index f6b57a9..1e6a4b2 100644
--- a/public/style.css
+++ b/public/style.css
@@ -235,12 +235,64 @@ button:disabled {
background: none;
}
-.empty-message {
+.empty-message,
+.loading-message {
text-align: center;
color: #999;
padding: 40px 20px;
}
+/* Asset Grid */
+.asset-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(70px, 1fr));
+ gap: 10px;
+ padding: 10px;
+ background: #f9f9f9;
+ border: 1px solid #ddd;
+ border-radius: 4px;
+}
+
+.asset-item {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ padding: 8px;
+ border: 2px solid transparent;
+ border-radius: 4px;
+ cursor: pointer;
+ transition: all 0.15s ease;
+}
+
+.asset-item:hover {
+ background: #eee;
+}
+
+.asset-item img {
+ width: 40px;
+ height: 40px;
+ object-fit: contain;
+}
+
+.asset-item .asset-name {
+ font-size: 11px;
+ color: #666;
+ margin-top: 4px;
+ text-align: center;
+ word-break: break-all;
+}
+
+.asset-item:has(input:checked) {
+ border-color: #3498db;
+ background: #e7f3fd;
+}
+
+.asset-item input[type="checkbox"] {
+ position: absolute;
+ opacity: 0;
+ pointer-events: none;
+}
+
@media (max-width: 900px) {
.container {
flex-direction: column;
diff --git a/src/mailer.js b/src/mailer.js
index 3879db4..f525f75 100644
--- a/src/mailer.js
+++ b/src/mailer.js
@@ -15,23 +15,25 @@ const transporter = nodemailer.createTransport({
});
const FOOTER_NAME = process.env.MAIL_FOOTER_NAME || 'Joachim Hummel';
-const FOOTER_IMAGE = path.join(__dirname, '..', 'assets', 'homeicon.png');
-function getHtmlFooter() {
+function getHtmlFooter(assets) {
+ if (!assets || assets.length === 0) {
+ return '';
+ }
+
+ const icons = assets.map(filename => {
+ const cid = filename.replace(/\.[^.]+$/, '');
+ return `
`;
+ }).join('');
+
return `
-
-
-
-
- |
-
- Absender
- ${FOOTER_NAME}
- |
-
-
+
+
${icons}
+
Absender
+ ${FOOTER_NAME}
+
`;
}
@@ -40,17 +42,19 @@ function getTextFooter() {
}
async function sendMail(email) {
+ const footerAssets = email.footerAssets || [];
+
+ const attachments = footerAssets.map(filename => ({
+ filename,
+ path: path.join(__dirname, '..', 'assets', filename),
+ cid: filename.replace(/\.[^.]+$/, '')
+ }));
+
const mailOptions = {
from: `"${process.env.MAIL_FROM_NAME}" <${process.env.MAIL_FROM_EMAIL}>`,
to: email.to,
subject: email.subject,
- attachments: [
- {
- filename: 'homeicon.png',
- path: FOOTER_IMAGE,
- cid: 'homeicon'
- }
- ]
+ attachments
};
if (email.cc) {
@@ -58,11 +62,10 @@ async function sendMail(email) {
}
if (email.isHtml) {
- mailOptions.html = email.body + getHtmlFooter();
+ mailOptions.html = email.body + getHtmlFooter(footerAssets);
} else {
mailOptions.text = email.body + getTextFooter();
- // Also send HTML version with footer for better display
- mailOptions.html = `${email.body}` + getHtmlFooter();
+ mailOptions.html = `${email.body}` + getHtmlFooter(footerAssets);
}
const info = await transporter.sendMail(mailOptions);
diff --git a/src/server.js b/src/server.js
index d43a56c..373a977 100644
--- a/src/server.js
+++ b/src/server.js
@@ -1,6 +1,7 @@
require('dotenv').config();
const express = require('express');
+const fs = require('fs');
const path = require('path');
const db = require('./database');
const mailer = require('./mailer');
@@ -11,6 +12,7 @@ const PORT = process.env.PORT || 3000;
// Middleware
app.use(express.json());
app.use(express.static(path.join(__dirname, '..', 'public')));
+app.use('/assets', express.static(path.join(__dirname, '..', 'assets')));
// Email validation helper
function isValidEmail(email) {
@@ -20,9 +22,29 @@ function isValidEmail(email) {
// API Routes
+// Get available assets
+app.get('/api/assets', (req, res) => {
+ const assetsDir = path.join(__dirname, '..', 'assets');
+ const allowedExtensions = ['.png', '.jpg', '.jpeg', '.gif', '.svg'];
+
+ try {
+ const files = fs.readdirSync(assetsDir);
+ const assets = files
+ .filter(file => {
+ const ext = path.extname(file).toLowerCase();
+ return allowedExtensions.includes(ext);
+ })
+ .sort();
+
+ res.json({ success: true, assets });
+ } catch (error) {
+ res.status(500).json({ success: false, error: error.message });
+ }
+});
+
// Send email
app.post('/api/send', async (req, res) => {
- const { to, cc, subject, body, isHtml } = req.body;
+ const { to, cc, subject, body, isHtml, footerAssets } = req.body;
// Validation
if (!to || !subject || !body) {
@@ -46,7 +68,7 @@ app.post('/api/send', async (req, res) => {
});
}
- const email = { to, cc, subject, body, isHtml: !!isHtml };
+ const email = { to, cc, subject, body, isHtml: !!isHtml, footerAssets: footerAssets || [] };
try {
const info = await mailer.sendMail(email);