Tmavý režim
Vývojové prostředí
Nastavení
Prerekvizity
- Node.js 20+
- Docker & Docker Compose
- Git
Instalace
bash
git clone <repo-url>
cd atrea-user-api
npm installSpuštění s Dockerem (doporučeno)
bash
# Celý stack (DB, Zitadel, API, ...)
docker compose up -d
# Sledování logů
docker compose logs -f atrea-user-apiSpuštění bez Dockeru
bash
# Potřebujete lokální PostgreSQL 16
createdb atrea-user-db
psql atrea-user-db < sql/init.sql
psql atrea-user-db < sql/data.sql
# TypeScript watch + nodemon
npm run devSkripty
bash
npm run dev # TypeScript watch + nodemon (hot reload)
npm run build # Kompilace TS → dist/
npm start # Build + spuštění produkce
npm run lint # ESLint kontrola
npm run lint:fix # ESLint autofix
npm run docs:dev # VitePress dev server (dokumentace)
npm run docs:build # Build dokumentace → docs/.vitepress/dist/
npm run docs:preview # Preview buildu dokumentacePřidání nového endpointu
1. Vytvořte nebo upravte router
typescript
// src/routes/myRouter.ts
import { Router } from 'express';
import { body, param } from 'express-validator';
import { ApiResponse } from '../utils/ApiResponse';
import { authorizationMiddleware, loadSuperAdminFlag, requireAppAdmin } from '../middleware/authorization';
import MyService from '../services/myService';
const router = Router();
/**
* @swagger
* /my-endpoint:
* get:
* summary: Popis endpointu
* tags: [MyTag]
* security:
* - bearerAuth: []
* responses:
* 200:
* description: Úspěch
*/
router.get('/my-endpoint',
authorizationMiddleware(),
loadSuperAdminFlag(),
requireAppAdmin(),
ApiResponse.asyncHandle(async (req, res) => {
const data = await MyService.getData(req.token.email);
ApiResponse.success(res, data);
})
);
export default router;2. Zaregistrujte router
typescript
// src/routes/router.ts
import myRouter from './myRouter';
router.use('/my-resource', myRouter);3. Přidejte Swagger dokumentaci
Swagger se generuje z JSDoc komentářů v routerech (viz výše). Swagger spec se načítá z ./src/routes/*.ts.
Přidání nové entity
typescript
// src/entities/MyEntity.ts
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn } from 'typeorm';
@Entity('my_entities')
export class MyEntity {
@PrimaryGeneratedColumn()
id: number;
@Column({ unique: true })
code: string;
@Column({ nullable: true })
description: string | null;
@CreateDateColumn({ name: 'created_at' })
createdAt: Date;
}Nezapomeňte přidat tabulku do sql/init.sql a entitu do AppDataSource v src/utils/data-source.ts.
ApiResponse wrapper
Všechny odpovědi by měly jít přes ApiResponse:
typescript
import { ApiResponse } from '../utils/ApiResponse';
// Úspěch
ApiResponse.success(res, { data: 'value' }); // 200
ApiResponse.success(res, { data: 'value' }, 201); // 201 Created
// Chyba
ApiResponse.error(res, 404, 'Not found');
ApiResponse.error(res, 403, 'Forbidden', 'PERMISSION_DENIED');
// Async handler (automaticky zachytí výjimky)
router.get('/endpoint', ApiResponse.asyncHandle(async (req, res) => {
const data = await service.getData();
ApiResponse.success(res, data);
}));ApiException
Pro chyby s HTTP kódem:
typescript
import { ApiException } from '../utils/ApiException';
import { ApiExceptionCode } from '../utils/ApiExceptionCode';
// V service
throw new ApiException(404, 'Uživatel nenalezen', ApiExceptionCode.NOT_FOUND);
// S detaily
throw new ApiException(422, 'Validační chyba', ApiExceptionCode.VALIDATION_ERROR, {
field: 'email',
message: 'Neplatný formát emailu'
});Logování
typescript
import logger from '../utils/logger';
// V service nebo route handleru
logger.info('Operace proběhla', { email, appCode });
logger.error('Neočekávaná chyba', { error, context });
logger.warn('Podezřelé chování', { detail });
logger.debug('Debug info', { query });Validace requestů
Pomocí express-validator:
typescript
import { body, param, query, validationResult } from 'express-validator';
router.post('/endpoint',
body('email').isEmail().normalizeEmail(),
body('name').isString().trim().notEmpty(),
body('count').optional().isInt({ min: 1, max: 100 }),
param('id').isInt(),
ApiResponse.asyncHandle(async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return ApiResponse.error(res, 422, 'Validation failed', undefined, errors.array());
}
// ...
})
);Struktura TypeORM DataSource
typescript
// src/utils/data-source.ts
export const AppDataSource = new DataSource({
type: 'postgres',
host: config.database.host,
port: config.database.port,
username: config.database.user,
password: config.database.password,
database: config.database.database,
synchronize: false,
logging: config.database.logging,
entities: [
User, Application, Permission, Role, RolePermission,
UserAppRole, UserResourcePermission, AppAdmin, SuperAdmin,
UserProfile, UserAppPreference, Notification, NotificationTemplate,
NotificationType, UserNotificationPreference, PushSubscription, Brand
],
});Databáze — užitečné příkazy
bash
# Připojení k DB v Dockeru
docker compose exec db psql -U app atrea-user-db
# Reset databáze
docker compose exec db psql -U app atrea-user-db < sql/drop.sql
docker compose exec db psql -U app atrea-user-db < sql/init.sql
docker compose exec db psql -U app atrea-user-db < sql/data.sql
# Výpis tabulek
\dt
# Výpis uživatelů
SELECT * FROM users;
# Výpis oprávnění
SELECT p.code, p.type, a.code AS app FROM permissions p
JOIN applications a ON a.id = p.application_id;Swagger dokumentace
Swagger spec se generuje z JSDoc komentářů v routerech při startu serveru.
Swagger UI: http://localhost:3001/api/docs
Swagger JSON: http://localhost:3001/api/docs.json
Při přidávání nového endpointu přidejte JSDoc s @swagger anotací:
typescript
/**
* @swagger
* /users/{email}:
* get:
* summary: Detail uživatele
* tags: [Users]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: email
* required: true
* schema:
* type: string
* responses:
* 200:
* description: Uživatel nalezen
* 404:
* description: Uživatel neexistuje
*/
router.get('/:email', ...);Dokumentace (VitePress)
Tato dokumentace je generována pomocí VitePress z souborů v docs/.
bash
# Dev server s live reloadem
npm run docs:dev
# Dostupné na http://localhost:5173/docs/
# Build statické stránky
npm run docs:build
# Výstup: docs/.vitepress/dist/
# Preview buildu
npm run docs:previewDokumentace je servovaná z Express jako statické soubory na /docs/ (po buildu).
Struktura dokumentace:
docs/
├── .vitepress/config.ts # VitePress konfigurace
├── index.md # Hlavní stránka
├── guide/ # Průvodce
├── auth/ # Autentizace
├── permissions/ # Systém oprávnění
├── api/ # API reference
├── notifications/ # Notifikace
├── configuration/ # Konfigurace
└── development/ # Tato stránka