1249 lines
31 KiB
Markdown
1249 lines
31 KiB
Markdown
# CRM Server API
|
|
|
|
Modern backend API server pre CRM systém s pokročilou autentifikáciou, JMAP email integráciou, kontakt managementom a kompletnou bezpečnostnou vrstvou.
|
|
|
|
## 📋 Obsah
|
|
|
|
- [Technológie](#technológie)
|
|
- [Funkcie](#funkcie)
|
|
- [Systém rolí a autorizácia](#systém-rolí-a-autorizácia)
|
|
- [Architektúra](#architektúra)
|
|
- [Databázová schéma](#databázová-schéma)
|
|
- [API Endpoints](#api-endpoints)
|
|
- [JMAP Email integrácia](#jmap-email-integrácia)
|
|
- [Bezpečnosť](#bezpečnosť)
|
|
- [Inštalácia a spustenie](#inštalácia-a-spustenie)
|
|
- [Štruktúra projektu](#štruktúra-projektu)
|
|
- [Testing](#testing)
|
|
- [Deployment](#deployment)
|
|
|
|
---
|
|
|
|
## 🚀 Technológie
|
|
|
|
### Backend Stack
|
|
- **Node.js 18+** - Runtime environment
|
|
- **Express.js 4.21** - Web framework
|
|
- **PostgreSQL 16** - Relačná databáza
|
|
- **Drizzle ORM 0.44** - Type-safe ORM
|
|
- **JWT (jsonwebtoken 9.0)** - Token-based autentifikácia
|
|
- **Bcrypt.js 3.0** - Password hashing
|
|
- **Zod 4.1** - Schema validation
|
|
- **Axios 1.13** - HTTP client pre JMAP
|
|
|
|
### Security & Middleware
|
|
- **Helmet 8.0** - HTTP security headers (CSP, HSTS)
|
|
- **CORS 2.8** - Cross-Origin Resource Sharing
|
|
- **Express Rate Limit 8.2** - Rate limiting protection
|
|
- **Cookie Parser 1.4** - Cookie parsing
|
|
- **Morgan 1.10** - HTTP request logger
|
|
- **XSS Clean 0.1** - XSS protection
|
|
|
|
### Dev Tools
|
|
- **Nodemon 3.1** - Auto-restart development server
|
|
- **Drizzle Kit 0.31** - Database migrations
|
|
- **ESLint** - Code linting
|
|
- **Jest 29.7** - Testing framework
|
|
- **Supertest 6.3** - HTTP API testing
|
|
|
|
### Email Integration
|
|
- **JMAP Protocol** - Modern email protocol
|
|
- **Truemail.sk** - Email provider integration
|
|
|
|
---
|
|
|
|
## ✨ Funkcie
|
|
|
|
### Autentifikácia a používatelia
|
|
- ✅ **3-krokový onboarding flow**
|
|
- Krok 1: Login s dočasným heslom
|
|
- Krok 2: Nastavenie vlastného hesla
|
|
- Krok 3: Voliteľné pripojenie JMAP email účtu
|
|
|
|
- ✅ **Session-based autentifikácia**
|
|
- JWT tokens (Access + Refresh)
|
|
- HttpOnly cookies pre bezpečnosť
|
|
- Automatický refresh tokenov
|
|
|
|
- ✅ **User management (Admin)**
|
|
- Vytvorenie používateľa s automatickým temp heslom
|
|
- Zmena rolí (admin/member)
|
|
- Zmazanie používateľa
|
|
- Zoznam všetkých používateľov
|
|
|
|
### Email Management (JMAP)
|
|
- ✅ **Email synchronizácia**
|
|
- Stiahnutie emailov z JMAP servera (Truemail.sk)
|
|
- Automatická synchronizácia len pre pridané kontakty
|
|
- Thread-based organizácia (konverzácie)
|
|
|
|
- ✅ **Email operácie**
|
|
- Označovanie ako prečítané/neprečítané
|
|
- Vyhľadávanie v emailoch (subject, body, sender)
|
|
- Odpovede na emaily cez JMAP
|
|
- Počítadlo neprečítaných správ
|
|
|
|
- ✅ **Contact Management**
|
|
- Objavovanie potenciálnych kontaktov z email odosielateľov
|
|
- Pridávanie kontaktov
|
|
- Editácia kontaktov (meno, poznámky)
|
|
- Odstránenie kontaktu
|
|
- Pri pridaní kontaktu sa automaticky syncujú všetky jeho emaily
|
|
|
|
### Audit a Logging
|
|
- ✅ **Kompletný audit trail**
|
|
- Všetky dôležité akcie logované do DB
|
|
- IP adresa, user agent, timestamp
|
|
- Staré a nové hodnoty (pre change tracking)
|
|
- Success/error status
|
|
|
|
- ✅ **Audit events**
|
|
- Login/logout
|
|
- Password changes
|
|
- Email linking
|
|
- Contact additions/removals
|
|
- Role changes (admin akcie)
|
|
|
|
---
|
|
|
|
## 🔐 Systém rolí a autorizácia
|
|
|
|
### Role Hierarchy
|
|
|
|
#### 1. **Admin**
|
|
**Plné oprávnenia:**
|
|
- ✅ Všetko čo môže Member
|
|
- ✅ **User management:**
|
|
- `POST /api/admin/users` - Vytvorenie používateľa
|
|
- `GET /api/admin/users` - Zoznam všetkých používateľov
|
|
- `GET /api/admin/users/:id` - Detail používateľa
|
|
- `PATCH /api/admin/users/:id/role` - Zmena role
|
|
- `DELETE /api/admin/users/:id` - Zmazanie používateľa
|
|
|
|
**Backend implementácia:**
|
|
```javascript
|
|
// Middleware chain
|
|
router.use(authenticate); // Overí JWT token
|
|
router.use(requireAdmin); // Overí role === 'admin'
|
|
```
|
|
|
|
#### 2. **Member**
|
|
**Základné oprávnenia:**
|
|
- ✅ Vlastný profil a nastavenia
|
|
- ✅ Zmena vlastného hesla
|
|
- ✅ Pripojenie vlastného email účtu
|
|
- ✅ Správa vlastných kontaktov
|
|
- ✅ Vlastné emaily a konverzácie
|
|
|
|
**NEMÁ prístup k:**
|
|
- ❌ Admin endpointy (`/api/admin/*`)
|
|
- ❌ Údaje iných používateľov
|
|
- ❌ Zmena rolí
|
|
|
|
### Middleware Chain
|
|
|
|
**Autentifikácia:**
|
|
```javascript
|
|
// src/middlewares/auth/authMiddleware.js
|
|
export const authenticate = async (req, res, next) => {
|
|
// 1. Získaj token z cookie alebo Authorization header
|
|
// 2. Verifikuj JWT token
|
|
// 3. Načítaj user z DB
|
|
// 4. Pridaj user do req.user
|
|
}
|
|
```
|
|
|
|
**Autorizácia (role-based):**
|
|
```javascript
|
|
// src/middlewares/auth/roleMiddleware.js
|
|
export const requireAdmin = (req, res, next) => {
|
|
if (req.user.role !== 'admin') {
|
|
return res.status(403).json({ error: 'Forbidden' })
|
|
}
|
|
next()
|
|
}
|
|
```
|
|
|
|
### Bezpečnostný princíp: Backend Filtering
|
|
|
|
**✅ Správne:** Backend filtruje dáta podľa role
|
|
```javascript
|
|
// Admin endpoint - middleware blokuje non-adminov
|
|
router.get('/api/admin/users', authenticate, requireAdmin, getAllUsers)
|
|
|
|
// Member endpoint - user vidí len svoje dáta
|
|
router.get('/api/contacts', authenticate, async (req, res) => {
|
|
// Service layer filtruje podľa req.user.id
|
|
const contacts = await getContactsByUserId(req.user.id)
|
|
res.json({ data: contacts })
|
|
})
|
|
```
|
|
|
|
**❌ Zlé:** Posielanie všetkých dát a filtrovanie na frontende
|
|
- Citlivé admin dáta by boli viditeľné v Network tab
|
|
- Security by bola len "visual" - nie skutočná
|
|
|
|
---
|
|
|
|
## 🏗️ Architektúra
|
|
|
|
### Layered Architecture
|
|
|
|
```
|
|
┌─────────────────────────────────────┐
|
|
│ Routes Layer │ ← API endpoints, middleware chains
|
|
│ (auth.routes.js, admin.routes.js) │
|
|
└──────────────┬──────────────────────┘
|
|
│
|
|
┌──────────────▼──────────────────────┐
|
|
│ Controllers Layer │ ← Request handling, validation
|
|
│ (auth.controller.js, ...) │
|
|
└──────────────┬──────────────────────┘
|
|
│
|
|
┌──────────────▼──────────────────────┐
|
|
│ Services Layer │ ← Business logic
|
|
│ (auth.service.js, jmap.service.js)│
|
|
└──────────────┬──────────────────────┘
|
|
│
|
|
┌──────────────▼──────────────────────┐
|
|
│ Database Layer (Drizzle ORM) │ ← Data persistence
|
|
│ (schema.js) │
|
|
└─────────────────────────────────────┘
|
|
```
|
|
|
|
### Middleware Pipeline
|
|
|
|
```
|
|
Request
|
|
│
|
|
├─→ Morgan (logging)
|
|
├─→ Helmet (security headers)
|
|
├─→ CORS (cross-origin)
|
|
├─→ Body Parser (JSON, URL-encoded)
|
|
├─→ Cookie Parser
|
|
├─→ Rate Limiter
|
|
│
|
|
├─→ Route Matching
|
|
│ │
|
|
│ ├─→ authenticate (JWT validation)
|
|
│ ├─→ requireAdmin (role check)
|
|
│ ├─→ validateBody (Zod schemas)
|
|
│ ├─→ Controller
|
|
│ └─→ Service → Database
|
|
│
|
|
├─→ Not Found Handler (404)
|
|
└─→ Error Handler (global catch)
|
|
```
|
|
|
|
---
|
|
|
|
## 💾 Databázová schéma
|
|
|
|
### Users Table
|
|
```sql
|
|
CREATE TABLE users (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
username TEXT NOT NULL UNIQUE,
|
|
email TEXT UNIQUE, -- JMAP email (Truemail.sk)
|
|
email_password TEXT, -- Encrypted JMAP password
|
|
jmap_account_id TEXT, -- JMAP account ID
|
|
first_name TEXT,
|
|
last_name TEXT,
|
|
password TEXT, -- Bcrypt hash (permanent)
|
|
temp_password TEXT, -- Bcrypt hash (temporary)
|
|
changed_password BOOLEAN DEFAULT false,
|
|
role role_enum DEFAULT 'member' NOT NULL, -- 'admin' | 'member'
|
|
last_login TIMESTAMP,
|
|
created_at TIMESTAMP DEFAULT NOW(),
|
|
updated_at TIMESTAMP DEFAULT NOW()
|
|
);
|
|
```
|
|
|
|
### Contacts Table
|
|
```sql
|
|
CREATE TABLE contacts (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
email TEXT NOT NULL,
|
|
name TEXT,
|
|
notes TEXT,
|
|
added_at TIMESTAMP DEFAULT NOW(),
|
|
created_at TIMESTAMP DEFAULT NOW(),
|
|
updated_at TIMESTAMP DEFAULT NOW()
|
|
);
|
|
```
|
|
|
|
### Emails Table
|
|
```sql
|
|
CREATE TABLE emails (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
contact_id UUID REFERENCES contacts(id) ON DELETE CASCADE,
|
|
jmap_id TEXT UNIQUE, -- JMAP message ID
|
|
message_id TEXT UNIQUE, -- Email Message-ID header
|
|
thread_id TEXT, -- Thread grouping
|
|
in_reply_to TEXT, -- Reply chain
|
|
"from" TEXT,
|
|
"to" TEXT,
|
|
subject TEXT,
|
|
body TEXT,
|
|
is_read BOOLEAN DEFAULT false,
|
|
date TIMESTAMP,
|
|
created_at TIMESTAMP DEFAULT NOW(),
|
|
updated_at TIMESTAMP DEFAULT NOW()
|
|
);
|
|
```
|
|
|
|
### Audit Logs Table
|
|
```sql
|
|
CREATE TABLE audit_logs (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id UUID REFERENCES users(id) ON DELETE SET NULL,
|
|
action TEXT NOT NULL, -- 'login', 'password_change', ...
|
|
resource TEXT NOT NULL, -- 'user', 'contact', 'email'
|
|
resource_id TEXT,
|
|
old_value TEXT, -- JSON string
|
|
new_value TEXT, -- JSON string
|
|
ip_address TEXT,
|
|
user_agent TEXT,
|
|
success BOOLEAN DEFAULT true,
|
|
error_message TEXT,
|
|
created_at TIMESTAMP DEFAULT NOW()
|
|
);
|
|
```
|
|
|
|
### Relationships
|
|
- **Users → Contacts** (1:N) - User môže mať viacero kontaktov
|
|
- **Users → Emails** (1:N) - User má svoje emaily
|
|
- **Contacts → Emails** (1:N) - Email je priradený kontaktu
|
|
- **Users → Audit Logs** (1:N) - Audit log pre každú akciu
|
|
|
|
---
|
|
|
|
## 📡 API Endpoints
|
|
|
|
### Autentifikácia (`/api/auth`)
|
|
|
|
| Method | Endpoint | Auth | Role | Popis |
|
|
|--------|----------|------|------|-------|
|
|
| POST | `/auth/login` | ❌ | - | Login s temporary alebo permanent heslom |
|
|
| POST | `/auth/set-password` | ✅ | - | Nastavenie nového hesla (onboarding) |
|
|
| POST | `/auth/link-email` | ✅ | - | Pripojenie JMAP email účtu |
|
|
| POST | `/auth/skip-email` | ✅ | - | Preskočenie email setupu |
|
|
| POST | `/auth/logout` | ✅ | - | Odhlásenie (clear cookies) |
|
|
| GET | `/auth/session` | ✅ | - | Získanie aktuálnej session |
|
|
| GET | `/auth/me` | ✅ | - | Profil aktuálneho usera |
|
|
|
|
**Príklad: Login request**
|
|
```javascript
|
|
POST /api/auth/login
|
|
Content-Type: application/json
|
|
|
|
{
|
|
"username": "jan.novak",
|
|
"password": "TempPass123!"
|
|
}
|
|
|
|
// Response:
|
|
{
|
|
"success": true,
|
|
"data": {
|
|
"user": { "id": "...", "username": "jan.novak", "role": "member" },
|
|
"needsPasswordChange": true,
|
|
"needsEmailSetup": true
|
|
}
|
|
}
|
|
// + Sets accessToken cookie (httpOnly)
|
|
```
|
|
|
|
---
|
|
|
|
### Admin (`/api/admin`) - **Len pre adminov**
|
|
|
|
| Method | Endpoint | Auth | Role | Popis |
|
|
|--------|----------|------|------|-------|
|
|
| POST | `/admin/users` | ✅ | Admin | Vytvorenie nového používateľa |
|
|
| GET | `/admin/users` | ✅ | Admin | Zoznam všetkých používateľov |
|
|
| GET | `/admin/users/:userId` | ✅ | Admin | Detail používateľa |
|
|
| PATCH | `/admin/users/:userId/role` | ✅ | Admin | Zmena role používateľa |
|
|
| DELETE | `/admin/users/:userId` | ✅ | Admin | Zmazanie používateľa |
|
|
|
|
**Príklad: Vytvorenie používateľa**
|
|
```javascript
|
|
POST /api/admin/users
|
|
Authorization: Bearer <admin-token>
|
|
Content-Type: application/json
|
|
|
|
{
|
|
"username": "jana.horvathova",
|
|
"firstName": "Jana",
|
|
"lastName": "Horvathova",
|
|
"email": "jana.horvathova@truemail.sk", // voliteľné
|
|
"emailPassword": "jmap-password-123" // voliteľné
|
|
}
|
|
|
|
// Response:
|
|
{
|
|
"success": true,
|
|
"data": {
|
|
"user": {
|
|
"id": "...",
|
|
"username": "jana.horvathova",
|
|
"tempPassword": "X9kL#mP2qR4w", // ← Admin si toto skopíruje!
|
|
"emailSetup": true // true ak bol email poskytnutý
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### Kontakty (`/api/contacts`)
|
|
|
|
| Method | Endpoint | Auth | Role | Popis |
|
|
|--------|----------|------|------|-------|
|
|
| GET | `/contacts` | ✅ | - | Všetky kontakty usera |
|
|
| GET | `/contacts/discover` | ✅ | - | Potenciálne kontakty (z JMAP) |
|
|
| POST | `/contacts` | ✅ | - | Pridať kontakt + sync jeho emailov |
|
|
| PATCH | `/contacts/:id` | ✅ | - | Upraviť kontakt |
|
|
| DELETE | `/contacts/:id` | ✅ | - | Odstrániť kontakt |
|
|
|
|
**Príklad: Objavenie potenciálnych kontaktov**
|
|
```javascript
|
|
GET /api/contacts/discover?limit=50&search=john
|
|
Authorization: Bearer <token>
|
|
|
|
// Response:
|
|
{
|
|
"success": true,
|
|
"data": [
|
|
{
|
|
"email": "john.doe@example.com",
|
|
"name": "John Doe",
|
|
"emailCount": 15,
|
|
"isContact": false, // Ešte nie je v kontaktoch
|
|
"lastEmailDate": "2024-11-18T10:30:00Z"
|
|
},
|
|
...
|
|
]
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### Emaily (`/api/emails`)
|
|
|
|
| Method | Endpoint | Auth | Role | Popis |
|
|
|--------|----------|------|------|-------|
|
|
| GET | `/emails` | ✅ | - | Všetky emaily usera |
|
|
| GET | `/emails/search?q=...` | ✅ | - | Vyhľadávanie v emailoch |
|
|
| GET | `/emails/unread-count` | ✅ | - | Počet neprečítaných emailov |
|
|
| POST | `/emails/sync` | ✅ | - | Manuálna synchronizácia z JMAP |
|
|
| GET | `/emails/thread/:threadId` | ✅ | - | Thread (konverzácia) |
|
|
| POST | `/emails/thread/:threadId/read` | ✅ | - | Označiť thread ako prečítaný |
|
|
| GET | `/emails/contact/:contactId` | ✅ | - | Emaily od konkrétneho kontaktu |
|
|
| PATCH | `/emails/:jmapId/read` | ✅ | - | Označiť email ako prečítaný |
|
|
| POST | `/emails/reply` | ✅ | - | Odpovedať na email cez JMAP |
|
|
|
|
**Príklad: Odpoveď na email**
|
|
```javascript
|
|
POST /api/emails/reply
|
|
Authorization: Bearer <token>
|
|
Content-Type: application/json
|
|
|
|
{
|
|
"to": "john.doe@example.com",
|
|
"subject": "Re: Project Update",
|
|
"body": "Thanks for the update!",
|
|
"inReplyTo": "<message-id@example.com>",
|
|
"threadId": "thread-abc123"
|
|
}
|
|
|
|
// Response:
|
|
{
|
|
"success": true,
|
|
"data": {
|
|
"messageId": "<new-message-id@truemail.sk>",
|
|
"sentAt": "2024-11-18T15:45:00Z"
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 📧 JMAP Email integrácia
|
|
|
|
### Čo je JMAP?
|
|
**JMAP (JSON Meta Application Protocol)** je moderný email protocol, náhrada za IMAP/SMTP.
|
|
|
|
**Výhody:**
|
|
- JSON-based API (nie custom text protocol ako IMAP)
|
|
- Stateless (REST-like)
|
|
- Efektívnejší bandwidth (delta updates)
|
|
- Lepší pre mobile aplikácie
|
|
|
|
### Truemail.sk integrácia
|
|
|
|
**JMAP Server:** `https://mail.truemail.sk/jmap/`
|
|
|
|
**Autentifikácia:**
|
|
```javascript
|
|
// Basic Auth
|
|
username: "user@truemail.sk"
|
|
password: "user-email-password"
|
|
```
|
|
|
|
**JMAP Session:**
|
|
```javascript
|
|
// 1. Získaj session info
|
|
GET https://mail.truemail.sk/jmap/
|
|
Authorization: Basic <base64-credentials>
|
|
|
|
// Response obsahuje:
|
|
{
|
|
"accounts": {
|
|
"ba": { ... } // ← accountId
|
|
},
|
|
"apiUrl": "https://mail.truemail.sk/jmap/"
|
|
}
|
|
```
|
|
|
|
### Synchronizácia emailov
|
|
|
|
**Kedy sa syncujú emaily:**
|
|
1. ✅ Pri pridaní nového kontaktu - automaticky sa stiahnu všetky jeho emaily
|
|
2. ✅ Manuálne cez `POST /api/emails/sync`
|
|
3. ✅ Automaticky každých 60 sekúnd (frontend polling)
|
|
|
|
**Proces:**
|
|
```javascript
|
|
// Service layer: src/services/jmap.service.js
|
|
export const syncContactEmails = async (user, contactEmail) => {
|
|
const jmapConfig = getJmapConfig(user)
|
|
|
|
// 1. JMAP Email/query - nájdi emaily od/pre kontakt
|
|
const emailIds = await jmapRequest(jmapConfig, [
|
|
['Email/query', {
|
|
accountId: jmapConfig.accountId,
|
|
filter: {
|
|
from: contactEmail
|
|
// OR to: contactEmail
|
|
}
|
|
}]
|
|
])
|
|
|
|
// 2. JMAP Email/get - získaj detaily emailov
|
|
const emails = await jmapRequest(jmapConfig, [
|
|
['Email/get', {
|
|
accountId: jmapConfig.accountId,
|
|
ids: emailIds
|
|
}]
|
|
])
|
|
|
|
// 3. Ulož do DB (emails table)
|
|
await saveEmailsToDB(emails, user.id, contact.id)
|
|
}
|
|
```
|
|
|
|
**Thread support:**
|
|
- Emaily sú zoskupené podľa `threadId` (JMAP native)
|
|
- `inReplyTo` header pre reply chains
|
|
- Frontend zobrazuje ako konverzácie
|
|
|
|
---
|
|
|
|
## 🔒 Bezpečnosť
|
|
|
|
### 1. Password Security
|
|
|
|
**Bcrypt hashing:**
|
|
```javascript
|
|
// 12 rounds (2^12 iterations)
|
|
const hash = await bcrypt.hash(password, 12)
|
|
```
|
|
|
|
**Strong password policy:**
|
|
- Minimálne 8 znakov
|
|
- Aspoň 1 veľké písmeno
|
|
- Aspoň 1 malé písmeno
|
|
- Aspoň 1 číslo
|
|
- Aspoň 1 špeciálny znak
|
|
|
|
**Email password encryption:**
|
|
```javascript
|
|
// AES-256-GCM encryption pre JMAP password
|
|
const encrypted = encryptPassword(emailPassword)
|
|
// Formát: "iv:authTag:encryptedText"
|
|
```
|
|
|
|
### 2. JWT Token Security
|
|
|
|
**Access Token:**
|
|
- Krátka životnosť (1 hodina)
|
|
- HttpOnly cookie (nedostupný pre JavaScript)
|
|
- Secure flag (len HTTPS v produkcii)
|
|
- SameSite=Strict (CSRF protection)
|
|
|
|
**Token generation:**
|
|
```javascript
|
|
// src/utils/jwt.js
|
|
export const generateAccessToken = (user) => {
|
|
return jwt.sign(
|
|
{ id: user.id, role: user.role },
|
|
process.env.JWT_SECRET,
|
|
{ expiresIn: '1h' }
|
|
)
|
|
}
|
|
```
|
|
|
|
### 3. Rate Limiting
|
|
|
|
**Login protection:**
|
|
```javascript
|
|
// 5 pokusov za 15 minút
|
|
loginRateLimiter: {
|
|
windowMs: 15 * 60 * 1000,
|
|
max: 5
|
|
}
|
|
```
|
|
|
|
**API protection:**
|
|
```javascript
|
|
// Development: 1000 req/15min
|
|
// Production: 100 req/15min
|
|
apiRateLimiter: {
|
|
windowMs: 15 * 60 * 1000,
|
|
max: process.env.NODE_ENV === 'production' ? 100 : 1000
|
|
}
|
|
```
|
|
|
|
**Sensitive operations:**
|
|
```javascript
|
|
// Password changes, email changes
|
|
// Production: 3 pokusy za 15 minút
|
|
sensitiveOperationLimiter: { max: 3 }
|
|
```
|
|
|
|
### 4. HTTP Security Headers (Helmet)
|
|
|
|
```javascript
|
|
helmet({
|
|
contentSecurityPolicy: {
|
|
directives: {
|
|
defaultSrc: ["'self'"],
|
|
styleSrc: ["'self'", "'unsafe-inline'"]
|
|
}
|
|
},
|
|
hsts: {
|
|
maxAge: 31536000, // 1 rok
|
|
includeSubDomains: true,
|
|
preload: true
|
|
}
|
|
})
|
|
```
|
|
|
|
### 5. Input Validation (Zod)
|
|
|
|
**Všetky user inputy sú validované:**
|
|
```javascript
|
|
// src/validators/auth.validators.js
|
|
export const loginSchema = z.object({
|
|
username: z.string().min(3).max(50),
|
|
password: z.string().min(8)
|
|
})
|
|
|
|
// Middleware
|
|
validateBody(loginSchema)
|
|
```
|
|
|
|
### 6. SQL Injection Protection
|
|
|
|
**Drizzle ORM používa prepared statements:**
|
|
```javascript
|
|
// ✅ Bezpečné - parametrizované query
|
|
await db.select()
|
|
.from(users)
|
|
.where(eq(users.username, username)) // ← automaticky escaped
|
|
```
|
|
|
|
### 7. CORS Configuration
|
|
|
|
```javascript
|
|
cors({
|
|
origin: process.env.CORS_ORIGIN || 'http://localhost:5173',
|
|
credentials: true, // Povoľ cookies
|
|
optionsSuccessStatus: 200
|
|
})
|
|
```
|
|
|
|
### 8. Audit Logging
|
|
|
|
**Každá dôležitá akcia je logovaná:**
|
|
```javascript
|
|
await logAuditEvent({
|
|
userId: user.id,
|
|
action: 'login',
|
|
resource: 'auth',
|
|
ipAddress: req.ip,
|
|
userAgent: req.headers['user-agent'],
|
|
success: true
|
|
})
|
|
```
|
|
|
|
---
|
|
|
|
## 📦 Inštalácia a spustenie
|
|
|
|
### Požiadavky
|
|
- **Node.js 18+** a **npm 9+**
|
|
- **PostgreSQL 16+**
|
|
- **Docker** (voliteľné, pre lokálne DB)
|
|
|
|
## Inštalácia
|
|
|
|
### 1. Klonovanie a Dependencies
|
|
|
|
```bash
|
|
npm install
|
|
```
|
|
|
|
### 2. Konfigurácia Environment Variables
|
|
|
|
Skopírujte `.env.example` do `.env` a upravte hodnoty:
|
|
|
|
```bash
|
|
cp .env.example .env
|
|
```
|
|
|
|
Dôležité premenné:
|
|
- `JWT_SECRET` - Zmeňte v produkcii!
|
|
- `JWT_REFRESH_SECRET` - Zmeňte v produkcii!
|
|
- `BETTER_AUTH_SECRET` - Zmeňte v produkcii!
|
|
- `DB_*` - Databázové credentials
|
|
- `JMAP_*` - Email server credentials
|
|
|
|
### 3. Databáza Setup
|
|
|
|
Spustite PostgreSQL (Docker):
|
|
|
|
```bash
|
|
docker compose up -d postgres
|
|
```
|
|
|
|
Vygenerujte a aplikujte migrácie:
|
|
|
|
```bash
|
|
npm run db:generate
|
|
npm run db:migrate
|
|
```
|
|
|
|
### 4. Seed Admin Account
|
|
|
|
Vytvorte prvý admin účet:
|
|
|
|
```bash
|
|
npm run db:seed
|
|
```
|
|
|
|
**DÔLEŽITÉ:** Uložte si vygenerované temporary password!
|
|
|
|
### 5. Spustenie Servera
|
|
|
|
Development mode:
|
|
|
|
```bash
|
|
npm run dev
|
|
```
|
|
|
|
Production mode:
|
|
|
|
```bash
|
|
npm start
|
|
```
|
|
|
|
Server beží na `http://localhost:5000`
|
|
|
|
## API Endpointy
|
|
|
|
### Public Endpoints
|
|
|
|
#### Login
|
|
```http
|
|
POST /api/auth/login
|
|
Content-Type: application/json
|
|
|
|
{
|
|
"username": "admin",
|
|
"password": "temporary-password"
|
|
}
|
|
```
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": {
|
|
"user": { ... },
|
|
"tokens": {
|
|
"accessToken": "...",
|
|
"refreshToken": "..."
|
|
},
|
|
"needsPasswordChange": true,
|
|
"needsEmailSetup": true
|
|
}
|
|
}
|
|
```
|
|
|
|
### Protected Endpoints (vyžadujú JWT token)
|
|
|
|
#### Set New Password (Krok 2)
|
|
```http
|
|
POST /api/auth/set-password
|
|
Authorization: Bearer <token>
|
|
Content-Type: application/json
|
|
|
|
{
|
|
"newPassword": "NewSecurePass123!",
|
|
"confirmPassword": "NewSecurePass123!"
|
|
}
|
|
```
|
|
|
|
#### Link Email (Krok 3)
|
|
```http
|
|
POST /api/auth/link-email
|
|
Authorization: Bearer <token>
|
|
Content-Type: application/json
|
|
|
|
{
|
|
"email": "user@example.com"
|
|
}
|
|
```
|
|
|
|
#### Skip Email Setup
|
|
```http
|
|
POST /api/auth/skip-email
|
|
Authorization: Bearer <token>
|
|
```
|
|
|
|
#### Get Current User
|
|
```http
|
|
GET /api/auth/me
|
|
Authorization: Bearer <token>
|
|
```
|
|
|
|
#### Logout
|
|
```http
|
|
POST /api/auth/logout
|
|
Authorization: Bearer <token>
|
|
```
|
|
|
|
### Admin Endpoints (vyžadujú admin rolu)
|
|
|
|
#### Create User
|
|
```http
|
|
POST /api/admin/users
|
|
Authorization: Bearer <admin-token>
|
|
Content-Type: application/json
|
|
|
|
{
|
|
"username": "newuser",
|
|
"tempPassword": "TempPass123!",
|
|
"role": "member",
|
|
"firstName": "John",
|
|
"lastName": "Doe"
|
|
}
|
|
```
|
|
|
|
#### Get All Users
|
|
```http
|
|
GET /api/admin/users
|
|
Authorization: Bearer <admin-token>
|
|
```
|
|
|
|
#### Get User by ID
|
|
```http
|
|
GET /api/admin/users/:userId
|
|
Authorization: Bearer <admin-token>
|
|
```
|
|
|
|
#### Change User Role
|
|
```http
|
|
PATCH /api/admin/users/:userId/role
|
|
Authorization: Bearer <admin-token>
|
|
Content-Type: application/json
|
|
|
|
{
|
|
"role": "admin"
|
|
}
|
|
```
|
|
|
|
#### Delete User
|
|
```http
|
|
DELETE /api/admin/users/:userId
|
|
Authorization: Bearer <admin-token>
|
|
```
|
|
|
|
## Databázová Schéma
|
|
|
|
### Users Table
|
|
```
|
|
- id (UUID)
|
|
- username (unique)
|
|
- email (unique, nullable)
|
|
- email_verified (boolean)
|
|
- first_name
|
|
- last_name
|
|
- password (bcrypt hash)
|
|
- temp_password (bcrypt hash)
|
|
- changed_password (boolean)
|
|
- role (enum: admin, member)
|
|
- last_login
|
|
- created_at
|
|
- updated_at
|
|
```
|
|
|
|
### Sessions Table
|
|
```
|
|
- id (text)
|
|
- user_id (UUID, FK)
|
|
- expires_at
|
|
- ip_address
|
|
- user_agent
|
|
- created_at
|
|
```
|
|
|
|
### Audit Logs Table
|
|
```
|
|
- id (UUID)
|
|
- user_id (UUID, FK, nullable)
|
|
- action
|
|
- resource
|
|
- resource_id
|
|
- old_value (JSON)
|
|
- new_value (JSON)
|
|
- ip_address
|
|
- user_agent
|
|
- success (boolean)
|
|
- error_message
|
|
- created_at
|
|
```
|
|
|
|
## Validačné Pravidlá
|
|
|
|
### Password Policy
|
|
- Minimálne 8 znakov
|
|
- Aspoň 1 veľké písmeno
|
|
- Aspoň 1 malé písmeno
|
|
- Aspoň 1 číslo
|
|
- Aspoň 1 špeciálny znak
|
|
|
|
### Username Policy
|
|
- 3-50 znakov
|
|
- Iba písmená, čísla, pomlčky a podčiarkovníky
|
|
|
|
## Rate Limiting
|
|
|
|
- **Login endpoint:** 5 pokusov / 15 minút
|
|
- **API endpoints:** 100 requestov / 15 minút
|
|
- **Citlivé operácie:** 3 pokusy / 15 minút
|
|
|
|
## Audit Logging
|
|
|
|
Všetky dôležité akcie sú automaticky logované:
|
|
- Login attempts (úspešné aj neúspešné)
|
|
- Password changes
|
|
- Email linking & verification
|
|
- Role changes
|
|
- User creation
|
|
- A ďalšie...
|
|
|
|
## Security Best Practices
|
|
|
|
1. **HTTPS Only** - V produkcii vždy používajte HTTPS
|
|
2. **Strong Secrets** - Zmeňte všetky secret keys v `.env`
|
|
3. **Regular Updates** - Aktualizujte dependencies
|
|
4. **Monitor Logs** - Sledujte audit logs
|
|
5. **Backup Database** - Pravidelné zálohy PostgreSQL
|
|
|
|
## Database Commands
|
|
|
|
```bash
|
|
# Generovanie migrácií
|
|
npm run db:generate
|
|
|
|
# Aplikovanie migrácií
|
|
npm run db:migrate
|
|
|
|
# Push schema (alternative to migrations)
|
|
npm run db:push
|
|
|
|
# Drizzle Studio (GUI)
|
|
npm run db:studio
|
|
|
|
# Seed admin account
|
|
npm run db:seed
|
|
```
|
|
|
|
## Testing
|
|
|
|
Spustenie testov:
|
|
|
|
```bash
|
|
npm test
|
|
```
|
|
|
|
## 📁 Štruktúra projektu
|
|
|
|
```
|
|
crm-server/
|
|
├── src/
|
|
│ ├── api/ # (unused, legacy?)
|
|
│ ├── config/
|
|
│ │ ├── database.js # Drizzle DB connection
|
|
│ │ └── jmap.js # JMAP configuration
|
|
│ │
|
|
│ ├── controllers/ # Request handlers
|
|
│ │ ├── auth.controller.js # Login, set password, link email
|
|
│ │ ├── admin.controller.js # User management (admin only)
|
|
│ │ ├── contact.controller.js # Contact CRUD
|
|
│ │ └── crm-email.controller.js # Email sync, search, replies
|
|
│ │
|
|
│ ├── db/
|
|
│ │ ├── schema.js # Drizzle tables (users, contacts, emails, audit_logs)
|
|
│ │ ├── migrate.js # Migration runner
|
|
│ │ ├── migrations/ # Auto-generated SQL migrations
|
|
│ │ └── seeds/
|
|
│ │ ├── admin.seed.js # Seed admin account
|
|
│ │ └── testuser.seed.js # Seed test user
|
|
│ │
|
|
│ ├── middlewares/
|
|
│ │ ├── auth/
|
|
│ │ │ ├── authMiddleware.js # JWT validation (authenticate)
|
|
│ │ │ └── roleMiddleware.js # Role check (requireAdmin, requireOwnerOrAdmin)
|
|
│ │ ├── security/
|
|
│ │ │ ├── rateLimiter.js # Login, API, sensitive operation limiters
|
|
│ │ │ └── validateInput.js # Zod schema validation
|
|
│ │ └── global/
|
|
│ │ ├── errorHandler.js # Global error handler
|
|
│ │ ├── notFound.js # 404 handler
|
|
│ │ └── validateBody.js # Body validation
|
|
│ │
|
|
│ ├── routes/ # API route definitions
|
|
│ │ ├── auth.routes.js # /api/auth/* (login, set-password, link-email)
|
|
│ │ ├── admin.routes.js # /api/admin/* (user management)
|
|
│ │ ├── contact.routes.js # /api/contacts/* (CRUD)
|
|
│ │ └── crm-email.routes.js # /api/emails/* (sync, search, reply)
|
|
│ │
|
|
│ ├── services/ # Business logic layer
|
|
│ │ ├── auth.service.js # User authentication, password management
|
|
│ │ ├── contact.service.js # Contact management
|
|
│ │ ├── crm-email.service.js # Email database operations
|
|
│ │ ├── jmap.service.js # JMAP API integration (Truemail.sk)
|
|
│ │ ├── email.service.js # Email utilities
|
|
│ │ └── audit.service.js # Audit logging
|
|
│ │
|
|
│ ├── validators/ # Zod validation schemas
|
|
│ │ └── auth.validators.js # Login, password, email schemas
|
|
│ │
|
|
│ ├── utils/ # Helper utilities
|
|
│ │ ├── errors.js # Custom error classes
|
|
│ │ ├── jwt.js # JWT generation & verification
|
|
│ │ ├── logger.js # Logging utility
|
|
│ │ └── password.js # Bcrypt hashing, temp password generation, encryption
|
|
│ │
|
|
│ ├── app.js # Express app configuration
|
|
│ └── index.js # Server entry point
|
|
│
|
|
├── __tests__/ # Jest test files
|
|
├── postgres/ # Docker volume for PostgreSQL data
|
|
├── .env.example # Environment variables template
|
|
├── .env # Environment variables (gitignored)
|
|
├── docker-compose.yml # PostgreSQL Docker setup
|
|
├── drizzle.config.js # Drizzle Kit configuration
|
|
├── package.json # NPM dependencies and scripts
|
|
└── README.md # This file
|
|
```
|
|
|
|
### Kľúčové súbory
|
|
|
|
**`src/app.js`**
|
|
- Express aplikácia setup
|
|
- Middleware chain (security, CORS, parsing, rate limiting)
|
|
- Route mounting
|
|
- Global error handling
|
|
|
|
**`src/db/schema.js`**
|
|
- Drizzle ORM schema definície
|
|
- Tables: users, contacts, emails, audit_logs
|
|
- Relationships a indexy
|
|
|
|
**`src/services/jmap.service.js`**
|
|
- JMAP API client pre Truemail.sk
|
|
- Email sync, search, send reply
|
|
- Mailbox management
|
|
|
|
**`src/middlewares/auth/authMiddleware.js`**
|
|
- JWT token validation
|
|
- User session loading
|
|
- Pridáva `req.user` do requestu
|
|
|
|
**`src/middlewares/auth/roleMiddleware.js`**
|
|
- Role-based access control
|
|
- `requireAdmin` - blokuje non-adminov
|
|
- `requireOwnerOrAdmin` - vlastník alebo admin
|
|
|
|
---
|
|
|
|
## 🧪 Testing
|
|
|
|
### Spustenie testov
|
|
|
|
```bash
|
|
npm test
|
|
```
|
|
|
|
### Test coverage (TODO)
|
|
- Unit testy pre services
|
|
- Integration testy pre API endpoints
|
|
- E2E testy pre autentifikačný flow
|
|
|
|
---
|
|
|
|
## 🚀 Deployment
|
|
|
|
### Production Checklist
|
|
|
|
**1. Environment Variables**
|
|
```bash
|
|
# Zmeň všetky secret keys!
|
|
JWT_SECRET=<strong-random-key>
|
|
JWT_REFRESH_SECRET=<strong-random-key>
|
|
BETTER_AUTH_SECRET=<strong-random-key>
|
|
|
|
# Production settings
|
|
NODE_ENV=production
|
|
CORS_ORIGIN=https://your-frontend-domain.com
|
|
RATE_LIMIT_MAX_REQUESTS=100
|
|
|
|
# Database
|
|
DB_HOST=your-production-db-host
|
|
DB_PORT=5432
|
|
DB_USER=crm_user
|
|
DB_PASSWORD=<strong-db-password>
|
|
DB_NAME=crm_production
|
|
```
|
|
|
|
**2. HTTPS Setup**
|
|
- Použite reverse proxy (Nginx, Caddy)
|
|
- SSL certifikáty (Let's Encrypt)
|
|
- Redirect HTTP → HTTPS
|
|
|
|
**3. Database**
|
|
- Povolte SSL connections
|
|
- Pravidelné backupy (pg_dump)
|
|
- Monitoring (pg_stat_statements)
|
|
|
|
**4. Security**
|
|
- Firewall rules (povoľ len potrebné porty)
|
|
- DDoS protection (Cloudflare, AWS Shield)
|
|
- Monitoring a alerting (Sentry, DataDog)
|
|
|
|
**5. Deployment Platforms**
|
|
- **VPS:** DigitalOcean, Linode, Vultr
|
|
- **Cloud:** AWS (EC2, RDS), Google Cloud, Azure
|
|
- **Platform:** Railway, Render, Fly.io
|
|
|
|
### Docker Deployment
|
|
|
|
```dockerfile
|
|
# Dockerfile
|
|
FROM node:18-alpine
|
|
|
|
WORKDIR /app
|
|
|
|
COPY package*.json ./
|
|
RUN npm ci --only=production
|
|
|
|
COPY . .
|
|
|
|
EXPOSE 5000
|
|
|
|
CMD ["npm", "start"]
|
|
```
|
|
|
|
```bash
|
|
# Build
|
|
docker build -t crm-server .
|
|
|
|
# Run
|
|
docker run -d \
|
|
--name crm-api \
|
|
-p 5000:5000 \
|
|
--env-file .env \
|
|
crm-server
|
|
```
|
|
|
|
---
|
|
|
|
## 📝 TODO / Budúce vylepšenia
|
|
|
|
- [ ] Refresh token rotation
|
|
- [ ] Email verification (2FA)
|
|
- [ ] Password reset flow
|
|
- [ ] User profile pictures
|
|
- [ ] Advanced audit log filtering
|
|
- [ ] Webhook support
|
|
- [ ] Real-time notifications (WebSockets)
|
|
- [ ] Multi-tenant support
|
|
- [ ] API rate limiting per user
|
|
- [ ] Automated backup system
|
|
|
|
---
|
|
|
|
## 🐛 Known Issues
|
|
|
|
- JMAP sync môže byť pomalý pri veľkom množstve emailov (optimalizovať batch size)
|
|
- Rate limiter je in-memory (nepersistuje cez reštarty) - zvážiť Redis
|
|
- Session refresh token nemá automatic rotation
|
|
|
|
---
|
|
|
|
## 📞 Podpora a Kontakt
|
|
|
|
Pre technické otázky a bug reporting:
|
|
- Vytvor Issue v repozitári
|
|
- Email: richard@example.com
|
|
|
|
---
|
|
|
|
## 📄 Licencia
|
|
|
|
MIT License
|
|
|
|
Copyright (c) 2024 Richard Tekula
|
|
|
|
---
|
|
|
|
## 👨💻 Autor
|
|
|
|
**Richard Tekula**
|
|
Backend Developer
|
|
|
|
---
|
|
|
|
**⚠️ PRODUCTION WARNING:**
|
|
|
|
Tento projekt je v aktívnom vývoji. Pred production deploymentom nezabudni:
|
|
|
|
1. ✅ Zmeniť všetky secret keys v `.env`
|
|
2. ✅ Nastaviť HTTPS (reverse proxy)
|
|
3. ✅ Konfigurovať firewall pravidlá
|
|
4. ✅ Nastaviť monitoring a alerting (Sentry, Prometheus)
|
|
5. ✅ Pravidelné security audity (`npm audit`)
|
|
6. ✅ Database backupy (automatizované)
|
|
7. ✅ Load balancing (pri vysokom trafficu)
|
|
8. ✅ CDN pre static assets
|
|
9. ✅ GDPR compliance (ak potrebné)
|
|
|
|
**Happy coding! 🚀**
|