CRM Server - Kompletná Dokumentácia
Obsah
- Prehľad projektu
- Štruktúra projektu
- Databázová schéma
- API Endpoints
- Middlewares
- Services
- Controllers
- Utility funkcie
- Validačné schémy
- Konfigurácia
- Autentizácia a bezpečnosť
- Cron Jobs a Notifikácie
1. Prehľad projektu
CRM Server je backend aplikácia postavená na:
- Express.js - Web framework
- Drizzle ORM - Databázový ORM
- PostgreSQL - Databáza
- JWT - Autentizácia
- Zod - Validácia
- JMAP - Email integrácia
Hlavné funkcie:
- Multi-user CRM s role-based access control (admin/member)
- Email integrácia cez JMAP API
- Time tracking s generovaním timesheetov (XLSX)
- Contact management s linkovaním na firmy
- Company/Project management s teamami
- Todo/Task management s priorítami
- Audit logging všetkých akcií
- Company reminders s due dates
- Meeting scheduling
2. Štruktúra projektu
3. Databázová schéma
Enums
Tabuľky
users
Používatelia systému.
| Stĺpec |
Typ |
Popis |
| id |
UUID |
Primárny kľúč |
| username |
VARCHAR(50) |
Unikátne užívateľské meno |
| firstName |
VARCHAR(100) |
Krstné meno |
| lastName |
VARCHAR(100) |
Priezvisko |
| password |
TEXT |
Hash hesla |
| tempPassword |
TEXT |
Dočasné heslo |
| changedPassword |
BOOLEAN |
Či si zmenil heslo |
| role |
ENUM |
'admin' alebo 'member' |
| lastLogin |
TIMESTAMP |
Posledné prihlásenie |
| createdAt |
TIMESTAMP |
Dátum vytvorenia |
| updatedAt |
TIMESTAMP |
Dátum aktualizácie |
email_accounts
Zdieľané email účty.
| Stĺpec |
Typ |
Popis |
| id |
UUID |
Primárny kľúč |
| email |
VARCHAR(255) |
Email adresa |
| emailPassword |
TEXT |
Šifrované heslo (AES-256-GCM) |
| jmapAccountId |
TEXT |
JMAP account ID |
| isActive |
BOOLEAN |
Či je účet aktívny |
| createdAt |
TIMESTAMP |
Dátum vytvorenia |
| updatedAt |
TIMESTAMP |
Dátum aktualizácie |
user_email_accounts
M2M väzba: users ↔ email_accounts
| Stĺpec |
Typ |
Popis |
| id |
UUID |
Primárny kľúč |
| userId |
UUID |
FK → users.id |
| emailAccountId |
UUID |
FK → email_accounts.id |
| isPrimary |
BOOLEAN |
Či je primárny účet |
| addedAt |
TIMESTAMP |
Dátum pridania |
companies
Firmy/spoločnosti.
| Stĺpec |
Typ |
Popis |
| id |
UUID |
Primárny kľúč |
| name |
VARCHAR(255) |
Názov firmy |
| description |
TEXT |
Popis |
| address |
TEXT |
Adresa |
| city |
VARCHAR(100) |
Mesto |
| country |
VARCHAR(100) |
Krajina |
| phone |
VARCHAR(50) |
Telefón |
| email |
VARCHAR(255) |
Email |
| website |
VARCHAR(255) |
Web stránka |
| isActive |
BOOLEAN |
Či je aktívna |
| createdBy |
UUID |
FK → users.id |
| createdAt |
TIMESTAMP |
Dátum vytvorenia |
| updatedAt |
TIMESTAMP |
Dátum aktualizácie |
company_users
M2M väzba: companies ↔ users (tím firmy)
| Stĺpec |
Typ |
Popis |
| id |
UUID |
Primárny kľúč |
| companyId |
UUID |
FK → companies.id (CASCADE) |
| userId |
UUID |
FK → users.id (CASCADE) |
| role |
TEXT |
Rola v tíme |
| addedBy |
UUID |
FK → users.id (SET NULL) |
| addedAt |
TIMESTAMP |
Dátum pridania |
Unique constraint: (companyId, userId)
company_remind
Pripomienky firmy.
| Stĺpec |
Typ |
Popis |
| id |
UUID |
Primárny kľúč |
| companyId |
UUID |
FK → companies.id (CASCADE) |
| description |
TEXT |
Popis pripomienky |
| dueDate |
TIMESTAMP |
Termín |
| isChecked |
BOOLEAN |
Či je splnená |
| createdAt |
TIMESTAMP |
Dátum vytvorenia |
| updatedAt |
TIMESTAMP |
Dátum aktualizácie |
projects
Projekty.
| Stĺpec |
Typ |
Popis |
| id |
UUID |
Primárny kľúč |
| name |
VARCHAR(255) |
Názov projektu |
| description |
TEXT |
Popis |
| companyId |
UUID |
FK → companies.id (SET NULL) |
| status |
ENUM |
Status projektu |
| startDate |
DATE |
Dátum začiatku |
| endDate |
DATE |
Dátum konca |
| createdBy |
UUID |
FK → users.id |
| createdAt |
TIMESTAMP |
Dátum vytvorenia |
| updatedAt |
TIMESTAMP |
Dátum aktualizácie |
project_users
M2M väzba: projects ↔ users (tím projektu)
| Stĺpec |
Typ |
Popis |
| id |
UUID |
Primárny kľúč |
| projectId |
UUID |
FK → projects.id (CASCADE) |
| userId |
UUID |
FK → users.id (CASCADE) |
| role |
TEXT |
Rola v projekte |
| addedBy |
UUID |
FK → users.id (SET NULL) |
| addedAt |
TIMESTAMP |
Dátum pridania |
Unique constraint: (projectId, userId)
todos
Úlohy/tasky.
| Stĺpec |
Typ |
Popis |
| id |
UUID |
Primárny kľúč |
| title |
VARCHAR(255) |
Názov úlohy |
| description |
TEXT |
Popis |
| projectId |
UUID |
FK → projects.id (SET NULL) |
| companyId |
UUID |
FK → companies.id (SET NULL) |
| status |
ENUM |
Status úlohy |
| priority |
ENUM |
Priorita |
| dueDate |
TIMESTAMP |
Termín |
| completedAt |
TIMESTAMP |
Dátum dokončenia |
| createdBy |
UUID |
FK → users.id |
| createdAt |
TIMESTAMP |
Dátum vytvorenia |
| updatedAt |
TIMESTAMP |
Dátum aktualizácie |
todo_users
M2M väzba: todos ↔ users (priradení k úlohe)
| Stĺpec |
Typ |
Popis |
| id |
UUID |
Primárny kľúč |
| todoId |
UUID |
FK → todos.id (CASCADE) |
| userId |
UUID |
FK → users.id (CASCADE) |
| assignedBy |
UUID |
FK → users.id (SET NULL) |
| assignedAt |
TIMESTAMP |
Dátum priradenia |
Unique constraint: (todoId, userId)
contacts
Kontakty (patriace email účtu).
| Stĺpec |
Typ |
Popis |
| id |
UUID |
Primárny kľúč |
| emailAccountId |
UUID |
FK → email_accounts.id (CASCADE) |
| companyId |
UUID |
FK → companies.id (SET NULL) |
| email |
VARCHAR(255) |
Email kontaktu |
| name |
VARCHAR(255) |
Meno kontaktu |
| notes |
TEXT |
Poznámky |
| addedBy |
UUID |
FK → users.id |
| addedAt |
TIMESTAMP |
Dátum pridania |
| createdAt |
TIMESTAMP |
Dátum vytvorenia |
| updatedAt |
TIMESTAMP |
Dátum aktualizácie |
personal_contacts
Osobné kontakty používateľa.
| Stĺpec |
Typ |
Popis |
| id |
UUID |
Primárny kľúč |
| userId |
UUID |
FK → users.id (CASCADE) |
| firstName |
VARCHAR(100) |
Krstné meno |
| lastName |
VARCHAR(100) |
Priezvisko |
| phone |
VARCHAR(50) |
Telefón |
| email |
VARCHAR(255) |
Email |
| secondaryEmail |
VARCHAR(255) |
Sekundárny email |
| createdAt |
TIMESTAMP |
Dátum vytvorenia |
| updatedAt |
TIMESTAMP |
Dátum aktualizácie |
emails
Uložené emaily z JMAP.
| Stĺpec |
Typ |
Popis |
| id |
UUID |
Primárny kľúč |
| emailAccountId |
UUID |
FK → email_accounts.id (CASCADE) |
| contactId |
UUID |
FK → contacts.id (SET NULL) |
| companyId |
UUID |
FK → companies.id (SET NULL) |
| jmapId |
TEXT |
JMAP ID emailu |
| messageId |
TEXT |
Message-ID header |
| threadId |
TEXT |
JMAP thread ID |
| inReplyTo |
TEXT |
In-Reply-To header |
| from |
JSONB |
Odosielateľ |
| to |
JSONB |
Príjemcovia |
| subject |
TEXT |
Predmet |
| body |
TEXT |
Telo emailu |
| isRead |
BOOLEAN |
Či je prečítaný |
| sentByUserId |
UUID |
FK → users.id (ak odoslané) |
| date |
TIMESTAMP |
Dátum emailu |
| createdAt |
TIMESTAMP |
Dátum vytvorenia |
| updatedAt |
TIMESTAMP |
Dátum aktualizácie |
notes
Poznámky (pripojené k rôznym entitám).
| Stĺpec |
Typ |
Popis |
| id |
UUID |
Primárny kľúč |
| title |
VARCHAR(255) |
Titulok |
| content |
TEXT |
Obsah poznámky |
| companyId |
UUID |
FK → companies.id (SET NULL) |
| projectId |
UUID |
FK → projects.id (SET NULL) |
| todoId |
UUID |
FK → todos.id (SET NULL) |
| contactId |
UUID |
FK → contacts.id (SET NULL) |
| reminderAt |
TIMESTAMP |
Čas pripomienky |
| reminderSent |
BOOLEAN |
Či bola pripomienka odoslaná |
| createdBy |
UUID |
FK → users.id |
| createdAt |
TIMESTAMP |
Dátum vytvorenia |
| updatedAt |
TIMESTAMP |
Dátum aktualizácie |
time_entries
Sledovanie času.
| Stĺpec |
Typ |
Popis |
| id |
UUID |
Primárny kľúč |
| userId |
UUID |
FK → users.id (CASCADE) |
| projectId |
UUID |
FK → projects.id (SET NULL) |
| todoId |
UUID |
FK → todos.id (SET NULL) |
| companyId |
UUID |
FK → companies.id (SET NULL) |
| startTime |
TIMESTAMP |
Čas začiatku |
| endTime |
TIMESTAMP |
Čas konca |
| duration |
INTEGER |
Trvanie v minútach |
| description |
TEXT |
Popis práce |
| isRunning |
BOOLEAN |
Či práve beží |
| isEdited |
BOOLEAN |
Či bol upravený |
| createdAt |
TIMESTAMP |
Dátum vytvorenia |
| updatedAt |
TIMESTAMP |
Dátum aktualizácie |
timesheets
Nahrané timesheets.
| Stĺpec |
Typ |
Popis |
| id |
UUID |
Primárny kľúč |
| userId |
UUID |
FK → users.id (CASCADE) |
| projectId |
UUID |
FK → projects.id (SET NULL) |
| fileName |
VARCHAR(255) |
Názov súboru |
| filePath |
TEXT |
Cesta k súboru |
| fileType |
VARCHAR(10) |
'pdf' alebo 'xlsx' |
| fileSize |
INTEGER |
Veľkosť v bytoch |
| year |
INTEGER |
Rok |
| month |
INTEGER |
Mesiac |
| isGenerated |
BOOLEAN |
Či bol generovaný |
| uploadedAt |
TIMESTAMP |
Dátum nahratia |
| createdAt |
TIMESTAMP |
Dátum vytvorenia |
| updatedAt |
TIMESTAMP |
Dátum aktualizácie |
meetings
Stretnutia (iba admin).
| Stĺpec |
Typ |
Popis |
| id |
UUID |
Primárny kľúč |
| title |
VARCHAR(255) |
Názov stretnutia |
| description |
TEXT |
Popis |
| start |
TIMESTAMP |
Čas začiatku |
| end |
TIMESTAMP |
Čas konca |
| createdBy |
UUID |
FK → users.id |
| createdAt |
TIMESTAMP |
Dátum vytvorenia |
| updatedAt |
TIMESTAMP |
Dátum aktualizácie |
audit_logs
Audit trail všetkých akcií.
| Stĺpec |
Typ |
Popis |
| id |
UUID |
Primárny kľúč |
| userId |
UUID |
FK → users.id (SET NULL) |
| action |
VARCHAR(100) |
Typ akcie |
| resource |
VARCHAR(100) |
Typ resource |
| resourceId |
UUID |
ID resource |
| oldValue |
JSONB |
Stará hodnota |
| newValue |
JSONB |
Nová hodnota |
| ipAddress |
VARCHAR(45) |
IP adresa |
| userAgent |
TEXT |
User agent |
| success |
BOOLEAN |
Či bola úspešná |
| errorMessage |
TEXT |
Chybová správa |
| createdAt |
TIMESTAMP |
Dátum vytvorenia |
4. API Endpoints
Formát Response
Success:
Error:
Authentication (/api/auth)
| Metóda |
Endpoint |
Popis |
Auth |
Rate Limit |
| POST |
/login |
Prihlásenie |
Nie |
5/15min |
| POST |
/set-password |
Nastavenie hesla |
Áno |
- |
| POST |
/logout |
Odhlásenie |
Áno |
- |
| GET |
/session |
Aktuálna session |
Áno |
- |
Admin (/api/admin)
| Metóda |
Endpoint |
Popis |
Auth |
Role |
| GET |
/users |
Zoznam userov |
Áno |
Všetci |
| POST |
/users |
Vytvoriť usera |
Áno |
Admin |
| GET |
/users/:userId |
Detail usera |
Áno |
Admin |
| PATCH |
/users/:userId/role |
Zmeniť rolu |
Áno |
Admin |
| DELETE |
/users/:userId |
Zmazať usera |
Áno |
Admin |
| GET |
/server-status |
Server status |
Áno |
Admin |
| POST |
/trigger-notifications |
Manuálne spustiť notifikácie |
Áno |
Admin |
Companies (/api/companies)
| Metóda |
Endpoint |
Popis |
Auth |
Role |
| GET |
/ |
Zoznam firiem |
Áno |
Všetci* |
| GET |
/:companyId |
Detail firmy |
Áno |
Access |
| POST |
/ |
Vytvoriť firmu |
Áno |
Admin |
| PATCH |
/:companyId |
Upraviť firmu |
Áno |
Admin |
| DELETE |
/:companyId |
Zmazať firmu |
Áno |
Admin |
| GET |
/reminders/summary |
Súhrn pripomienok |
Áno |
Všetci* |
| GET |
/reminders/counts |
Počty pripomienok |
Áno |
Všetci* |
| GET |
/reminders/upcoming |
Budúce pripomienky |
Áno |
Všetci* |
| GET |
/email-unread |
Neprečítané emaily |
Áno |
Všetci |
| GET |
/:companyId/email-threads |
Email vlákna |
Áno |
Access |
Company Notes:
| Metóda |
Endpoint |
Popis |
Auth |
Role |
| GET |
/:companyId/notes |
Poznámky firmy |
Áno |
Access |
| POST |
/:companyId/notes |
Pridať poznámku |
Áno |
Admin |
| PATCH |
/:companyId/notes/:noteId |
Upraviť poznámku |
Áno |
Admin |
| DELETE |
/:companyId/notes/:noteId |
Zmazať poznámku |
Áno |
Admin |
Company Reminders:
| Metóda |
Endpoint |
Popis |
Auth |
Role |
| GET |
/:companyId/reminders |
Pripomienky firmy |
Áno |
Access |
| POST |
/:companyId/reminders |
Vytvoriť |
Áno |
Admin |
| PATCH |
/:companyId/reminders/:reminderId |
Upraviť |
Áno |
Admin |
| DELETE |
/:companyId/reminders/:reminderId |
Zmazať |
Áno |
Admin |
Company Users (Team):
| Metóda |
Endpoint |
Popis |
Auth |
Role |
| GET |
/:companyId/users |
Členovia tímu |
Áno |
Access |
| POST |
/:companyId/users |
Pridať člena |
Áno |
Admin |
| PATCH |
/:companyId/users/:userId |
Upraviť rolu |
Áno |
Admin |
| DELETE |
/:companyId/users/:userId |
Odstrániť člena |
Áno |
Admin |
* Member vidí len firmy kde je priradený
Projects (/api/projects)
| Metóda |
Endpoint |
Popis |
Auth |
Role |
| GET |
/ |
Zoznam projektov |
Áno |
Všetci* |
| GET |
/:projectId |
Detail projektu |
Áno |
Access |
| POST |
/ |
Vytvoriť projekt |
Áno |
Admin |
| PATCH |
/:projectId |
Upraviť projekt |
Áno |
Admin |
| DELETE |
/:projectId |
Zmazať projekt |
Áno |
Admin |
Project Notes:
| Metóda |
Endpoint |
Popis |
Auth |
Role |
| GET |
/:projectId/notes |
Poznámky projektu |
Áno |
Access |
| POST |
/:projectId/notes |
Pridať poznámku |
Áno |
Admin |
| PATCH |
/:projectId/notes/:noteId |
Upraviť |
Áno |
Admin |
| DELETE |
/:projectId/notes/:noteId |
Zmazať |
Áno |
Admin |
Project Users (Team):
| Metóda |
Endpoint |
Popis |
Auth |
Role |
| GET |
/:projectId/users |
Členovia projektu |
Áno |
Access |
| POST |
/:projectId/users |
Pridať člena |
Áno |
Admin |
| PATCH |
/:projectId/users/:userId |
Upraviť rolu |
Áno |
Admin |
| DELETE |
/:projectId/users/:userId |
Odstrániť člena |
Áno |
Admin |
* Member vidí len projekty kde je priradený alebo projekty firiem kde je priradený
Todos (/api/todos)
| Metóda |
Endpoint |
Popis |
Auth |
Role |
| GET |
/ |
Zoznam úloh |
Áno |
Všetci* |
| GET |
/:todoId |
Detail úlohy |
Áno |
Access |
| POST |
/ |
Vytvoriť úlohu |
Áno |
Admin |
| PATCH |
/:todoId |
Upraviť úlohu |
Áno |
Admin |
| DELETE |
/:todoId |
Zmazať úlohu |
Áno |
Admin |
| PATCH |
/:todoId/toggle |
Prepnúť stav |
Áno |
Access |
* Member vidí len úlohy kde je priradený
Query parametre pre GET /:
search - Vyhľadávanie v title/description
projectId - Filter podľa projektu
companyId - Filter podľa firmy
assignedTo - Filter podľa priradeného usera
status - Filter podľa statusu (pending/in_progress/completed/cancelled)
completed - true/false (alternatíva k status)
priority - Filter podľa priority (low/medium/high/urgent)
Contacts (/api/contacts)
| Metóda |
Endpoint |
Popis |
Auth |
Role |
| GET |
/ |
Zoznam kontaktov |
Áno |
Všetci |
| GET |
/discover |
Objaviť z JMAP |
Áno |
Všetci |
| POST |
/ |
Pridať kontakt |
Áno |
Všetci |
| PATCH |
/:contactId |
Upraviť kontakt |
Áno |
Všetci |
| POST |
/:contactId/link-company |
Pripojiť firmu |
Áno |
Všetci |
| POST |
/:contactId/unlink-company |
Odpojiť firmu |
Áno |
Všetci |
| POST |
/:contactId/create-company |
Vytvoriť firmu |
Áno |
Všetci |
| DELETE |
/:contactId |
Zmazať kontakt |
Áno |
Všetci |
Personal Contacts (/api/personal-contacts)
| Metóda |
Endpoint |
Popis |
Auth |
Role |
| GET |
/ |
Moje osobné kontakty |
Áno |
Všetci |
| POST |
/ |
Vytvoriť |
Áno |
Všetci |
| PUT |
/:contactId |
Upraviť |
Áno |
Všetci |
| DELETE |
/:contactId |
Zmazať |
Áno |
Všetci |
Time Tracking (/api/time-tracking)
| Metóda |
Endpoint |
Popis |
Auth |
Role |
| POST |
/start |
Spustiť tracking |
Áno |
Všetci |
| POST |
/:entryId/stop |
Zastaviť tracking |
Áno |
Všetci |
| GET |
/running |
Bežiaci entry |
Áno |
Všetci |
| GET |
/running-all |
Všetky bežiace |
Áno |
Všetci |
| GET |
/ |
Zoznam entries |
Áno |
Všetci |
| GET |
/month/:year/:month |
Mesačné entries |
Áno |
Všetci |
| POST |
/month/:year/:month/generate |
Generovať XLSX |
Áno |
Všetci |
| GET |
/stats/monthly/:year/:month |
Mesačné štatistiky |
Áno |
Všetci |
| GET |
/:entryId |
Detail entry |
Áno |
Všetci |
| GET |
/:entryId/details |
Entry s reláciami |
Áno |
Všetci |
| PATCH |
/:entryId |
Upraviť entry |
Áno |
Všetci |
| DELETE |
/:entryId |
Zmazať entry |
Áno |
Všetci |
Timesheets (/api/timesheets)
| Metóda |
Endpoint |
Popis |
Auth |
Role |
| POST |
/upload |
Nahrať timesheet |
Áno |
Všetci |
| GET |
/my |
Moje timesheets |
Áno |
Všetci |
| GET |
/all |
Všetky timesheets |
Áno |
Admin |
| GET |
/:timesheetId/download |
Stiahnuť súbor |
Áno |
Všetci |
| DELETE |
/:timesheetId |
Zmazať |
Áno |
Všetci |
Meetings (/api/meetings)
| Metóda |
Endpoint |
Popis |
Auth |
Role |
| GET |
/ |
Zoznam meetingov |
Áno |
Všetci |
| GET |
/:meetingId |
Detail meetingu |
Áno |
Všetci |
| POST |
/ |
Vytvoriť meeting |
Áno |
Admin |
| PUT |
/:meetingId |
Upraviť meeting |
Áno |
Admin |
| DELETE |
/:meetingId |
Zmazať meeting |
Áno |
Admin |
Query parametre pre GET /:
year - Rok
month - Mesiac
Notes (/api/notes)
| Metóda |
Endpoint |
Popis |
Auth |
Role |
| GET |
/ |
Zoznam poznámok |
Áno |
Všetci |
| GET |
/my-reminders |
Moje pripomienky |
Áno |
Všetci |
| GET |
/:noteId |
Detail poznámky |
Áno |
Všetci |
| POST |
/ |
Vytvoriť poznámku |
Áno |
Všetci |
| PATCH |
/:noteId |
Upraviť poznámku |
Áno |
Všetci |
| DELETE |
/:noteId |
Zmazať poznámku |
Áno |
Všetci |
| POST |
/:noteId/mark-reminder-sent |
Označiť odoslanú |
Áno |
Všetci |
CRM Emails (/api/emails)
| Metóda |
Endpoint |
Popis |
Auth |
Role |
| GET |
/ |
Zoznam emailov |
Áno |
Všetci |
| GET |
/search |
Vyhľadávanie v DB |
Áno |
Všetci |
| GET |
/search-jmap |
JMAP full-text |
Áno |
Všetci |
| GET |
/unread-count |
Počet neprečítaných |
Áno |
Všetci |
| POST |
/sync |
Synchronizovať z JMAP |
Áno |
Všetci |
| GET |
/thread/:threadId |
Email vlákno |
Áno |
Všetci |
| POST |
/thread/:threadId/read |
Označiť prečítané |
Áno |
Všetci |
| POST |
/contact/:contactId/read |
Označiť od kontaktu |
Áno |
Všetci |
| POST |
/reply |
Odpovedať na email |
Áno |
Všetci |
Väčšina vyžaduje accountId v query parametroch.
Email Accounts (/api/email-accounts)
| Metóda |
Endpoint |
Popis |
Auth |
Role |
Rate Limit |
| GET |
/ |
Moje email účty |
Áno |
Všetci |
- |
| POST |
/ |
Vytvoriť účet |
Áno |
Všetci |
Sensitive |
| POST |
/:id/set-primary |
Nastaviť primárny |
Áno |
Všetci |
- |
| DELETE |
/:id |
Zmazať účet |
Áno |
Všetci |
Sensitive |
Audit Logs (/api/audit-logs)
| Metóda |
Endpoint |
Popis |
Auth |
Role |
| GET |
/ |
Nedávne audit logy |
Áno |
Všetci |
Health Check
| Metóda |
Endpoint |
Popis |
Auth |
| GET |
/health |
Health check |
Nie |
| GET |
/ |
API info |
Nie |
5. Middlewares
Auth Middlewares (/middlewares/auth/)
authMiddleware.js
- Overí JWT token z Authorization header alebo cookies
- Načíta aktuálne user data z DB
- Pridá
req.user a req.userId
- Vracia 401 ak token chýba/je neplatný/expiroval
roleMiddleware.js
- Overí či má user jednu z povolených rolí
- Vracia 403 ak nemá oprávnenie
- Skratka pre
requireRole('admin')
- Admin má vždy prístup
- Inak overí ownership cez callback funkciu
resourceAccessMiddleware.js
- Univerzálny middleware pre kontrolu prístupu k resources
- Admin má prístup vždy
- Member len ak je priradený k resource
- Vráti zoznam resource IDs ku ktorým má user prístup
- Použité v service vrstvách pre filtrovanie
- Skontroluje či user má prístup k danému resource
- Kombinácia role check + access check
Podporované resource typy:
| Resource Type |
Junction Table |
Resource ID Column |
| company |
company_users |
companyId |
| project |
project_users |
projectId |
| todo |
todo_users |
todoId |
Security Middlewares (/middlewares/security/)
rateLimiter.js
- Login endpoint: 5 pokusov za 15 minút
- Všeobecné API: 1000 (dev) / 100 (prod) za 15 minút
- Citlivé operácie: 50 (dev) / 10 (prod) za 15 minút
validateInput.js
- Validuje request body podľa Zod schémy
Všetky vracajú 400 s detailmi chýb:
requireAccountId.js
- Kontroluje či má user
accountId v query
- Potrebné pre email operácie
Global Middlewares (/middlewares/global/)
errorHandler.js
- Globálny error handler (posledný middleware)
- Formátuje error response
- Loguje chyby
- Vracia príslušný status code
validateBody.js
- Overí či je request body valid JSON
notFound.js
- Handler pre 404 (neexistujúce routes)
6. Services
auth.service.js
| Funkcia |
Popis |
login(username, password, ip, userAgent) |
Prihlásenie s temp/permanent heslom |
setPassword(userId, newPassword) |
Nastavenie nového hesla |
logout(userId) |
Odhlásenie (audit log) |
getUserById(userId) |
Získať usera podľa ID |
admin.service.js
| Funkcia |
Popis |
getAllUsers() |
Zoznam všetkých userov |
getUserById(userId) |
Detail usera |
createUser(data) |
Vytvoriť usera s temp heslom |
updateUserRole(userId, role) |
Zmeniť rolu usera |
deleteUser(userId) |
Zmazať usera |
company.service.js
| Funkcia |
Popis |
getAllCompanies(searchTerm, userId, userRole) |
Zoznam firiem (filtrované pre membera) |
getCompanyById(companyId) |
Detail firmy |
createCompany(data, userId) |
Vytvoriť firmu |
updateCompany(companyId, data) |
Upraviť firmu |
deleteCompany(companyId) |
Zmazať firmu |
getCompanyUsers(companyId) |
Členovia tímu |
addUserToCompany(companyId, userId, role, addedBy) |
Pridať člena |
updateUserRoleInCompany(companyId, userId, role) |
Upraviť rolu |
removeUserFromCompany(companyId, userId) |
Odstrániť člena |
company-reminder.service.js
| Funkcia |
Popis |
getRemindersByCompanyId(companyId) |
Pripomienky firmy |
createReminder(companyId, data) |
Vytvoriť pripomienku |
updateReminder(reminderId, data) |
Upraviť pripomienku |
deleteReminder(reminderId) |
Zmazať pripomienku |
getReminderSummary(userId, userRole) |
Súhrn pripomienok (filtrované) |
getReminderCountsByCompany(userId, userRole) |
Počty podľa firmy (filtrované) |
getUpcomingReminders(limit, userId, userRole) |
Budúce pripomienky (filtrované) |
project.service.js
| Funkcia |
Popis |
getAllProjects(searchTerm, companyId, userId, userRole) |
Zoznam projektov (filtrované) |
getProjectById(projectId) |
Detail projektu |
createProject(data, userId) |
Vytvoriť projekt |
updateProject(projectId, data) |
Upraviť projekt |
deleteProject(projectId) |
Zmazať projekt |
getProjectUsers(projectId) |
Členovia projektu |
assignUserToProject(projectId, userId, role, addedBy) |
Pridať člena |
updateProjectUserRole(projectId, userId, role) |
Upraviť rolu |
removeUserFromProject(projectId, userId) |
Odstrániť člena |
todo.service.js
| Funkcia |
Popis |
getAllTodos(filters, userId, userRole) |
Zoznam úloh (filtrované) |
getTodoById(todoId) |
Detail úlohy |
createTodo(userId, data) |
Vytvoriť úlohu |
updateTodo(todoId, data) |
Upraviť úlohu |
deleteTodo(todoId) |
Zmazať úlohu |
getTodoWithRelations(todoId) |
Úloha s reláciami |
getTodosByProjectId(projectId) |
Úlohy projektu |
getTodosByCompanyId(companyId) |
Úlohy firmy |
getTodosByUserId(userId) |
Úlohy usera |
contact.service.js
| Funkcia |
Popis |
getAllContacts(accountId, search, companyId) |
Zoznam kontaktov |
getContactById(contactId) |
Detail kontaktu |
createContact(data) |
Vytvoriť kontakt |
updateContact(contactId, data) |
Upraviť kontakt |
deleteContact(contactId) |
Zmazať kontakt |
linkCompanyToContact(contactId, companyId) |
Pripojiť firmu |
unlinkCompanyFromContact(contactId) |
Odpojiť firmu |
personal-contact.service.js
| Funkcia |
Popis |
getPersonalContacts(userId) |
Osobné kontakty usera |
createPersonalContact(userId, data) |
Vytvoriť |
updatePersonalContact(contactId, userId, data) |
Upraviť |
deletePersonalContact(contactId, userId) |
Zmazať |
time-tracking.service.js
| Funkcia |
Popis |
startTimeEntry(userId, data) |
Spustiť tracking |
stopTimeEntry(entryId, endTime) |
Zastaviť tracking |
getRunningEntry(userId) |
Bežiaci entry usera |
getAllRunningEntries() |
Všetky bežiace entries |
getTimeEntries(filters) |
Zoznam entries |
getTimeEntryById(entryId) |
Detail entry |
getTimeEntryWithRelations(entryId) |
Entry s reláciami |
updateTimeEntry(entryId, data) |
Upraviť entry |
deleteTimeEntry(entryId) |
Zmazať entry |
getMonthlyEntries(userId, year, month) |
Mesačné entries |
getMonthlyStats(userId, year, month) |
Mesačné štatistiky |
generateMonthlyTimesheet(userId, year, month) |
Generovať XLSX |
timesheet.service.js
| Funkcia |
Popis |
uploadTimesheet(userId, file, year, month, projectId) |
Nahrať |
getMyTimesheets(userId) |
Moje timesheets |
getAllTimesheets() |
Všetky timesheets |
getTimesheetById(timesheetId) |
Detail |
deleteTimesheet(timesheetId, userId) |
Zmazať |
meeting.service.js
| Funkcia |
Popis |
getMeetingsByMonth(year, month) |
Meetingy v mesiaci |
getMeetingById(meetingId) |
Detail meetingu |
createMeeting(data, userId) |
Vytvoriť |
updateMeeting(meetingId, data) |
Upraviť |
deleteMeeting(meetingId) |
Zmazať |
note.service.js
| Funkcia |
Popis |
getAllNotes(filters) |
Zoznam poznámok |
getNoteById(noteId) |
Detail poznámky |
createNote(data, userId) |
Vytvoriť |
updateNote(noteId, data) |
Upraviť |
deleteNote(noteId) |
Zmazať |
getMyReminders(userId) |
Moje pripomienky |
markReminderSent(noteId) |
Označiť odoslanú |
crm-email.service.js
| Funkcia |
Popis |
getEmails(accountId, filters) |
Zoznam emailov |
searchEmails(accountId, query) |
Vyhľadávanie v DB |
searchEmailsJmap(accountId, query) |
JMAP full-text |
getUnreadCount(accountId) |
Počet neprečítaných |
syncEmails(accountId) |
Synchronizovať z JMAP |
getEmailThread(accountId, threadId) |
Email vlákno |
markThreadAsRead(accountId, threadId) |
Označiť prečítané |
markContactEmailsAsRead(accountId, contactId) |
Označiť od kontaktu |
replyToEmail(accountId, userId, data) |
Odpovedať |
email-account.service.js
| Funkcia |
Popis |
getUserEmailAccounts(userId) |
Email účty usera |
createEmailAccount(userId, data) |
Vytvoriť účet |
setPrimaryAccount(userId, accountId) |
Nastaviť primárny |
deleteEmailAccount(userId, accountId) |
Zmazať účet |
audit.service.js
| Funkcia |
Popis |
logAction(data) |
Zaznamenať akciu |
logLogin(userId, success, ip, userAgent, error) |
Login log |
logLogout(userId, ip, userAgent) |
Logout log |
logPasswordChange(userId, ip, userAgent) |
Password change log |
logTodoCreated(userId, todoId, title, ip, userAgent) |
Todo created log |
logTodoDeleted(userId, todoId, title, ip, userAgent) |
Todo deleted log |
logTodoCompleted(userId, todoId, title, ip, userAgent) |
Todo completed log |
getRecentLogs(limit) |
Nedávne logy |
status.service.js
| Funkcia |
Popis |
getServerStatus() |
CPU, RAM, Disk, Network, Uptime |
JMAP Services (/services/jmap/)
| Súbor |
Funkcie |
client.js |
JMAP HTTP klient |
config.js |
JMAP konfigurácia |
discovery.js |
JMAP discovery (.well-known) |
operations.js |
Email/set, Mailbox/get |
search.js |
Email/query s full-text |
sync.js |
Synchronizácia emailov |
7. Controllers
Každý controller:
- Extrahuje dáta z
req (params, query, body, user)
- Volá príslušný service
- Formátuje a vracia response
- Zachytáva a propaguje chyby cez
next(error)
Príklad controller funkcie:
8. Utility funkcie
errors.js
jwt.js
password.js
logger.js
9. Validačné schémy
auth.validators.js
crm.validators.js
10. Konfigurácia
.env
package.json scripts
drizzle.config.js
11. Autentizácia a bezpečnosť
Autentizačný flow
- Login - User zadá username + temp heslo
- Validácia - Kontrola temp/permanent hesla
- Token generovanie - Access token (1h) + Refresh token (7d)
- Cookies - httpOnly, secure, sameSite=strict
- Password change - Povinný v prvej prihláške
- Email setup - Nastavenie email accounts
Security features
| Feature |
Implementácia |
| JWT tokeny |
httpOnly cookies |
| Rate limiting |
express-rate-limit |
| CORS |
cors middleware |
| XSS protection |
xss-clean |
| Security headers |
Helmet.js |
| Password hashing |
bcrypt (12 rounds) |
| Email password encryption |
AES-256-GCM |
| Audit logging |
Všetky akcie |
| RBAC |
admin/member role |
| Resource access control |
Junction tables |
Role-based access control
| Akcia |
Admin |
Member |
| Vytvoriť/upraviť/zmazať firmu |
✓ |
✗ |
| Vytvoriť/upraviť/zmazať projekt |
✓ |
✗ |
| Vytvoriť/upraviť/zmazať todo |
✓ |
✗ |
| Toggle todo (splnené) |
✓ |
✓ (ak priradený) |
| Zobraziť firmu |
✓ |
✓ (ak priradený) |
| Zobraziť projekt |
✓ |
✓ (ak priradený) |
| Zobraziť todo |
✓ |
✓ (ak priradený) |
| Spravovať tím |
✓ |
✗ |
| Server status |
✓ |
✗ |
| Vytvoriť/upraviť/zmazať meeting |
✓ |
✗ |
| Time tracking |
✓ |
✓ |
| Kontakty |
✓ |
✓ |
| Emaily |
✓ |
✓ |
12. Cron Jobs a Notifikácie
Prehľad
Systém obsahuje automatické cron joby pre odosielanie emailových notifikácií. Cron jobs sa spúšťajú automaticky pri štarte servera.
Štruktúra
Kalendárne notifikácie
Ako to funguje
- Cron beží každý deň o čase nastavenom v
NOTIFICATION_TIME (default: 07:00)
- Vyhľadá eventy ktoré začínajú zajtra (00:00 - 23:59)
- Získa priradených používateľov cez
eventUsers junction tabuľku
- Zistí primárny email každého používateľa z
userEmailAccounts
- Odošle HTML email cez JMAP z účtu nastaveného v
NOTIFICATION_SENDER_EMAIL
Kedy sa notifikácie posielajú
| Event začína |
Notifikácia |
| Včera / dnes (minulosť) |
❌ Nie |
| Zajtra |
✅ Áno |
| Pozajtra a neskôr |
❌ Nie |
Konfigurácia
Dôležité: NOTIFICATION_SENDER_EMAIL musí byť email účet uložený v tabuľke email_accounts s platným zašifrovaným heslom.
Testovací mód
Pre testovanie nastavte v .env:
Cron bude bežať každú minútu namiesto raz denne. Po dokončení testovania nastavte späť na false.
Manuálne spustenie
Admin môže manuálne spustiť notifikácie cez API:
Response:
sent - Počet úspešne odoslaných emailov
failed - Počet neúspešných odoslaní
skipped - Počet preskočených (napr. používateľ nemá nastavený email)
Email šablóna
Notifikačný email obsahuje:
- Header s logom CRM
- Pozdrav s menom používateľa
- Karta eventu s:
- Typ (Stretnutie / Udalosť) - farebne odlíšené
- Názov eventu
- Popis (ak existuje)
- Dátum a čas
- Footer s informáciou o automatickom generovaní
Email je responzívny a zobrazuje sa správne v rôznych email klientoch.
Logy
Pri behu cron jobu sa zobrazujú logy:
Rozšírenie
Pre pridanie nových cron jobs:
- Vytvorte nový folder v
src/cron/ (napr. reminders/)
- Implementujte logiku podobne ako v
calendar/
- Exportujte start funkciu
- Importujte a zavolajte v
src/cron/index.js
Záver
Táto dokumentácia poskytuje kompletný prehľad CRM Server backendu. Pre ďalšie informácie kontaktujte administrátora projektu.