138 lines
6.4 KiB
JavaScript
138 lines
6.4 KiB
JavaScript
const express = require('express');
|
|
const multer = require('multer');
|
|
const path = require('path');
|
|
const fs = require('fs');
|
|
const sharp = require('sharp');
|
|
const router = express.Router();
|
|
const { getDb } = require('../models/db');
|
|
const { authMiddleware, adminMiddleware } = require('../middleware/auth');
|
|
|
|
// Generic icon storage factory
|
|
function makeIconStorage(prefix) {
|
|
return multer.diskStorage({
|
|
destination: '/app/uploads/logos',
|
|
filename: (req, file, cb) => {
|
|
const ext = path.extname(file.originalname);
|
|
cb(null, `${prefix}_${Date.now()}${ext}`);
|
|
}
|
|
});
|
|
}
|
|
|
|
const iconUploadOpts = {
|
|
limits: { fileSize: 1 * 1024 * 1024 },
|
|
fileFilter: (req, file, cb) => {
|
|
if (file.mimetype.startsWith('image/')) cb(null, true);
|
|
else cb(new Error('Images only'));
|
|
}
|
|
};
|
|
|
|
const uploadLogo = multer({ storage: makeIconStorage('logo'), ...iconUploadOpts });
|
|
const uploadNewChat = multer({ storage: makeIconStorage('newchat'), ...iconUploadOpts });
|
|
const uploadGroupInfo = multer({ storage: makeIconStorage('groupinfo'), ...iconUploadOpts });
|
|
|
|
// Get public settings (accessible by all)
|
|
router.get('/', (req, res) => {
|
|
const db = getDb();
|
|
const settings = db.prepare('SELECT key, value FROM settings').all();
|
|
const obj = {};
|
|
for (const s of settings) obj[s.key] = s.value;
|
|
const admin = db.prepare('SELECT email FROM users WHERE is_default_admin = 1').get();
|
|
if (admin) obj.admin_email = admin.email;
|
|
// Expose app version from Docker build arg env var
|
|
obj.app_version = process.env.JAMA_VERSION || process.env.TEAMCHAT_VERSION || 'dev';
|
|
obj.user_pass = process.env.USER_PASS || 'user@1234';
|
|
res.json({ settings: obj });
|
|
});
|
|
|
|
// Update app name (admin)
|
|
router.patch('/app-name', authMiddleware, adminMiddleware, (req, res) => {
|
|
const { name } = req.body;
|
|
if (!name?.trim()) return res.status(400).json({ error: 'Name required' });
|
|
const db = getDb();
|
|
db.prepare("UPDATE settings SET value = ?, updated_at = datetime('now') WHERE key = 'app_name'").run(name.trim());
|
|
res.json({ success: true, name: name.trim() });
|
|
});
|
|
|
|
// Upload app logo (admin) — also generates 192x192 and 512x512 PWA icons
|
|
router.post('/logo', authMiddleware, adminMiddleware, uploadLogo.single('logo'), async (req, res) => {
|
|
if (!req.file) return res.status(400).json({ error: 'No file' });
|
|
|
|
const logoUrl = `/uploads/logos/${req.file.filename}`;
|
|
const srcPath = req.file.path;
|
|
|
|
try {
|
|
// Generate PWA icons from the uploaded logo
|
|
const icon192Path = '/app/uploads/logos/pwa-icon-192.png';
|
|
const icon512Path = '/app/uploads/logos/pwa-icon-512.png';
|
|
|
|
await sharp(srcPath)
|
|
.resize(192, 192, { fit: 'contain', background: { r: 255, g: 255, b: 255, alpha: 0 } })
|
|
.png()
|
|
.toFile(icon192Path);
|
|
|
|
await sharp(srcPath)
|
|
.resize(512, 512, { fit: 'contain', background: { r: 255, g: 255, b: 255, alpha: 0 } })
|
|
.png()
|
|
.toFile(icon512Path);
|
|
|
|
const db = getDb();
|
|
db.prepare("UPDATE settings SET value = ?, updated_at = datetime('now') WHERE key = 'logo_url'").run(logoUrl);
|
|
// Store the PWA icon paths so the manifest can reference them
|
|
db.prepare("INSERT INTO settings (key, value) VALUES ('pwa_icon_192', ?) ON CONFLICT(key) DO UPDATE SET value = ?, updated_at = datetime('now')")
|
|
.run('/uploads/logos/pwa-icon-192.png', '/uploads/logos/pwa-icon-192.png');
|
|
db.prepare("INSERT INTO settings (key, value) VALUES ('pwa_icon_512', ?) ON CONFLICT(key) DO UPDATE SET value = ?, updated_at = datetime('now')")
|
|
.run('/uploads/logos/pwa-icon-512.png', '/uploads/logos/pwa-icon-512.png');
|
|
|
|
res.json({ logoUrl });
|
|
} catch (err) {
|
|
console.error('[Logo] Failed to generate PWA icons:', err.message);
|
|
// Still save the logo even if icon generation fails
|
|
const db = getDb();
|
|
db.prepare("UPDATE settings SET value = ?, updated_at = datetime('now') WHERE key = 'logo_url'").run(logoUrl);
|
|
res.json({ logoUrl });
|
|
}
|
|
});
|
|
|
|
// Upload New Chat icon (admin)
|
|
router.post('/icon-newchat', authMiddleware, adminMiddleware, uploadNewChat.single('icon'), (req, res) => {
|
|
if (!req.file) return res.status(400).json({ error: 'No file' });
|
|
const iconUrl = `/uploads/logos/${req.file.filename}`;
|
|
const db = getDb();
|
|
db.prepare("INSERT INTO settings (key, value) VALUES ('icon_newchat', ?) ON CONFLICT(key) DO UPDATE SET value = ?, updated_at = datetime('now')")
|
|
.run(iconUrl, iconUrl);
|
|
res.json({ iconUrl });
|
|
});
|
|
|
|
// Upload Group Info icon (admin)
|
|
router.post('/icon-groupinfo', authMiddleware, adminMiddleware, uploadGroupInfo.single('icon'), (req, res) => {
|
|
if (!req.file) return res.status(400).json({ error: 'No file' });
|
|
const iconUrl = `/uploads/logos/${req.file.filename}`;
|
|
const db = getDb();
|
|
db.prepare("INSERT INTO settings (key, value) VALUES ('icon_groupinfo', ?) ON CONFLICT(key) DO UPDATE SET value = ?, updated_at = datetime('now')")
|
|
.run(iconUrl, iconUrl);
|
|
res.json({ iconUrl });
|
|
});
|
|
|
|
// Reset all settings to defaults (admin)
|
|
router.patch('/colors', authMiddleware, adminMiddleware, (req, res) => {
|
|
const { colorTitle, colorTitleDark, colorAvatarPublic, colorAvatarDm } = req.body;
|
|
const db = getDb();
|
|
const upd = db.prepare("INSERT INTO settings (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = ?, updated_at = datetime('now')");
|
|
if (colorTitle !== undefined) upd.run('color_title', colorTitle || '', colorTitle || '');
|
|
if (colorTitleDark !== undefined) upd.run('color_title_dark', colorTitleDark || '', colorTitleDark || '');
|
|
if (colorAvatarPublic !== undefined) upd.run('color_avatar_public', colorAvatarPublic || '', colorAvatarPublic || '');
|
|
if (colorAvatarDm !== undefined) upd.run('color_avatar_dm', colorAvatarDm || '', colorAvatarDm || '');
|
|
res.json({ success: true });
|
|
});
|
|
|
|
router.post('/reset', authMiddleware, adminMiddleware, (req, res) => {
|
|
const db = getDb();
|
|
const originalName = process.env.APP_NAME || 'jama';
|
|
db.prepare("UPDATE settings SET value = ?, updated_at = datetime('now') WHERE key = 'app_name'").run(originalName);
|
|
db.prepare("UPDATE settings SET value = '', updated_at = datetime('now') WHERE key = 'logo_url'").run();
|
|
db.prepare("UPDATE settings SET value = '', updated_at = datetime('now') WHERE key IN ('icon_newchat', 'icon_groupinfo', 'pwa_icon_192', 'pwa_icon_512', 'color_title', 'color_title_dark', 'color_avatar_public', 'color_avatar_dm')").run();
|
|
res.json({ success: true });
|
|
});
|
|
|
|
module.exports = router;
|