439 Zeilen
12 KiB
JavaScript
439 Zeilen
12 KiB
JavaScript
/**
|
|
* TASKMATE - Contact Routes
|
|
* =========================
|
|
* CRUD für Kontakte
|
|
*/
|
|
|
|
const express = require('express');
|
|
const router = express.Router();
|
|
const { getDb } = require('../database');
|
|
const logger = require('../utils/logger');
|
|
const { validators } = require('../middleware/validation');
|
|
|
|
/**
|
|
* GET /api/contacts
|
|
* Alle Kontakte abrufen mit optionalem Filter
|
|
*/
|
|
router.get('/', (req, res) => {
|
|
try {
|
|
const db = getDb();
|
|
const { search, tag, sortBy = 'created_at', sortOrder = 'desc' } = req.query;
|
|
|
|
let query = `
|
|
SELECT c.*, u.display_name as creator_name
|
|
FROM contacts c
|
|
LEFT JOIN users u ON c.created_by = u.id
|
|
WHERE 1=1
|
|
`;
|
|
const params = [];
|
|
|
|
// Suchfilter
|
|
if (search) {
|
|
query += ` AND (
|
|
c.first_name LIKE ? OR
|
|
c.last_name LIKE ? OR
|
|
c.company LIKE ? OR
|
|
c.email LIKE ? OR
|
|
c.phone LIKE ? OR
|
|
c.mobile LIKE ?
|
|
)`;
|
|
const searchParam = `%${search}%`;
|
|
params.push(searchParam, searchParam, searchParam, searchParam, searchParam, searchParam);
|
|
}
|
|
|
|
// Tag-Filter
|
|
if (tag) {
|
|
query += ` AND c.tags LIKE ?`;
|
|
params.push(`%${tag}%`);
|
|
}
|
|
|
|
// Sortierung
|
|
const validSortFields = ['first_name', 'last_name', 'company', 'created_at', 'updated_at'];
|
|
const sortField = validSortFields.includes(sortBy) ? sortBy : 'created_at';
|
|
const order = sortOrder.toLowerCase() === 'asc' ? 'ASC' : 'DESC';
|
|
query += ` ORDER BY c.${sortField} ${order}`;
|
|
|
|
const contacts = db.prepare(query).all(params);
|
|
|
|
res.json(contacts.map(c => ({
|
|
id: c.id,
|
|
firstName: c.first_name,
|
|
lastName: c.last_name,
|
|
company: c.company,
|
|
position: c.position,
|
|
email: c.email,
|
|
phone: c.phone,
|
|
mobile: c.mobile,
|
|
address: c.address,
|
|
postalCode: c.postal_code,
|
|
city: c.city,
|
|
country: c.country,
|
|
website: c.website,
|
|
notes: c.notes,
|
|
tags: c.tags ? c.tags.split(',').map(t => t.trim()) : [],
|
|
createdAt: c.created_at,
|
|
updatedAt: c.updated_at,
|
|
createdBy: c.created_by,
|
|
creatorName: c.creator_name
|
|
})));
|
|
} catch (error) {
|
|
logger.error('Fehler beim Abrufen der Kontakte:', { error: error.message });
|
|
res.status(500).json({ error: 'Interner Serverfehler' });
|
|
}
|
|
});
|
|
|
|
/**
|
|
* GET /api/contacts/:id
|
|
* Einzelnen Kontakt abrufen
|
|
*/
|
|
router.get('/:id', (req, res) => {
|
|
try {
|
|
const db = getDb();
|
|
const contactId = req.params.id;
|
|
|
|
const contact = db.prepare(`
|
|
SELECT c.*, u.display_name as creator_name
|
|
FROM contacts c
|
|
LEFT JOIN users u ON c.created_by = u.id
|
|
WHERE c.id = ?
|
|
`).get(contactId);
|
|
|
|
if (!contact) {
|
|
return res.status(404).json({ error: 'Kontakt nicht gefunden' });
|
|
}
|
|
|
|
res.json({
|
|
id: contact.id,
|
|
firstName: contact.first_name,
|
|
lastName: contact.last_name,
|
|
company: contact.company,
|
|
position: contact.position,
|
|
email: contact.email,
|
|
phone: contact.phone,
|
|
mobile: contact.mobile,
|
|
address: contact.address,
|
|
postalCode: contact.postal_code,
|
|
city: contact.city,
|
|
country: contact.country,
|
|
website: contact.website,
|
|
notes: contact.notes,
|
|
tags: contact.tags ? contact.tags.split(',').map(t => t.trim()) : [],
|
|
createdAt: contact.created_at,
|
|
updatedAt: contact.updated_at,
|
|
createdBy: contact.created_by,
|
|
creatorName: contact.creator_name
|
|
});
|
|
} catch (error) {
|
|
logger.error('Fehler beim Abrufen des Kontakts:', { error: error.message, contactId: req.params.id });
|
|
res.status(500).json({ error: 'Interner Serverfehler' });
|
|
}
|
|
});
|
|
|
|
/**
|
|
* POST /api/contacts
|
|
* Neuen Kontakt erstellen
|
|
*/
|
|
router.post('/', validators.contact, (req, res) => {
|
|
try {
|
|
const db = getDb();
|
|
const userId = req.user.id;
|
|
const {
|
|
firstName,
|
|
lastName,
|
|
company,
|
|
position,
|
|
email,
|
|
phone,
|
|
mobile,
|
|
address,
|
|
postalCode,
|
|
city,
|
|
country,
|
|
website,
|
|
notes,
|
|
tags
|
|
} = req.body;
|
|
|
|
const result = db.prepare(`
|
|
INSERT INTO contacts (
|
|
first_name, last_name, company, position,
|
|
email, phone, mobile, address, postal_code,
|
|
city, country, website, notes, tags,
|
|
created_by
|
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
`).run(
|
|
firstName || null,
|
|
lastName || null,
|
|
company || null,
|
|
position || null,
|
|
email || null,
|
|
phone || null,
|
|
mobile || null,
|
|
address || null,
|
|
postalCode || null,
|
|
city || null,
|
|
country || null,
|
|
website || null,
|
|
notes || null,
|
|
Array.isArray(tags) ? tags.join(', ') : null,
|
|
userId
|
|
);
|
|
|
|
const newContact = db.prepare(`
|
|
SELECT c.*, u.display_name as creator_name
|
|
FROM contacts c
|
|
LEFT JOIN users u ON c.created_by = u.id
|
|
WHERE c.id = ?
|
|
`).get(result.lastInsertRowid);
|
|
|
|
// Socket.io Event
|
|
const io = req.app.get('io');
|
|
io.emit('contact:created', {
|
|
contact: {
|
|
id: newContact.id,
|
|
firstName: newContact.first_name,
|
|
lastName: newContact.last_name,
|
|
company: newContact.company,
|
|
position: newContact.position,
|
|
email: newContact.email,
|
|
phone: newContact.phone,
|
|
mobile: newContact.mobile,
|
|
address: newContact.address,
|
|
postalCode: newContact.postal_code,
|
|
city: newContact.city,
|
|
country: newContact.country,
|
|
website: newContact.website,
|
|
notes: newContact.notes,
|
|
tags: newContact.tags ? newContact.tags.split(',').map(t => t.trim()) : [],
|
|
createdAt: newContact.created_at,
|
|
updatedAt: newContact.updated_at,
|
|
createdBy: newContact.created_by,
|
|
creatorName: newContact.creator_name
|
|
},
|
|
userId
|
|
});
|
|
|
|
res.status(201).json({
|
|
id: newContact.id,
|
|
firstName: newContact.first_name,
|
|
lastName: newContact.last_name,
|
|
company: newContact.company,
|
|
position: newContact.position,
|
|
email: newContact.email,
|
|
phone: newContact.phone,
|
|
mobile: newContact.mobile,
|
|
address: newContact.address,
|
|
postalCode: newContact.postal_code,
|
|
city: newContact.city,
|
|
country: newContact.country,
|
|
website: newContact.website,
|
|
notes: newContact.notes,
|
|
tags: newContact.tags ? newContact.tags.split(',').map(t => t.trim()) : [],
|
|
createdAt: newContact.created_at,
|
|
updatedAt: newContact.updated_at,
|
|
createdBy: newContact.created_by,
|
|
creatorName: newContact.creator_name
|
|
});
|
|
|
|
logger.info('Kontakt erstellt', { contactId: newContact.id, userId });
|
|
} catch (error) {
|
|
logger.error('Fehler beim Erstellen des Kontakts:', { error: error.message, body: req.body });
|
|
res.status(500).json({ error: 'Interner Serverfehler' });
|
|
}
|
|
});
|
|
|
|
/**
|
|
* PUT /api/contacts/:id
|
|
* Kontakt aktualisieren
|
|
*/
|
|
router.put('/:id', validators.contact, (req, res) => {
|
|
try {
|
|
const db = getDb();
|
|
const contactId = req.params.id;
|
|
const userId = req.user.id;
|
|
const {
|
|
firstName,
|
|
lastName,
|
|
company,
|
|
position,
|
|
email,
|
|
phone,
|
|
mobile,
|
|
address,
|
|
postalCode,
|
|
city,
|
|
country,
|
|
website,
|
|
notes,
|
|
tags
|
|
} = req.body;
|
|
|
|
// Prüfen ob Kontakt existiert
|
|
const existing = db.prepare('SELECT id FROM contacts WHERE id = ?').get(contactId);
|
|
if (!existing) {
|
|
return res.status(404).json({ error: 'Kontakt nicht gefunden' });
|
|
}
|
|
|
|
// Update
|
|
db.prepare(`
|
|
UPDATE contacts SET
|
|
first_name = ?,
|
|
last_name = ?,
|
|
company = ?,
|
|
position = ?,
|
|
email = ?,
|
|
phone = ?,
|
|
mobile = ?,
|
|
address = ?,
|
|
postal_code = ?,
|
|
city = ?,
|
|
country = ?,
|
|
website = ?,
|
|
notes = ?,
|
|
tags = ?,
|
|
updated_at = CURRENT_TIMESTAMP
|
|
WHERE id = ?
|
|
`).run(
|
|
firstName || null,
|
|
lastName || null,
|
|
company || null,
|
|
position || null,
|
|
email || null,
|
|
phone || null,
|
|
mobile || null,
|
|
address || null,
|
|
postalCode || null,
|
|
city || null,
|
|
country || null,
|
|
website || null,
|
|
notes || null,
|
|
Array.isArray(tags) ? tags.join(', ') : null,
|
|
contactId
|
|
);
|
|
|
|
const updatedContact = db.prepare(`
|
|
SELECT c.*, u.display_name as creator_name
|
|
FROM contacts c
|
|
LEFT JOIN users u ON c.created_by = u.id
|
|
WHERE c.id = ?
|
|
`).get(contactId);
|
|
|
|
// Socket.io Event
|
|
const io = req.app.get('io');
|
|
io.emit('contact:updated', {
|
|
contact: {
|
|
id: updatedContact.id,
|
|
firstName: updatedContact.first_name,
|
|
lastName: updatedContact.last_name,
|
|
company: updatedContact.company,
|
|
position: updatedContact.position,
|
|
email: updatedContact.email,
|
|
phone: updatedContact.phone,
|
|
mobile: updatedContact.mobile,
|
|
address: updatedContact.address,
|
|
postalCode: updatedContact.postal_code,
|
|
city: updatedContact.city,
|
|
country: updatedContact.country,
|
|
website: updatedContact.website,
|
|
notes: updatedContact.notes,
|
|
tags: updatedContact.tags ? updatedContact.tags.split(',').map(t => t.trim()) : [],
|
|
createdAt: updatedContact.created_at,
|
|
updatedAt: updatedContact.updated_at,
|
|
createdBy: updatedContact.created_by,
|
|
creatorName: updatedContact.creator_name
|
|
},
|
|
userId
|
|
});
|
|
|
|
res.json({
|
|
id: updatedContact.id,
|
|
firstName: updatedContact.first_name,
|
|
lastName: updatedContact.last_name,
|
|
company: updatedContact.company,
|
|
position: updatedContact.position,
|
|
email: updatedContact.email,
|
|
phone: updatedContact.phone,
|
|
mobile: updatedContact.mobile,
|
|
address: updatedContact.address,
|
|
postalCode: updatedContact.postal_code,
|
|
city: updatedContact.city,
|
|
country: updatedContact.country,
|
|
website: updatedContact.website,
|
|
notes: updatedContact.notes,
|
|
tags: updatedContact.tags ? updatedContact.tags.split(',').map(t => t.trim()) : [],
|
|
createdAt: updatedContact.created_at,
|
|
updatedAt: updatedContact.updated_at,
|
|
createdBy: updatedContact.created_by,
|
|
creatorName: updatedContact.creator_name
|
|
});
|
|
|
|
logger.info('Kontakt aktualisiert', { contactId, userId });
|
|
} catch (error) {
|
|
logger.error('Fehler beim Aktualisieren des Kontakts:', { error: error.message, contactId: req.params.id });
|
|
res.status(500).json({ error: 'Interner Serverfehler' });
|
|
}
|
|
});
|
|
|
|
/**
|
|
* DELETE /api/contacts/:id
|
|
* Kontakt löschen
|
|
*/
|
|
router.delete('/:id', (req, res) => {
|
|
try {
|
|
const db = getDb();
|
|
const contactId = req.params.id;
|
|
const userId = req.user.id;
|
|
|
|
// Prüfen ob Kontakt existiert
|
|
const existing = db.prepare('SELECT id FROM contacts WHERE id = ?').get(contactId);
|
|
if (!existing) {
|
|
return res.status(404).json({ error: 'Kontakt nicht gefunden' });
|
|
}
|
|
|
|
// Löschen
|
|
db.prepare('DELETE FROM contacts WHERE id = ?').run(contactId);
|
|
|
|
// Socket.io Event
|
|
const io = req.app.get('io');
|
|
io.emit('contact:deleted', { contactId, userId });
|
|
|
|
res.json({ success: true });
|
|
|
|
logger.info('Kontakt gelöscht', { contactId, userId });
|
|
} catch (error) {
|
|
logger.error('Fehler beim Löschen des Kontakts:', { error: error.message, contactId: req.params.id });
|
|
res.status(500).json({ error: 'Interner Serverfehler' });
|
|
}
|
|
});
|
|
|
|
/**
|
|
* GET /api/contacts/tags
|
|
* Alle verwendeten Tags abrufen
|
|
*/
|
|
router.get('/tags/all', (req, res) => {
|
|
try {
|
|
const db = getDb();
|
|
|
|
const contacts = db.prepare('SELECT DISTINCT tags FROM contacts WHERE tags IS NOT NULL').all();
|
|
|
|
// Alle Tags sammeln und deduplizieren
|
|
const allTags = new Set();
|
|
contacts.forEach(contact => {
|
|
if (contact.tags) {
|
|
contact.tags.split(',').forEach(tag => {
|
|
const trimmedTag = tag.trim();
|
|
if (trimmedTag) {
|
|
allTags.add(trimmedTag);
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
res.json(Array.from(allTags).sort());
|
|
} catch (error) {
|
|
logger.error('Fehler beim Abrufen der Tags:', { error: error.message });
|
|
res.status(500).json({ error: 'Interner Serverfehler' });
|
|
}
|
|
});
|
|
|
|
module.exports = router; |