diff --git a/check-tables.js b/check-tables.js deleted file mode 100644 index b50b29f..0000000 --- a/check-tables.js +++ /dev/null @@ -1,24 +0,0 @@ -import pkg from 'pg'; -const { Pool } = pkg; -import dotenv from 'dotenv'; -dotenv.config(); - -const pool = new Pool({ - host: process.env.DB_HOST || 'localhost', - port: parseInt(process.env.DB_PORT || '5432'), - user: process.env.DB_USER || 'admin', - password: process.env.DB_PASSWORD || 'heslo123', - database: process.env.DB_NAME || 'crm', -}); - -const query = "SELECT tablename FROM pg_tables WHERE schemaname = 'public';"; -pool.query(query).then(res => { - console.log('Tabuľky v databáze:'); - res.rows.forEach(row => console.log(' -', row.tablename)); - pool.end(); - process.exit(0); -}).catch(err => { - console.error('Chyba:', err.message); - pool.end(); - process.exit(1); -}); diff --git a/scripts/encrypt-password.js b/scripts/encrypt-password.js deleted file mode 100644 index 9a3d1aa..0000000 --- a/scripts/encrypt-password.js +++ /dev/null @@ -1,25 +0,0 @@ -import crypto from 'crypto'; - -const encryptPassword = (text) => { - const algorithm = 'aes-256-gcm'; - const key = crypto.scryptSync(process.env.JWT_SECRET || 'your-super-secret-jwt-key-change-this-in-production', 'salt', 32); - const iv = crypto.randomBytes(16); - - const cipher = crypto.createCipheriv(algorithm, key, iv); - let encrypted = cipher.update(text, 'utf8', 'hex'); - encrypted += cipher.final('hex'); - - const authTag = cipher.getAuthTag(); - - return `${iv.toString('hex')}:${authTag.toString('hex')}:${encrypted}`; -}; - -// Usage: node scripts/encrypt-password.js YOUR_PASSWORD -const password = process.argv[2]; -if (!password) { - console.error('Usage: node scripts/encrypt-password.js YOUR_PASSWORD'); - process.exit(1); -} - -console.log('Encrypted password:'); -console.log(encryptPassword(password)); diff --git a/src/config/database.js b/src/config/database.js index 6be13e8..0e068a0 100644 --- a/src/config/database.js +++ b/src/config/database.js @@ -15,13 +15,9 @@ const pool = new Pool({ connectionTimeoutMillis: 2000, }); -// Test database connection -pool.on('connect', () => { - console.log('✅ Database connected successfully'); -}); - +// Note: Connection logging handled in index.js to avoid circular dependencies pool.on('error', (err) => { - console.error('❌ Unexpected database error:', err); + console.error('Unexpected database error:', err); process.exit(-1); }); diff --git a/src/controllers/admin.controller.js b/src/controllers/admin.controller.js index 24de922..3ad0477 100644 --- a/src/controllers/admin.controller.js +++ b/src/controllers/admin.controller.js @@ -128,7 +128,7 @@ export const getAllUsers = async (req, res) => { * Získanie konkrétneho usera (admin only) * GET /api/admin/users/:userId */ -export const getUserById = async (req, res) => { +export const getUser = async (req, res) => { const { userId } = req.params; try { diff --git a/src/controllers/contact.controller.js b/src/controllers/contact.controller.js index 9a74d1e..2cce4a5 100644 --- a/src/controllers/contact.controller.js +++ b/src/controllers/contact.controller.js @@ -2,6 +2,7 @@ import * as contactService from '../services/contact.service.js'; import { discoverContactsFromJMAP, getJmapConfigFromAccount } from '../services/jmap.service.js'; import { formatErrorResponse } from '../utils/errors.js'; import * as emailAccountService from '../services/email-account.service.js'; +import { logger } from '../utils/logger.js'; /** * Get all contacts for authenticated user @@ -34,18 +35,18 @@ export const discoverContacts = async (req, res) => { const userId = req.userId; const { accountId, search = '', limit = 50 } = req.query; - console.log('🔍 discoverContacts called:', { userId, accountId, search, limit }); + logger.debug('discoverContacts called', { userId, accountId, search, limit }); // Get email account (or primary if not specified) let emailAccount; if (accountId) { - console.log('📧 Getting email account by ID:', accountId); + logger.debug('Getting email account by ID', { accountId }); emailAccount = await emailAccountService.getEmailAccountWithCredentials(accountId, userId); - console.log('✅ Email account retrieved:', { id: emailAccount.id, email: emailAccount.email }); + logger.debug('Email account retrieved', { id: emailAccount.id, email: emailAccount.email }); } else { - console.log('📧 No accountId provided, getting primary account for user:', userId); + logger.debug('No accountId provided, getting primary account', { userId }); const primaryAccount = await emailAccountService.getPrimaryEmailAccount(userId); - console.log('🔑 Primary account:', primaryAccount ? { id: primaryAccount.id, email: primaryAccount.email } : 'NOT FOUND'); + logger.debug('Primary account', primaryAccount ? { id: primaryAccount.id, email: primaryAccount.email } : { found: false }); if (!primaryAccount) { return res.status(400).json({ success: false, @@ -56,11 +57,11 @@ export const discoverContacts = async (req, res) => { }); } emailAccount = await emailAccountService.getEmailAccountWithCredentials(primaryAccount.id, userId); - console.log('✅ Email account retrieved from primary:', { id: emailAccount.id, email: emailAccount.email }); + logger.debug('Email account retrieved from primary', { id: emailAccount.id, email: emailAccount.email }); } const jmapConfig = getJmapConfigFromAccount(emailAccount); - console.log('🔧 JMAP Config created:', { + logger.debug('JMAP Config created', { server: jmapConfig.server, username: jmapConfig.username, accountId: jmapConfig.accountId, @@ -80,8 +81,7 @@ export const discoverContacts = async (req, res) => { data: potentialContacts, }); } catch (error) { - console.error('❌ ERROR in discoverContacts:', error); - console.error('Error stack:', error.stack); + logger.error('ERROR in discoverContacts', { error: error.message, stack: error.stack }); const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); res.status(error.statusCode || 500).json(errorResponse); } @@ -95,10 +95,10 @@ export const discoverContacts = async (req, res) => { export const addContact = async (req, res) => { try { const userId = req.userId; - console.log('📦 Full req.body:', JSON.stringify(req.body, null, 2)); + logger.debug('Full req.body', { body: req.body }); const { email, name = '', notes = '', accountId } = req.body; - console.log('➕ addContact called:', { userId, email, name, accountId }); + logger.debug('addContact called', { userId, email, name, accountId }); if (!email) { return res.status(400).json({ @@ -113,10 +113,10 @@ export const addContact = async (req, res) => { // Get email account (or primary if not specified) let emailAccount; if (accountId) { - console.log('📧 Using provided accountId:', accountId); + logger.debug('Using provided accountId', { accountId }); emailAccount = await emailAccountService.getEmailAccountWithCredentials(accountId, userId); } else { - console.log('📧 No accountId provided, using primary account'); + logger.debug('No accountId provided, using primary account'); const primaryAccount = await emailAccountService.getPrimaryEmailAccount(userId); if (!primaryAccount) { return res.status(400).json({ @@ -128,7 +128,7 @@ export const addContact = async (req, res) => { }); } emailAccount = await emailAccountService.getEmailAccountWithCredentials(primaryAccount.id, userId); - console.log('📧 Using primary account:', primaryAccount.id); + logger.debug('Using primary account', { accountId: primaryAccount.id }); } const jmapConfig = getJmapConfigFromAccount(emailAccount); diff --git a/src/controllers/crm-email.controller.js b/src/controllers/crm-email.controller.js index 15b2399..38c1115 100644 --- a/src/controllers/crm-email.controller.js +++ b/src/controllers/crm-email.controller.js @@ -4,6 +4,7 @@ import * as emailAccountService from '../services/email-account.service.js'; import { markEmailAsRead, sendEmail, getJmapConfig, getJmapConfigFromAccount, syncEmailsFromSender, searchEmailsJMAP as searchEmailsJMAPService } from '../services/jmap.service.js'; import { formatErrorResponse } from '../utils/errors.js'; import { getUserById } from '../services/auth.service.js'; +import { logger } from '../utils/logger.js'; /** * Get all emails for authenticated user @@ -91,7 +92,7 @@ export const getUnreadCount = async (req, res) => { }, }); } catch (error) { - console.error('❌ ERROR in getUnreadCount:', error); + logger.error('ERROR in getUnreadCount', { error: error.message }); const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); res.status(error.statusCode || 500).json(errorResponse); } @@ -153,7 +154,7 @@ export const syncEmails = async (req, res) => { totalSynced += total; totalNew += saved; } catch (syncError) { - console.error(`Failed to sync emails for contact ${contact.email}`, syncError); + logger.error('Failed to sync emails for contact', { contactEmail: contact.email, error: syncError.message }); } } @@ -198,6 +199,29 @@ export const markAsRead = async (req, res) => { } }; +/** + * Mark all emails from contact as read + * POST /api/emails/contact/:contactId/read + */ +export const markContactEmailsRead = async (req, res) => { + try { + const userId = req.userId; + const { contactId } = req.params; + + const result = await crmEmailService.markContactEmailsAsRead(userId, contactId); + + res.status(200).json({ + success: true, + message: `Označených ${result.count} emailov ako prečítaných`, + data: result, + }); + } catch (error) { + logger.error('ERROR in markContactEmailsRead', { error: error.message }); + const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); + res.status(error.statusCode || 500).json(errorResponse); + } +}; + /** * Mark entire thread as read * POST /api/emails/thread/:threadId/read @@ -224,7 +248,7 @@ export const markThreadRead = async (req, res) => { try { await markEmailAsRead(jmapConfig, userId, email.jmapId, true); } catch (jmapError) { - console.error(`Failed to mark JMAP email ${email.jmapId} as read`, jmapError); + logger.error('Failed to mark JMAP email as read', { jmapId: email.jmapId, error: jmapError.message }); } } } @@ -327,15 +351,15 @@ export const searchEmailsJMAP = async (req, res) => { const userId = req.userId; const { query = '', limit = 50, offset = 0, accountId } = req.query; - console.log('🔍 searchEmailsJMAP called:', { userId, query, limit, offset, accountId }); + logger.debug('searchEmailsJMAP called', { userId, query, limit, offset, accountId }); // Get email account (or primary if not specified) let emailAccount; if (accountId) { - console.log('📧 Using provided accountId:', accountId); + logger.debug('Using provided accountId', { accountId }); emailAccount = await emailAccountService.getEmailAccountWithCredentials(accountId, userId); } else { - console.log('📧 No accountId provided, using primary account'); + logger.debug('No accountId provided, using primary account'); const primaryAccount = await emailAccountService.getPrimaryEmailAccount(userId); if (!primaryAccount) { return res.status(400).json({ @@ -347,7 +371,7 @@ export const searchEmailsJMAP = async (req, res) => { }); } emailAccount = await emailAccountService.getEmailAccountWithCredentials(primaryAccount.id, userId); - console.log('📧 Using primary account:', primaryAccount.id); + logger.debug('Using primary account', { accountId: primaryAccount.id }); } const jmapConfig = getJmapConfigFromAccount(emailAccount); @@ -366,7 +390,7 @@ export const searchEmailsJMAP = async (req, res) => { data: results, }); } catch (error) { - console.error('❌ ERROR in searchEmailsJMAP:', error); + logger.error('ERROR in searchEmailsJMAP', { error: error.message }); const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development'); res.status(error.statusCode || 500).json(errorResponse); } diff --git a/src/db/migrations/migrate-data-only.js b/src/db/migrations/archive/migrate-data-only.js similarity index 100% rename from src/db/migrations/migrate-data-only.js rename to src/db/migrations/archive/migrate-data-only.js diff --git a/src/db/migrations/migrate-to-email-accounts.js b/src/db/migrations/archive/migrate-to-email-accounts.js similarity index 100% rename from src/db/migrations/migrate-to-email-accounts.js rename to src/db/migrations/archive/migrate-to-email-accounts.js diff --git a/src/middlewares/auth/authMiddleware.js b/src/middlewares/auth/authMiddleware.js index f9e25a2..bb11c9b 100644 --- a/src/middlewares/auth/authMiddleware.js +++ b/src/middlewares/auth/authMiddleware.js @@ -57,34 +57,3 @@ export const authenticate = async (req, res, next) => { }); } }; - -/** - * Optional authentication - nepovinnné overenie - * Ak je token poskytnutý, overí ho, ale nehodí error ak nie je - */ -export const optionalAuthenticate = async (req, res, next) => { - try { - let token = null; - - const authHeader = req.headers.authorization; - if (authHeader && authHeader.startsWith('Bearer ')) { - token = authHeader.substring(7); - } - - if (!token && req.cookies && req.cookies.accessToken) { - token = req.cookies.accessToken; - } - - if (token) { - const decoded = verifyAccessToken(token); - const user = await getUserById(decoded.id); - req.user = user; - req.userId = user.id; - } - - next(); - } catch (error) { - // Ignoruj chyby, len pokračuj bez user objektu - next(); - } -}; diff --git a/src/middlewares/global/validateBody.js b/src/middlewares/global/validateBody.js index d5875d8..462b9a2 100644 --- a/src/middlewares/global/validateBody.js +++ b/src/middlewares/global/validateBody.js @@ -1,3 +1,5 @@ +import { logger } from '../../utils/logger.js'; + export function validateBody(req, res, next) { const data = JSON.stringify({ body: req.body, query: req.query, params: req.params }); const dangerousPatterns = [ @@ -10,8 +12,8 @@ export function validateBody(req, res, next) { ]; for (const pattern of dangerousPatterns) { if (pattern.test(data)) { - console.warn(`❌ Suspicious input detected: ${data}`); - return res.status(400).json({ message: '🚨 Malicious content detected in request data' }); + logger.warn('Suspicious input detected', { data: data.substring(0, 100) }); + return res.status(400).json({ message: 'Malicious content detected in request data' }); } } next(); diff --git a/src/middlewares/security/validateInput.js b/src/middlewares/security/validateInput.js index c296382..ff5ed8d 100644 --- a/src/middlewares/security/validateInput.js +++ b/src/middlewares/security/validateInput.js @@ -1,5 +1,6 @@ import { ZodError } from 'zod'; import { ValidationError } from '../../utils/errors.js'; +import { logger } from '../../utils/logger.js'; /** * Middleware na validáciu request body pomocou Zod schema @@ -34,7 +35,7 @@ export const validateBody = (schema) => { } // Log unexpected errors - console.error('Validation error:', error); + logger.error('Validation error', { error: error.message }); return res.status(400).json({ success: false, @@ -74,7 +75,7 @@ export const validateQuery = (schema) => { }); } - console.error('Query validation error:', error); + logger.error('Query validation error', { error: error.message }); return res.status(400).json({ success: false, @@ -114,7 +115,7 @@ export const validateParams = (schema) => { }); } - console.error('Params validation error:', error); + logger.error('Params validation error', { error: error.message }); return res.status(400).json({ success: false, diff --git a/src/routes/admin.routes.js b/src/routes/admin.routes.js index 2515807..80ba5c4 100644 --- a/src/routes/admin.routes.js +++ b/src/routes/admin.routes.js @@ -28,7 +28,7 @@ router.get('/users', adminController.getAllUsers); router.get( '/users/:userId', validateParams(z.object({ userId: z.string().uuid() })), - adminController.getUserById + adminController.getUser ); // Zmena role usera diff --git a/src/routes/crm-email.routes.js b/src/routes/crm-email.routes.js index 22816c7..876c3bc 100644 --- a/src/routes/crm-email.routes.js +++ b/src/routes/crm-email.routes.js @@ -49,6 +49,13 @@ router.get( crmEmailController.getContactEmails ); +// Mark all emails from contact as read +router.post( + '/contact/:contactId/read', + validateParams(z.object({ contactId: z.string().uuid() })), + crmEmailController.markContactEmailsRead +); + // Mark email as read/unread router.patch( '/:jmapId/read', diff --git a/src/services/auth.service.js b/src/services/auth.service.js index fd5c1ec..2de7eb1 100644 --- a/src/services/auth.service.js +++ b/src/services/auth.service.js @@ -85,9 +85,10 @@ export const setNewPassword = async (userId, newPassword) => { throw new NotFoundError('Používateľ nenájdený'); } - if (user.changedPassword) { - throw new ValidationError('Heslo už bolo zmenené'); - } + // Allow users to change password anytime (removed restriction) + // if (user.changedPassword) { + // throw new ValidationError('Heslo už bolo zmenené'); + // } // Hash nového hesla const hashedPassword = await hashPassword(newPassword); diff --git a/src/services/contact.service.js b/src/services/contact.service.js index 342c7f3..f33668a 100644 --- a/src/services/contact.service.js +++ b/src/services/contact.service.js @@ -3,6 +3,7 @@ import { contacts, emails } from '../db/schema.js'; import { eq, and, desc } from 'drizzle-orm'; import { NotFoundError, ConflictError } from '../utils/errors.js'; import { syncEmailsFromSender } from './jmap.service.js'; +import { logger } from '../utils/logger.js'; /** * Get all contacts for a user @@ -61,7 +62,7 @@ export const addContact = async (userId, emailAccountId, jmapConfig, email, name try { await syncEmailsFromSender(jmapConfig, userId, emailAccountId, newContact.id, email); } catch (error) { - console.error('Failed to sync emails for new contact:', error); + logger.error('Failed to sync emails for new contact', { error: error.message }); // Don't throw - contact was created successfully } diff --git a/src/services/crm-email.service.js b/src/services/crm-email.service.js index 2e40453..da8cbdf 100644 --- a/src/services/crm-email.service.js +++ b/src/services/crm-email.service.js @@ -129,12 +129,52 @@ export const getUnreadCount = async (userId) => { * Mark thread as read */ export const markThreadAsRead = async (userId, threadId) => { + console.log('🟦 markThreadAsRead called:', { userId, threadId }); + const result = await db .update(emails) .set({ isRead: true, updatedAt: new Date() }) .where(and(eq(emails.userId, userId), eq(emails.threadId, threadId), eq(emails.isRead, false))) .returning(); + console.log('✅ markThreadAsRead result:', { count: result.length, threadId }); + + return { success: true, count: result.length }; +}; + +/** + * Mark all emails from a contact as read + */ +export const markContactEmailsAsRead = async (userId, contactId) => { + console.log('🟦 markContactEmailsAsRead called:', { userId, contactId }); + + // First, check what emails exist for this contact (including already read ones) + const allContactEmails = await db + .select({ + id: emails.id, + contactId: emails.contactId, + isRead: emails.isRead, + from: emails.from, + subject: emails.subject, + }) + .from(emails) + .where(and(eq(emails.userId, userId), eq(emails.contactId, contactId))); + + console.log('📧 All emails for this contact:', { + total: allContactEmails.length, + unread: allContactEmails.filter(e => !e.isRead).length, + read: allContactEmails.filter(e => e.isRead).length, + sampleEmails: allContactEmails.slice(0, 3), + }); + + const result = await db + .update(emails) + .set({ isRead: true, updatedAt: new Date() }) + .where(and(eq(emails.userId, userId), eq(emails.contactId, contactId), eq(emails.isRead, false))) + .returning(); + + console.log('✅ markContactEmailsAsRead result:', { count: result.length, contactId }); + return { success: true, count: result.length }; }; diff --git a/src/services/email-account.service.js b/src/services/email-account.service.js index 561d853..1816505 100644 --- a/src/services/email-account.service.js +++ b/src/services/email-account.service.js @@ -9,6 +9,7 @@ import { ConflictError, AuthenticationError, } from '../utils/errors.js'; +import { logger } from '../utils/logger.js'; /** * Get all email accounts for a user @@ -200,22 +201,27 @@ export const toggleEmailAccountStatus = async (accountId, userId, isActive) => { export const setPrimaryEmailAccount = async (accountId, userId) => { const account = await getEmailAccountById(accountId, userId); - // Remove primary flag from all accounts - await db - .update(emailAccounts) - .set({ isPrimary: false, updatedAt: new Date() }) - .where(eq(emailAccounts.userId, userId)); + // Use transaction to prevent race conditions + const updated = await db.transaction(async (tx) => { + // Remove primary flag from all accounts + await tx + .update(emailAccounts) + .set({ isPrimary: false, updatedAt: new Date() }) + .where(eq(emailAccounts.userId, userId)); - // Set new primary account - const [updated] = await db - .update(emailAccounts) - .set({ - isPrimary: true, - isActive: true, // Primary account must be active - updatedAt: new Date(), - }) - .where(eq(emailAccounts.id, accountId)) - .returning(); + // Set new primary account + const [updatedAccount] = await tx + .update(emailAccounts) + .set({ + isPrimary: true, + isActive: true, // Primary account must be active + updatedAt: new Date(), + }) + .where(eq(emailAccounts.id, accountId)) + .returning(); + + return updatedAccount; + }); return { id: updated.id, @@ -260,9 +266,9 @@ export const deleteEmailAccount = async (accountId, userId) => { * Get email account with decrypted password (for JMAP operations) */ export const getEmailAccountWithCredentials = async (accountId, userId) => { - console.log('🔐 getEmailAccountWithCredentials called:', { accountId, userId }); + logger.debug('getEmailAccountWithCredentials called', { accountId, userId }); const account = await getEmailAccountById(accountId, userId); - console.log('📦 Account retrieved:', { + logger.debug('Account retrieved', { id: account.id, email: account.email, hasPassword: !!account.emailPassword, @@ -270,7 +276,7 @@ export const getEmailAccountWithCredentials = async (accountId, userId) => { }); const decryptedPassword = decryptPassword(account.emailPassword); - console.log('🔓 Password decrypted, length:', decryptedPassword?.length); + logger.debug('Password decrypted', { passwordLength: decryptedPassword?.length }); return { id: account.id, diff --git a/src/services/email.service.js b/src/services/email.service.js index 0fa0974..6dbb5a1 100644 --- a/src/services/email.service.js +++ b/src/services/email.service.js @@ -1,31 +1,6 @@ import axios from 'axios'; import { logger } from '../utils/logger.js'; -const JMAP_CONFIG = { - server: process.env.JMAP_SERVER || 'https://mail.truemail.sk/jmap/', - username: process.env.JMAP_USERNAME || 'info1_test@truemail.sk', - password: process.env.JMAP_PASSWORD || 'info1', - accountId: process.env.JMAP_ACCOUNT_ID || 'ba', -}; - -/** - * Získa JMAP session - */ -const getJmapSession = async () => { - try { - const response = await axios.get(`${JMAP_CONFIG.server}session`, { - auth: { - username: JMAP_CONFIG.username, - password: JMAP_CONFIG.password, - }, - }); - return response.data; - } catch (error) { - logger.error('Failed to get JMAP session', error); - throw new Error('Email service nedostupný'); - } -}; - /** * Validuje JMAP credentials a vráti account ID * @param {string} email - Email address @@ -33,8 +8,14 @@ const getJmapSession = async () => { * @returns {Promise<{accountId: string, session: object}>} */ export const validateJmapCredentials = async (email, password) => { + const jmapServer = process.env.JMAP_SERVER; + + if (!jmapServer) { + throw new Error('JMAP_SERVER environment variable is not configured'); + } + try { - const response = await axios.get(`${JMAP_CONFIG.server}session`, { + const response = await axios.get(`${jmapServer}session`, { auth: { username: email, password: password, @@ -68,188 +49,3 @@ export const validateJmapCredentials = async (email, password) => { throw new Error('Nepodarilo sa overiť emailový účet'); } }; - -/** - * Pošle email pomocou JMAP - */ -const sendJmapEmail = async ({ to, subject, htmlBody, textBody }) => { - try { - const session = await getJmapSession(); - const apiUrl = session.apiUrl; - - const emailObject = { - from: [{ email: JMAP_CONFIG.username }], - to: [{ email: to }], - subject, - htmlBody: [ - { - partId: '1', - type: 'text/html', - value: htmlBody, - }, - ], - textBody: [ - { - partId: '2', - type: 'text/plain', - value: textBody || subject, - }, - ], - }; - - const response = await axios.post( - apiUrl, - { - using: ['urn:ietf:params:jmap:core', 'urn:ietf:params:jmap:mail'], - methodCalls: [ - [ - 'Email/set', - { - accountId: JMAP_CONFIG.accountId, - create: { - draft: emailObject, - }, - }, - '0', - ], - [ - 'EmailSubmission/set', - { - accountId: JMAP_CONFIG.accountId, - create: { - submission: { - emailId: '#draft', - envelope: { - mailFrom: { email: JMAP_CONFIG.username }, - rcptTo: [{ email: to }], - }, - }, - }, - }, - '1', - ], - ], - }, - { - auth: { - username: JMAP_CONFIG.username, - password: JMAP_CONFIG.password, - }, - headers: { - 'Content-Type': 'application/json', - }, - } - ); - - logger.success(`Email sent to ${to}`); - return response.data; - } catch (error) { - logger.error(`Failed to send email to ${to}`, error); - throw new Error('Nepodarilo sa odoslať email'); - } -}; - -/** - * Email templates - */ - -export const sendVerificationEmail = async (to, username, verificationToken) => { - const verificationUrl = `${process.env.BETTER_AUTH_URL}/api/auth/verify-email?token=${verificationToken}`; - - const htmlBody = ` - - -
- - - -Prosím, verifikujte svoju emailovú adresu kliknutím na tlačidlo nižšie:
- Verifikovať email -Alebo skopírujte tento link do prehliadača:
-${verificationUrl}
- -Váš účet bol úspešne vytvorený a nastavený.
-Môžete sa prihlásiť a začať používať systém.
-