🌊 TeamFlow — Modern Trello alternative with email integration
- Full-stack: React 18 + Express + SQLite - Drag-and-drop kanban boards with @hello-pangea/dnd - Google App Password email integration (SMTP + IMAP) - Inbound email: create cards by sending emails - Reply-to-card: email replies become comments - Admin/user management with role-based access - Setup wizard: email config → admin creation - Checklists, time tracking, priorities, labels, due dates - Real-time notifications with activity feed - Beautiful HTML email templates
This commit is contained in:
83
server/routes/setup.js
Normal file
83
server/routes/setup.js
Normal file
@@ -0,0 +1,83 @@
|
||||
import { Router } from 'express';
|
||||
import bcrypt from 'bcryptjs';
|
||||
import db from '../db.js';
|
||||
import { generateToken } from '../middleware/auth.js';
|
||||
import { testConnection, initTransporter } from '../email/transporter.js';
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.get('/status', (req, res) => {
|
||||
const hasEmail = !!db.prepare('SELECT 1 FROM email_config WHERE id = 1').get();
|
||||
const userCount = db.prepare('SELECT COUNT(*) as c FROM users').get().c;
|
||||
const setupComplete = hasEmail && userCount > 0;
|
||||
res.json({ hasEmail, userCount, setupComplete });
|
||||
});
|
||||
|
||||
router.post('/email', async (req, res) => {
|
||||
try {
|
||||
const { smtp_host, smtp_port, email, app_password } = req.body;
|
||||
if (!email || !app_password) return res.status(400).json({ error: 'Email and app password required' });
|
||||
const host = smtp_host || 'smtp.gmail.com';
|
||||
const port = parseInt(smtp_port) || 587;
|
||||
await testConnection(host, port, email, app_password);
|
||||
db.prepare(`INSERT OR REPLACE INTO email_config (id, smtp_host, smtp_port, email, app_password)
|
||||
VALUES (1, ?, ?, ?, ?)`).run(host, port, email, app_password);
|
||||
await initTransporter();
|
||||
res.json({ success: true });
|
||||
} catch (err) {
|
||||
res.status(400).json({ error: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/email/test', async (req, res) => {
|
||||
try {
|
||||
const { to } = req.body;
|
||||
const config = db.prepare('SELECT * FROM email_config WHERE id = 1').get();
|
||||
if (!config) return res.status(400).json({ error: 'Email not configured' });
|
||||
const { sendMail } = await import('../email/transporter.js');
|
||||
await sendMail({
|
||||
to,
|
||||
subject: 'TeamFlow — Test Email',
|
||||
html: `<div style="padding:32px;font-family:sans-serif">
|
||||
<h2 style="color:#6366f1">✅ TeamFlow Email Working!</h2>
|
||||
<p>Your email integration is configured correctly.</p>
|
||||
</div>`,
|
||||
});
|
||||
res.json({ success: true });
|
||||
} catch (err) {
|
||||
res.status(400).json({ error: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/admin', async (req, res) => {
|
||||
const hasEmail = !!db.prepare('SELECT 1 FROM email_config WHERE id = 1').get();
|
||||
if (!hasEmail) return res.status(400).json({ error: 'Configure email first' });
|
||||
const existing = db.prepare('SELECT COUNT(*) as c FROM users').get();
|
||||
if (existing.c > 0) return res.status(400).json({ error: 'Admin already exists' });
|
||||
const { email, name, password } = req.body;
|
||||
if (!email || !name || !password) return res.status(400).json({ error: 'All fields required' });
|
||||
if (password.length < 6) return res.status(400).json({ error: 'Password must be at least 6 characters' });
|
||||
const hash = await bcrypt.hash(password, 12);
|
||||
const result = db.prepare('INSERT INTO users (email, name, password, role) VALUES (?, ?, ?, ?)').run(email, name, hash);
|
||||
const user = db.prepare('SELECT id, email, name, avatar_color, role FROM users WHERE id = ?').get(result.lastInsertRowid);
|
||||
const token = generateToken(user);
|
||||
res.json({ user, token });
|
||||
});
|
||||
|
||||
router.put('/email/inbound', async (req, res) => {
|
||||
const config = db.prepare('SELECT * FROM email_config WHERE id = 1').get();
|
||||
if (!config) return res.status(400).json({ error: 'Email not configured' });
|
||||
const { enabled, folder, prefix } = req.body;
|
||||
db.prepare(`UPDATE email_config SET inbound_enabled = ?, inbound_folder = ?, board_email_prefix = ? WHERE id = 1`)
|
||||
.run(enabled ? 1 : 0, folder || 'INBOX', prefix || 'tf-');
|
||||
if (enabled) {
|
||||
const { startImapPolling } = await import('../email/imap.js');
|
||||
startImapPolling();
|
||||
} else {
|
||||
const { stopImapPolling } = await import('../email/imap.js');
|
||||
stopImapPolling();
|
||||
}
|
||||
res.json({ success: true });
|
||||
});
|
||||
|
||||
export default router;
|
||||
Reference in New Issue
Block a user