diff --git a/INSTALL.md b/INSTALL.md
index 3713180..2d0d938 100644
--- a/INSTALL.md
+++ b/INSTALL.md
@@ -50,6 +50,12 @@ Click **+ Add another Path, Port, Variable, Label or Device** for each of these:
- **Host Path:** `/mnt/user/appdata/email-sigs/data`
- **Access Mode:** `Read/Write`
+3. **Uploads Path**
+ - **Name:** `Uploads`
+ - **Container Path:** `/app/public/uploads`
+ - **Host Path:** `/mnt/user/appdata/email-sigs/public/uploads`
+ - **Access Mode:** `Read/Write`
+
### Add Variables
Click **+ Add another Path, Port, Variable, Label or Device** for each of these:
diff --git a/docker-compose.yml b/docker-compose.yml
index 69834f1..eeb980e 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -17,6 +17,7 @@ services:
- ./secrets:/app/secrets:ro
- ./data:/app/data
- ./public/assets:/app/public/assets
+ - ./public/uploads:/app/public/uploads
environment:
- GOOGLE_ADMIN_EMAIL
- GOOGLE_CUSTOMER_ID
diff --git a/email-sig-manager.xml b/email-sig-manager.xml
index ea00165..78ce130 100644
--- a/email-sig-manager.xml
+++ b/email-sig-manager.xml
@@ -25,6 +25,7 @@
/mnt/user/appdata/email-sigs/secrets
/mnt/user/appdata/email-sigs/data
/mnt/user/appdata/email-sigs/public/assets
+ /mnt/user/appdata/email-sigs/public/uploads
my_customer
admin
diff --git a/package.json b/package.json
index 5a1072f..c0b733b 100644
--- a/package.json
+++ b/package.json
@@ -13,6 +13,7 @@
"express-basic-auth": "^1.2.1",
"googleapis": "^140.0.1",
"handlebars": "^4.7.8",
+ "multer": "^1.4.5-lts.1",
"node-cron": "^3.0.3"
},
"devDependencies": {
diff --git a/public/editor.html b/public/editor.html
index 104b5cf..7a70b6c 100644
--- a/public/editor.html
+++ b/public/editor.html
@@ -42,6 +42,13 @@
.v-btn { padding: 4px 8px; font-size: 11px; }
.save-form { display: flex; gap: 8px; margin-top: 10px; }
.save-form input { flex: 1; background: #333; border: 1px solid #444; color: #eee; padding: 8px; border-radius: 4px; font-size: 13px; }
+
+ .asset-panel { background: #222; border: 1px solid #444; border-radius: 6px; padding: 16px; margin-top: 20px; }
+ .asset-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(80px, 1fr)); gap: 10px; margin-top: 10px; }
+ .asset-item { position: relative; border: 1px solid #333; border-radius: 4px; overflow: hidden; cursor: pointer; aspect-ratio: 1; }
+ .asset-item img { width: 100%; height: 100%; object-fit: cover; }
+ .asset-item:hover .asset-copy { opacity: 1; }
+ .asset-copy { position: absolute; bottom: 0; left: 0; right: 0; background: rgba(0,0,0,0.7); color: #C9A84C; font-size: 10px; padding: 4px; text-align: center; opacity: 0; transition: opacity 0.2s; }
@@ -66,6 +73,18 @@
+
+
Image Assets
+
+
+
+
+
+
+
+
+
+
Handlebars Template (HTML)
@@ -164,6 +183,46 @@
}
}
+ async function loadImages() {
+ const images = await fetch('/api/admin/images').then(r=>r.json()).catch(()=>[]);
+ const list = document.getElementById('asset-list');
+ list.innerHTML = images.map(img => `
+
+

+
Copy URL
+
+ `).join('') || '
No images.
';
+ }
+
+ async function uploadImage() {
+ const fileEl = document.getElementById('asset-upload');
+ if (!fileEl.files.length) return;
+
+ const formData = new FormData();
+ formData.append('image', fileEl.files[0]);
+
+ showStatus('Uploading...', true);
+ const res = await fetch('/api/admin/images', {
+ method: 'POST',
+ body: formData
+ }).then(r=>r.json()).catch(e=>({error:e.message}));
+
+ if (res.ok) {
+ showStatus('Image uploaded!', true);
+ loadImages();
+ } else {
+ showStatus('Error: '+res.error, false);
+ }
+ fileEl.value = ''; // Reset input
+ }
+
+ function copyImageUrl(url) {
+ const fullUrl = window.location.origin + url;
+ navigator.clipboard.writeText(fullUrl).then(() => {
+ showStatus('URL copied to clipboard: ' + url, true);
+ });
+ }
+
async function updatePreview() {
const templateHtml = document.getElementById('template-editor').value;
const userData = {
@@ -184,6 +243,7 @@
}
loadTemplate();
loadVersions();
+ loadImages();