Skip to content

Vývojové prostředí

Nastavení

Prerekvizity

  • Node.js 20+
  • Docker & Docker Compose
  • Git

Instalace

bash
git clone <repo-url>
cd atrea-user-api
npm install

Spuš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-api

Spuš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 dev

Skripty

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 dokumentace

Př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:preview

Dokumentace 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

Atrea User API — interní dokumentace