diff --git a/.env.example b/.env.example
index 27de352..aa2557e 100644
--- a/.env.example
+++ b/.env.example
@@ -7,4 +7,6 @@ CACHE_LOCATION=./cache
ROOT_CATEGORY_ID=0
JTL_SHOP_ID=0
JTL_SPRACHE_ID=1
-JTL_PLATTFORM_ID=1
\ No newline at end of file
+JTL_PLATTFORM_ID=1
+SERVER_PORT=3991
+SERVER_HOST=127.0.0.1
\ No newline at end of file
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..3b27159
--- /dev/null
+++ b/index.html
@@ -0,0 +1,226 @@
+
+
+
+
+
+ Category Tree Viewer
+
+
+
+
+
📦 Category Tree
+
+
Loading categories...
+
+
+
+
+
+
diff --git a/index.js b/index.js
index 4c12dcb..e171987 100644
--- a/index.js
+++ b/index.js
@@ -1,5 +1,6 @@
import categorySyncer from './category-syncer.js';
import pictureSyncer from './picture-syncer.js';
+import { startServer } from './server.js';
categorySyncer.on('synced', async ({ tree, unprunedTree, changed }) => {
if (changed) {
@@ -44,3 +45,6 @@ if (process.stdout.isTTY) {
process.exit(0);
});
}
+
+// Start Express server
+startServer();
diff --git a/picture-syncer.js b/picture-syncer.js
index 1b75110..74a2bd0 100644
--- a/picture-syncer.js
+++ b/picture-syncer.js
@@ -1,5 +1,6 @@
import fs from 'fs/promises';
import path from 'path';
+import sharp from 'sharp';
import { createConnection } from './database.js';
class PictureSyncer {
@@ -25,18 +26,17 @@ class PictureSyncer {
// Directory might be empty or new
}
- // Filter for image files (assuming we save as {id}.jpg or similar, but let's check just by ID prefix)
- // Actually, let's assume we save as `${id}.jpg`
+ // Filter for image files (assuming we save as {id}.avif)
const existingIds = existingFiles
- .filter(f => f.endsWith('.jpg'))
- .map(f => parseInt(f.replace('.jpg', '')));
+ .filter(f => f.endsWith('.avif'))
+ .map(f => parseInt(f.replace('.avif', '')));
const validIds = new Set(imageIds.filter(id => id !== null && id !== undefined));
// 1. Delete obsolete images
const toDelete = existingIds.filter(id => !validIds.has(id));
for (const id of toDelete) {
- const filePath = path.join(groupDir, `${id}.jpg`);
+ const filePath = path.join(groupDir, `${id}.avif`);
await fs.unlink(filePath);
}
if (toDelete.length > 0) {
@@ -73,9 +73,11 @@ class PictureSyncer {
for (const record of result.recordset) {
if (record.bBild) {
- const filePath = path.join(dir, `${record.kBild}.jpg`);
- await fs.writeFile(filePath, record.bBild);
- // console.log(`💾 Saved image: ${filePath}`);
+ const filePath = path.join(dir, `${record.kBild}.avif`);
+ // Convert to AVIF using sharp
+ await sharp(record.bBild)
+ .avif({ quality: 80 })
+ .toFile(filePath);
}
}
const processed = Math.min(i + chunkSize, ids.length);
diff --git a/server.js b/server.js
new file mode 100644
index 0000000..6147a30
--- /dev/null
+++ b/server.js
@@ -0,0 +1,52 @@
+import express from 'express';
+import path from 'path';
+import { fileURLToPath } from 'url';
+import fs from 'fs/promises';
+
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = path.dirname(__filename);
+
+export function startServer() {
+ const app = express();
+ const PORT = process.env.SERVER_PORT || 3000;
+ const HOST = process.env.SERVER_HOST || '0.0.0.0';
+ const CACHE_DIR = process.env.CACHE_LOCATION || './cache';
+
+ // Serve category tree JSON
+ app.get('/api/categories', async (req, res) => {
+ try {
+ const treePath = path.join(CACHE_DIR, 'category_tree.json');
+ const data = await fs.readFile(treePath, 'utf-8');
+ res.json(JSON.parse(data));
+ } catch (err) {
+ res.status(500).json({ error: 'Failed to load category tree' });
+ }
+ });
+
+ // Serve category images
+ app.get('/img/cat/:id.avif', (req, res) => {
+ const { id } = req.params;
+ const imagePath = path.join(CACHE_DIR, 'img', 'categories', `${id}.avif`);
+ res.sendFile(path.resolve(imagePath), (err) => {
+ if (err) {
+ res.status(404).send('Image not found');
+ }
+ });
+ });
+
+ // Serve index.html
+ app.get('/', (req, res) => {
+ const htmlPath = path.join(__dirname, 'index.html');
+ console.log('Attempting to serve:', htmlPath);
+ res.sendFile(htmlPath, (err) => {
+ if (err) {
+ console.error('Error serving index.html:', err);
+ res.status(500).send('Error loading page');
+ }
+ });
+ });
+
+ app.listen(PORT, HOST, () => {
+ console.log(`🌐 Server running on http://${HOST}:${PORT}`);
+ });
+}