refactor: Modularize IAM service routes into dedicated modules and add home page description

This commit is contained in:
Ho Ngoc Hai
2026-01-04 13:53:34 +07:00
parent 76b1c9b3a0
commit 7e7a7a5853
14 changed files with 4025 additions and 452 deletions

View File

@@ -1,7 +1,7 @@
'use client';
import { useAuthStore } from '@/stores/auth-store';
import { useEffect } from 'react';
import { useEffect, useState } from 'react';
import { useTranslation } from '@/hooks/use-translation';
/**
@@ -16,39 +16,84 @@ export default function Home() {
// VI: Lấy trạng thái xác thực từ store
const { user, isAuthenticated, isLoading, fetchUser } = useAuthStore();
// EN: State to track if component is mounted on client
// VI: State để theo dõi xem component đã mount trên client chưa
const [isMounted, setIsMounted] = useState(false);
// EN: Set mounted state to true on client side
// VI: Đặt trạng thái mounted thành true ở phía client
useEffect(() => {
setIsMounted(true);
}, []);
// EN: Fetch user data on component mount if not authenticated
// VI: Fetch dữ liệu user khi component mount nếu chưa xác thực
useEffect(() => {
if (!isAuthenticated && !isLoading) {
if (isMounted && !isAuthenticated) {
fetchUser();
}
}, [isAuthenticated, isLoading, fetchUser]);
// EN: Only run once when mounted or fetchUser changes (which is stable)
// VI: Chỉ chạy một lần khi mounted hoặc fetchUser thay đổi (ổn định)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isMounted]);
// EN: Prevent hydration mismatch by not rendering auth-dependent content until mounted
// VI: Ngăn chặn hydration mismatch bằng cách không render nội dung phụ thuộc auth cho đến khi mounted
if (!isMounted) {
return null;
}
// EN: Show loading state while checking authentication
// VI: Hiển thị trạng thái loading trong khi kiểm tra xác thực
if (isLoading) {
return <div className="p-8">{t('common.loading')}</div>;
return <div className="min-h-screen flex items-center justify-center p-8 bg-bg-primary text-text-primary">{t('common.loading')}</div>;
}
return (
// EN: Main content area with responsive padding
// VI: Khu vực nội dung chính với padding responsive
<main className="min-h-screen p-8">
<h1 className="text-4xl font-bold mb-4">{t('home.title')}</h1>
// EN: Main content area with centered layout for minimal aesthetic
// VI: Khu vực nội dung chính với layout căn giữa cho thẩm mỹ tối giản
<main className="min-h-screen flex flex-col items-center justify-center p-8 text-center bg-bg-primary text-text-primary">
<div className="max-w-container-md w-full space-y-8 animate-fadeIn">
{/* EN: Hero Title / VI: Tiêu đề Hero */}
<h1 className="text-6xl font-bold tracking-tight mb-2 font-display">
{t('home.title')}
</h1>
{/* EN: Conditional rendering based on authentication status / VI: Render có điều kiện dựa trên trạng thái xác thực */}
{isAuthenticated && user ? (
// EN: Authenticated user welcome message / VI: Thông báo chào mừng người dùng đã xác thực
<div>
<p>{t('home.welcome', { email: user.email })}</p>
<p>{t('home.role', { role: user.role })}</p>
{/* EN: Subtitle/Description / VI: Phụ đề/Mô tả */}
<p className="text-xl text-text-secondary max-w-lg mx-auto leading-relaxed">
{t('home.description')}
</p>
{/* EN: Conditional rendering based on authentication status / VI: Render có điều kiện dựa trên trạng thái xác thực */}
<div className="pt-8">
{isAuthenticated && user ? (
// EN: Authenticated user welcome message / VI: Thông báo chào mừng người dùng đã xác thực
<div className="space-y-4 p-6 rounded-2xl bg-bg-secondary border border-border-primary inline-block min-w-[300px]">
<div className="w-16 h-16 bg-bg-tertiary rounded-full mx-auto flex items-center justify-center mb-4 text-2xl">
{user.email?.[0].toUpperCase()}
</div>
<p className="text-lg font-medium">{t('home.welcome', { email: user.email })}</p>
<div className="inline-flex items-center px-3 py-1 rounded-full bg-bg-tertiary text-text-secondary text-sm">
<span className="w-2 h-2 rounded-full bg-accent-success mr-2"></span>
{t('home.role', { role: user.role })}
</div>
</div>
) : (
// EN: Login prompt for unauthenticated users / VI: Nhắc đăng nhập cho người dùng chưa xác thực
<div className="space-y-6">
<p className="text-text-secondary">{t('home.pleaseLogin')}</p>
<div className="flex gap-4 justify-center">
<a
href="/auth/login"
className="px-8 py-3 rounded-full bg-text-primary text-bg-primary font-medium hover:bg-text-secondary transition-colors"
>
Login
</a>
</div>
</div>
)}
</div>
) : (
// EN: Login prompt for unauthenticated users / VI: Nhắc đăng nhập cho người dùng chưa xác thực
<div>
<p>{t('home.pleaseLogin')}</p>
</div>
)}
</div>
</main>
);
}

View File

@@ -341,6 +341,7 @@
},
"home": {
"title": "GoodGo Platform",
"description": "Experience the next generation of minimal intelligence.",
"welcome": "Welcome, {email}!",
"role": "Role: {role}",
"pleaseLogin": "Please log in to continue."
@@ -396,4 +397,4 @@
"enable": "Enable"
}
}
}
}

View File

@@ -341,6 +341,7 @@
},
"home": {
"title": "Nền tảng GoodGo",
"description": "Trải nghiệm thế hệ tiếp theo của trí tuệ tối giản.",
"welcome": "Chào mừng, {email}!",
"role": "Vai trò: {role}",
"pleaseLogin": "Vui lòng đăng nhập để tiếp tục."
@@ -396,4 +397,4 @@
"enable": "Bật"
}
}
}
}

View File

@@ -14,163 +14,235 @@
*/
:root {
/* ============================================
/* ============================================
EN: Color Palette - Dark Mode (Primary Theme)
VI: Bảng màu - Dark Mode (Theme chính)
============================================ */
/* Background Colors / Màu nền */
--bg-primary: #0A0A0A; /* Almost black - Main background */
--bg-secondary: #121212; /* Dark grey - Card/Panel background */
--bg-tertiary: #1A1A1A; /* Dark grey - Hover states */
--bg-elevated: #242424; /* Elevated surfaces (modals, dropdowns) */
/* Background Colors / Màu nền */
--bg-primary: #000000;
/* Pure black - Main background */
--bg-secondary: #0A0A0A;
/* Almost black - Card/Panel background */
--bg-tertiary: #141414;
/* Very dark grey - Hover states */
--bg-elevated: #1A1A1A;
/* Elevated surfaces (modals, dropdowns) */
/* Text Colors (WCAG Compliant) / Màu chữ (tuân thủ WCAG) */
--text-primary: #FAFAFA; /* Off-white - Primary text (4.5:1 contrast) */
--text-secondary: #E0E0E0; /* Light grey - Secondary text */
--text-tertiary: #A0A0A0; /* Grey - Tertiary/disabled text */
--text-inverse: #1A1A1A; /* Dark - Text on light backgrounds */
/* Text Colors (WCAG Compliant) / Màu chữ (tuân thủ WCAG) */
--text-primary: #FFFFFF;
/* Pure white - Primary text */
--text-secondary: #888888;
/* Mid grey - Secondary text */
--text-tertiary: #444444;
/* Dark grey - Tertiary/disabled text */
--text-inverse: #000000;
/* Black - Text on light/white backgrounds */
/* Brand/Accent Colors / Màu thương hiệu/Accent */
--accent-primary: #3B82F6; /* Primary blue - CTAs, links */
--accent-secondary: #8B5CF6; /* Purple - Highlights */
--accent-success: #10B981; /* Green - Success states */
--accent-warning: #F59E0B; /* Amber - Warnings */
--accent-error: #EF4444; /* Red - Errors */
--accent-info: #06B6D4; /* Cyan - Info */
/* Brand/Accent Colors / Màu thương hiệu/Accent */
--accent-primary: #FFFFFF;
/* White - Primary actions (High contrast) */
--accent-secondary: #333333;
/* Dark grey - Secondary actions */
--accent-success: #10B981;
/* Green - Success states */
--accent-warning: #F59E0B;
/* Amber - Warnings */
--accent-error: #EF4444;
/* Red - Errors */
--accent-info: #06B6D4;
/* Cyan - Info */
/* Chat Specific Colors / Màu riêng cho Chat */
--chat-user-bubble: #2563EB; /* Deep blue - User message */
--chat-ai-bubble: #374151; /* Dark grey - AI message */
--chat-user-text: #FFFFFF; /* White text on blue */
--chat-ai-text: #F3F4F6; /* Light text on grey */
--chat-timestamp: #9CA3AF; /* Timestamp grey */
--chat-divider: #1F2937; /* Divider between messages */
/* Chat Specific Colors / Màu riêng cho Chat */
--chat-user-bubble: #1A1A1A;
/* Dark grey - User message */
--chat-ai-bubble: transparent;
/* Transparent - AI message (Minimal) */
--chat-user-text: #FFFFFF;
/* White text */
--chat-ai-text: #E5E5E5;
/* Off-white text */
--chat-timestamp: #555555;
/* Dark grey timestamp */
--chat-divider: #222222;
/* Divider between messages */
/* Border Colors / Màu viền */
--border-primary: #2A2A2A; /* Default borders */
--border-secondary: #3A3A3A; /* Hover borders */
--border-focus: #3B82F6; /* Focus state - Blue */
/* Border Colors / Màu viền */
--border-primary: #222222;
/* Subtle borders */
--border-secondary: #333333;
/* Hover borders */
--border-focus: #FFFFFF;
/* Focus state - White */
/* ============================================
/* ============================================
EN: Light Mode Colors (Secondary Theme)
VI: Màu sắc cho Light Mode (Theme phụ)
============================================ */
--bg-primary-light: #FFFFFF;
--bg-secondary-light: #F9FAFB;
--bg-tertiary-light: #F3F4F6;
--text-primary-light: #111827;
--text-secondary-light: #4B5563;
--border-primary-light: #E5E7EB;
--bg-primary-light: #F9F9F9;
--bg-secondary-light: #FFFFFF;
--bg-tertiary-light: #F0F0F0;
--text-primary-light: #000000;
--text-secondary-light: #666666;
--border-primary-light: #E5E5E5;
/* ============================================
/* ============================================
EN: Typography
VI: Kiểu chữ
============================================ */
/* Font Stack / Bộ font */
--font-sans: -apple-system, BlinkMacSystemFont, "SF Pro Display", "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
--font-mono: "SF Mono", Consolas, "Liberation Mono", Menlo, monospace;
/* Font Stack / Bộ font */
--font-sans: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
--font-mono: "JetBrains Mono", "SF Mono", Consolas, "Liberation Mono", Menlo, monospace;
/* Type Scale / Kích thước chữ */
--text-6xl: 3.75rem; /* 60px - Hero titles */
--text-5xl: 3rem; /* 48px - Page titles */
--text-4xl: 2.25rem; /* 36px - Section headers */
--text-3xl: 1.875rem; /* 30px - Card headers */
--text-2xl: 1.5rem; /* 24px - Large body */
--text-xl: 1.25rem; /* 20px - Emphasized text */
--text-lg: 1.125rem; /* 18px - Large body */
--text-base: 1rem; /* 16px - Default body */
--text-sm: 0.875rem; /* 14px - Small text */
--text-xs: 0.75rem; /* 12px - Captions */
/* Type Scale / Kích thước chữ */
--text-6xl: 3.75rem;
/* 60px - Hero titles */
--text-5xl: 3rem;
/* 48px - Page titles */
--text-4xl: 2.25rem;
/* 36px - Section headers */
--text-3xl: 1.875rem;
/* 30px - Card headers */
--text-2xl: 1.5rem;
/* 24px - Large body */
--text-xl: 1.25rem;
/* 20px - Emphasized text */
--text-lg: 1.125rem;
/* 18px - Large body */
--text-base: 1rem;
/* 16px - Default body */
--text-sm: 0.875rem;
/* 14px - Small text */
--text-xs: 0.75rem;
/* 12px - Captions */
/* Line Heights / Chiều cao dòng */
--leading-none: 1;
--leading-tight: 1.1;
--leading-snug: 1.2;
--leading-normal: 1.3;
--leading-relaxed: 1.4;
--leading-loose: 1.5;
/* Line Heights / Chiều cao dòng */
--leading-none: 1;
--leading-tight: 1.1;
--leading-snug: 1.2;
--leading-normal: 1.5;
--leading-relaxed: 1.625;
--leading-loose: 2;
/* Font Weights / Độ đậm chữ */
--font-light: 300; /* Light text */
--font-normal: 400; /* Body text */
--font-medium: 500; /* Emphasized */
--font-semibold: 600; /* Headings */
--font-bold: 700; /* Strong emphasis */
/* Font Weights / Độ đậm chữ */
--font-light: 300;
/* Light text */
--font-normal: 400;
/* Body text */
--font-medium: 500;
/* Emphasized */
--font-semibold: 600;
/* Headings */
--font-bold: 700;
/* Strong emphasis */
/* ============================================
/* ============================================
EN: Spacing & Layout
VI: Khoảng cách & Bố cục
============================================ */
/* Base Unit: 4px (0.25rem) / Đơn vị cơ sở: 4px (0.25rem) */
--space-0: 0;
--space-1: 0.25rem; /* 4px */
--space-2: 0.5rem; /* 8px */
--space-3: 0.75rem; /* 12px */
--space-4: 1rem; /* 16px */
--space-5: 1.25rem; /* 20px */
--space-6: 1.5rem; /* 24px */
--space-8: 2rem; /* 32px */
--space-10: 2.5rem; /* 40px */
--space-12: 3rem; /* 48px */
--space-16: 4rem; /* 64px */
--space-20: 5rem; /* 80px */
/* Base Unit: 4px (0.25rem) / Đơn vị cơ sở: 4px (0.25rem) */
--space-0: 0;
--space-1: 0.25rem;
/* 4px */
--space-2: 0.5rem;
/* 8px */
--space-3: 0.75rem;
/* 12px */
--space-4: 1rem;
/* 16px */
--space-5: 1.25rem;
/* 20px */
--space-6: 1.5rem;
/* 24px */
--space-8: 2rem;
/* 32px */
--space-10: 2.5rem;
/* 40px */
--space-12: 3rem;
/* 48px */
--space-16: 4rem;
/* 64px */
--space-20: 5rem;
/* 80px */
/* Container Widths / Chiều rộng container */
--container-sm: 640px; /* Small devices */
--container-md: 768px; /* Medium devices */
--container-lg: 1024px; /* Large devices */
--container-xl: 1280px; /* Extra large */
--container-2xl: 1536px; /* 2X large */
--chat-max-width: 768px; /* Max width for chat messages */
--sidebar-width: 280px; /* Conversation history sidebar */
/* Container Widths / Chiều rộng container */
--container-sm: 640px;
/* Small devices */
--container-md: 768px;
/* Medium devices */
--container-lg: 1024px;
/* Large devices */
--container-xl: 1280px;
/* Extra large */
--container-2xl: 1536px;
/* 2X large */
--chat-max-width: 800px;
/* Max width for chat messages */
--sidebar-width: 280px;
/* Conversation history sidebar */
/* Border Radius / Bo góc */
--radius-sm: 0.25rem; /* 4px - Small elements */
--radius-md: 0.5rem; /* 8px - Buttons, inputs */
--radius-lg: 0.75rem; /* 12px - Cards */
--radius-xl: 1rem; /* 16px - Large cards */
--radius-2xl: 1.5rem; /* 24px - Modals */
--radius-full: 9999px; /* Full round - Avatars, pills */
/* Border Radius / Bo góc */
--radius-sm: 2px;
/* Small elements - sharp */
--radius-md: 4px;
/* Buttons, inputs - sharp */
--radius-lg: 8px;
/* Cards - minimal roundness */
--radius-xl: 12px;
/* Large cards */
--radius-2xl: 16px;
/* Modals */
--radius-full: 9999px;
/* Full round - Avatars, pills */
/* ============================================
/* ============================================
EN: Shadows (Dark Mode Optimized)
VI: Đổ bóng (Tối ưu cho Dark Mode)
============================================ */
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.5);
--shadow-md: 0 4px 6px rgba(0, 0, 0, 0.5);
--shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.6);
--shadow-xl: 0 20px 25px rgba(0, 0, 0, 0.7);
--shadow-glow: 0 0 20px rgba(59, 130, 246, 0.3); /* Blue glow for focus */
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.8);
--shadow-md: 0 4px 6px rgba(0, 0, 0, 0.8);
--shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.9);
--shadow-xl: 0 20px 25px rgba(0, 0, 0, 0.95);
--shadow-glow: 0 0 20px rgba(255, 255, 255, 0.1);
/* White glow for focus */
/* ============================================
/* ============================================
EN: Grid System & Breakpoints
VI: Hệ thống lưới & Điểm ngắt
============================================ */
--screen-sm: 640px; /* Mobile landscape */
--screen-md: 768px; /* Tablet */
--screen-lg: 1024px; /* Desktop */
--screen-xl: 1280px; /* Large desktop */
--screen-2xl: 1536px; /* Extra large desktop */
--screen-sm: 640px;
/* Mobile landscape */
--screen-md: 768px;
/* Tablet */
--screen-lg: 1024px;
/* Desktop */
--screen-xl: 1280px;
/* Large desktop */
--screen-2xl: 1536px;
/* Extra large desktop */
/* ============================================
/* ============================================
EN: Animation & Transitions
VI: Animation & Chuyển tiếp
============================================ */
/* Timing Functions / Hàm thời gian */
--ease-in: cubic-bezier(0.4, 0, 1, 1);
--ease-out: cubic-bezier(0, 0, 0.2, 1);
--ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
--ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
/* Timing Functions / Hàm thời gian */
--ease-in: cubic-bezier(0.4, 0, 1, 1);
--ease-out: cubic-bezier(0, 0, 0.2, 1);
--ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
--ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
/* Duration / Thời lượng */
--duration-fast: 150ms; /* Hover effects */
--duration-normal: 250ms; /* Default transitions */
--duration-slow: 350ms; /* Complex animations */
--duration-slower: 500ms; /* Page transitions */
/* Duration / Thời lượng */
--duration-fast: 150ms;
/* Hover effects */
--duration-normal: 250ms;
/* Default transitions */
--duration-slow: 350ms;
/* Complex animations */
--duration-slower: 500ms;
/* Page transitions */
}
/* ============================================
@@ -178,14 +250,14 @@
VI: Ghi đè theme cho Light Mode
============================================ */
@media (prefers-color-scheme: light) {
:root {
--bg-primary: var(--bg-primary-light);
--bg-secondary: var(--bg-secondary-light);
--bg-tertiary: var(--bg-tertiary-light);
--text-primary: var(--text-primary-light);
--text-secondary: var(--text-secondary-light);
--border-primary: var(--border-primary-light);
}
:root {
--bg-primary: var(--bg-primary-light);
--bg-secondary: var(--bg-secondary-light);
--bg-tertiary: var(--bg-tertiary-light);
--text-primary: var(--text-primary-light);
--text-secondary: var(--text-secondary-light);
--border-primary: var(--border-primary-light);
}
}
/* ============================================
@@ -194,15 +266,15 @@
============================================ */
[data-theme="dark"],
.dark {
--bg-primary: #0A0A0A;
--bg-secondary: #121212;
--bg-tertiary: #1A1A1A;
--bg-elevated: #242424;
--text-primary: #FAFAFA;
--text-secondary: #E0E0E0;
--text-tertiary: #A0A0A0;
--border-primary: #2A2A2A;
--border-secondary: #3A3A3A;
--bg-primary: #000000;
--bg-secondary: #0A0A0A;
--bg-tertiary: #141414;
--bg-elevated: #1A1A1A;
--text-primary: #FFFFFF;
--text-secondary: #888888;
--text-tertiary: #444444;
--border-primary: #222222;
--border-secondary: #333333;
}
/* ============================================
@@ -211,10 +283,10 @@
============================================ */
[data-theme="light"],
.light {
--bg-primary: var(--bg-primary-light);
--bg-secondary: var(--bg-secondary-light);
--bg-tertiary: var(--bg-tertiary-light);
--text-primary: var(--text-primary-light);
--text-secondary: var(--text-secondary-light);
--border-primary: var(--border-primary-light);
}
--bg-primary: var(--bg-primary-light);
--bg-secondary: var(--bg-secondary-light);
--bg-tertiary: var(--bg-tertiary-light);
--text-primary: var(--text-primary-light);
--text-secondary: var(--text-secondary-light);
--border-primary: var(--border-primary-light);
}

View File

@@ -1,6 +1,7 @@
import { Application } from 'express';
import swaggerJSDoc from 'swagger-jsdoc';
import swaggerUi from 'swagger-ui-express';
import { appConfig } from '../config/app.config';
/**
* EN: Swagger/OpenAPI configuration for API documentation
@@ -29,7 +30,7 @@ const options = {
description: 'Development server',
variables: {
port: {
default: '5000',
default: String(appConfig.port),
description: 'Port number for the development server',
},
},
@@ -104,6 +105,155 @@ const options = {
},
required: ['success', 'error', 'timestamp'],
},
RegisterRequest: {
type: 'object',
properties: {
email: { type: 'string', format: 'email', example: 'user@example.com' },
password: { type: 'string', minLength: 8, example: 'StrongP@ssw0rd!' },
username: { type: 'string', minLength: 3, example: 'johndoe' },
},
required: ['email', 'password'],
},
RegisterResponse: {
type: 'object',
properties: {
user: { $ref: '#/components/schemas/User' },
token: { type: 'string' },
},
},
LoginRequest: {
type: 'object',
properties: {
email: { type: 'string', format: 'email', example: 'user@example.com' },
password: { type: 'string', example: 'StrongP@ssw0rd!' },
},
required: ['email', 'password'],
},
LoginResponse: {
type: 'object',
properties: {
accessToken: { type: 'string' },
refreshToken: { type: 'string' },
user: { $ref: '#/components/schemas/User' },
},
},
User: {
type: 'object',
properties: {
id: { type: 'string', example: 'cuid12345' },
email: { type: 'string', format: 'email' },
username: { type: 'string' },
firstName: { type: 'string' },
lastName: { type: 'string' },
isActive: { type: 'boolean' },
isEmailVerified: { type: 'boolean' },
createdAt: { type: 'string', format: 'date-time' },
updatedAt: { type: 'string', format: 'date-time' },
},
},
CreateUserRequest: {
type: 'object',
properties: {
email: { type: 'string', format: 'email' },
username: { type: 'string' },
firstName: { type: 'string' },
lastName: { type: 'string' },
phone: { type: 'string' },
},
required: ['email'],
},
Organization: {
type: 'object',
properties: {
id: { type: 'string' },
name: { type: 'string' },
domain: { type: 'string' },
isActive: { type: 'boolean' },
parentId: { type: 'string' },
createdAt: { type: 'string', format: 'date-time' },
updatedAt: { type: 'string', format: 'date-time' },
},
},
CreateOrganizationRequest: {
type: 'object',
properties: {
name: { type: 'string' },
domain: { type: 'string' },
parentId: { type: 'string' },
settings: { type: 'object' },
},
required: ['name'],
},
UpdateOrganizationRequest: {
type: 'object',
properties: {
name: { type: 'string' },
domain: { type: 'string' },
parentId: { type: 'string' },
settings: { type: 'object' },
isActive: { type: 'boolean' },
},
},
Group: {
type: 'object',
properties: {
id: { type: 'string' },
name: { type: 'string' },
description: { type: 'string' },
organizationId: { type: 'string' },
isActive: { type: 'boolean' },
createdAt: { type: 'string', format: 'date-time' },
updatedAt: { type: 'string', format: 'date-time' },
},
},
CreateGroupRequest: {
type: 'object',
properties: {
name: { type: 'string' },
description: { type: 'string' },
organizationId: { type: 'string' },
},
required: ['name'],
},
UpdateGroupRequest: {
type: 'object',
properties: {
name: { type: 'string' },
description: { type: 'string' },
isActive: { type: 'boolean' },
},
},
UserProfile: {
type: 'object',
properties: {
id: { type: 'string' },
userId: { type: 'string' },
avatarUrl: { type: 'string' },
bio: { type: 'string' },
phoneNumber: { type: 'string' },
address: { type: 'string' },
preferences: { type: 'object' },
},
},
UpdateProfileRequest: {
type: 'object',
properties: {
firstName: { type: 'string' },
lastName: { type: 'string' },
phone: { type: 'string' },
avatarUrl: { type: 'string' },
preferences: { type: 'object' },
},
},
UpdateUserRequest: {
type: 'object',
properties: {
username: { type: 'string' },
email: { type: 'string', format: 'email' },
isActive: { type: 'boolean' },
organizationId: { type: 'string' },
},
},
Feature: {
type: 'object',
properties: {
@@ -304,7 +454,17 @@ const options = {
},
],
},
apis: ['./src/routes/*.ts', './src/modules/*/feature.module.ts'], // Paths to files containing OpenAPI definitions
apis: [
'./src/routes/*.ts',
'./src/modules/auth/*.routes.ts',
'./src/modules/session/*.routes.ts',
'./src/modules/oidc/*.routes.ts',
'./src/modules/rbac/*.routes.ts',
'./src/modules/mfa/*.routes.ts',
'./src/modules/identity/*.routes.ts',
'./src/modules/access/*.routes.ts',
'./src/modules/governance/*.routes.ts'
],
};
/**
@@ -357,7 +517,7 @@ export const setupSwagger = (app: Application, basePath: string = '/api-docs') =
res.send(JSON.stringify(specs, null, 2));
});
console.log(`📚 Swagger documentation available at: http://localhost:5000${basePath}`);
console.log(`📚 Swagger documentation available at: http://localhost:${appConfig.port}${basePath}`);
};
export { specs };

View File

@@ -0,0 +1,591 @@
import { Router } from 'express';
import { authenticate } from '../../middlewares/auth.middleware';
import { requirePermission } from '../../middlewares/rbac.middleware';
import { accessRequestController } from './request';
import { accessReviewController } from './review';
import { accessAnalyticsController } from './analytics';
/**
* EN: Create and configure Access Management routes
* VI: Tạo và cấu hình routes cho Access Management
*/
export const createAccessRouter = (): Router => {
const router = Router();
// =============================================================================
// EN: Access Request Endpoints
// VI: Endpoints Yêu Cầu Truy Cập
// =============================================================================
/**
* @swagger
* /api/{version}/access/requests:
* get:
* summary: List access requests
* description: List access requests visible to the current user
* tags: [Access - Requests]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: version
* required: true
* schema:
* type: string
* default: v1
* description: API version
* - in: query
* name: status
* schema:
* type: string
* enum: [PENDING, APPROVED, REJECTED, CANCELLED]
* - in: query
* name: page
* schema:
* type: integer
* - in: query
* name: limit
* schema:
* type: integer
* responses:
* 200:
* description: Requests list
* content:
* application/json:
* schema:
* allOf:
* - $ref: '#/components/schemas/ApiResponse'
* - type: object
* properties:
* data:
* type: array
* items:
* $ref: '#/components/schemas/AccessRequest'
*/
router.get('/requests', authenticate(), accessRequestController.list.bind(accessRequestController));
/**
* @swagger
* /api/{version}/access/requests:
* post:
* summary: Create access request
* description: Submit a new access request
* tags: [Access - Requests]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: version
* required: true
* schema:
* type: string
* default: v1
* description: API version
* requestBody:
* required: true
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/CreateAccessRequest'
* responses:
* 201:
* description: Request created
* content:
* application/json:
* schema:
* allOf:
* - $ref: '#/components/schemas/ApiResponse'
* - type: object
* properties:
* data:
* $ref: '#/components/schemas/AccessRequest'
*/
router.post('/requests', authenticate(), accessRequestController.create.bind(accessRequestController));
/**
* @swagger
* /api/{version}/access/requests/{id}:
* get:
* summary: Get access request
* description: Get access request details
* tags: [Access - Requests]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: version
* required: true
* schema:
* type: string
* default: v1
* description: API version
* - in: path
* name: id
* required: true
* schema:
* type: string
* description: Request ID
* responses:
* 200:
* description: Request details
* content:
* application/json:
* schema:
* allOf:
* - $ref: '#/components/schemas/ApiResponse'
* - type: object
* properties:
* data:
* $ref: '#/components/schemas/AccessRequest'
*/
router.get('/requests/:id', authenticate(), accessRequestController.get.bind(accessRequestController));
/**
* @swagger
* /api/{version}/access/requests/{id}/approve:
* put:
* summary: Approve access request
* description: Approve a pending access request
* tags: [Access - Requests]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: version
* required: true
* schema:
* type: string
* default: v1
* description: API version
* - in: path
* name: id
* required: true
* schema:
* type: string
* description: Request ID
* requestBody:
* content:
* application/json:
* schema:
* type: object
* properties:
* comment:
* type: string
* responses:
* 200:
* description: Request approved
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/ApiResponse'
*/
router.put('/requests/:id/approve', authenticate(), requirePermission('access', 'approve'), accessRequestController.approve.bind(accessRequestController));
/**
* @swagger
* /api/{version}/access/requests/{id}/reject:
* put:
* summary: Reject access request
* description: Reject a pending access request
* tags: [Access - Requests]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: version
* required: true
* schema:
* type: string
* default: v1
* description: API version
* - in: path
* name: id
* required: true
* schema:
* type: string
* description: Request ID
* requestBody:
* content:
* application/json:
* schema:
* type: object
* properties:
* comment:
* type: string
* responses:
* 200:
* description: Request rejected
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/ApiResponse'
*/
router.put('/requests/:id/reject', authenticate(), requirePermission('access', 'approve'), accessRequestController.reject.bind(accessRequestController));
/**
* @swagger
* /api/{version}/access/requests/{id}:
* delete:
* summary: Cancel access request
* description: Cancel a pending access request
* tags: [Access - Requests]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: version
* required: true
* schema:
* type: string
* default: v1
* description: API version
* - in: path
* name: id
* required: true
* schema:
* type: string
* description: Request ID
* responses:
* 200:
* description: Request cancelled
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/ApiResponse'
*/
router.delete('/requests/:id', authenticate(), accessRequestController.cancel.bind(accessRequestController));
// =============================================================================
// EN: Access Review Endpoints
// VI: Endpoints Đánh Giá Truy Cập
// =============================================================================
/**
* @swagger
* /api/{version}/access/reviews:
* get:
* summary: List access reviews
* description: List access reviews
* tags: [Access - Reviews]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: version
* required: true
* schema:
* type: string
* default: v1
* description: API version
* responses:
* 200:
* description: Reviews list
* content:
* application/json:
* schema:
* allOf:
* - $ref: '#/components/schemas/ApiResponse'
* - type: object
* properties:
* data:
* type: array
* items:
* $ref: '#/components/schemas/AccessReview'
*/
router.get('/reviews', authenticate(), requirePermission('access', 'read'), accessReviewController.list.bind(accessReviewController));
/**
* @swagger
* /api/{version}/access/reviews:
* post:
* summary: Create access review
* description: Create a new access review campaign
* tags: [Access - Reviews]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: version
* required: true
* schema:
* type: string
* default: v1
* description: API version
* requestBody:
* required: true
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/CreateAccessReviewRequest'
* responses:
* 201:
* description: Review created
* content:
* application/json:
* schema:
* allOf:
* - $ref: '#/components/schemas/ApiResponse'
* - type: object
* properties:
* data:
* $ref: '#/components/schemas/AccessReview'
*/
router.post('/reviews', authenticate(), requirePermission('access', 'create'), accessReviewController.create.bind(accessReviewController));
/**
* @swagger
* /api/{version}/access/reviews/{id}:
* get:
* summary: Get access review
* description: Get access review details
* tags: [Access - Reviews]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: version
* required: true
* schema:
* type: string
* default: v1
* description: API version
* - in: path
* name: id
* required: true
* schema:
* type: string
* description: Review ID
* responses:
* 200:
* description: Review details
* content:
* application/json:
* schema:
* allOf:
* - $ref: '#/components/schemas/ApiResponse'
* - type: object
* properties:
* data:
* $ref: '#/components/schemas/AccessReview'
*/
router.get('/reviews/:id', authenticate(), requirePermission('access', 'read'), accessReviewController.get.bind(accessReviewController));
/**
* @swagger
* /api/{version}/access/reviews/{id}/start:
* post:
* summary: Start access review
* description: Start an access review campaign
* tags: [Access - Reviews]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: version
* required: true
* schema:
* type: string
* default: v1
* description: API version
* - in: path
* name: id
* required: true
* schema:
* type: string
* description: Review ID
* responses:
* 200:
* description: Review started
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/ApiResponse'
*/
router.post('/reviews/:id/start', authenticate(), requirePermission('access', 'update'), accessReviewController.start.bind(accessReviewController));
/**
* @swagger
* /api/{version}/access/reviews/{id}/complete:
* post:
* summary: Complete access review
* description: Complete an access review campaign
* tags: [Access - Reviews]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: version
* required: true
* schema:
* type: string
* default: v1
* description: API version
* - in: path
* name: id
* required: true
* schema:
* type: string
* description: Review ID
* responses:
* 200:
* description: Review completed
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/ApiResponse'
*/
router.post('/reviews/:id/complete', authenticate(), requirePermission('access', 'update'), accessReviewController.complete.bind(accessReviewController));
/**
* @swagger
* /api/{version}/access/reviews/{id}/items:
* get:
* summary: Get review items
* description: Get items in an access review
* tags: [Access - Reviews]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: version
* required: true
* schema:
* type: string
* default: v1
* description: API version
* - in: path
* name: id
* required: true
* schema:
* type: string
* description: Review ID
* responses:
* 200:
* description: Review items
* content:
* application/json:
* schema:
* allOf:
* - $ref: '#/components/schemas/ApiResponse'
* - type: object
* properties:
* data:
* type: array
* items:
* $ref: '#/components/schemas/AccessReviewItem'
*/
router.get('/reviews/:id/items', authenticate(), requirePermission('access', 'read'), accessReviewController.getItems.bind(accessReviewController));
/**
* @swagger
* /api/{version}/access/reviews/{id}/items/{itemId}/review:
* put:
* summary: Review item
* description: Make a decision on a review item (Certify/Revoke)
* tags: [Access - Reviews]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: version
* required: true
* schema:
* type: string
* default: v1
* description: API version
* - in: path
* name: id
* required: true
* schema:
* type: string
* description: Review ID
* - in: path
* name: itemId
* required: true
* schema:
* type: string
* description: Item ID
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* required: [decision]
* properties:
* decision:
* type: string
* enum: [CERTIFY, REVOKE]
* comment:
* type: string
* responses:
* 200:
* description: Item reviewed
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/ApiResponse'
*/
router.put('/reviews/:id/items/:itemId/review', authenticate(), requirePermission('access', 'update'), accessReviewController.reviewItem.bind(accessReviewController));
// =============================================================================
// EN: Analytics Endpoints
// VI: Endpoints Analytics
// =============================================================================
/**
* @swagger
* /api/{version}/access/analytics/usage:
* get:
* summary: Access usage analytics
* description: Get access usage analytics
* tags: [Access - Analytics]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: version
* required: true
* schema:
* type: string
* default: v1
* description: API version
* responses:
* 200:
* description: Usage analytics
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/ApiResponse'
*/
router.get('/analytics/usage', authenticate(), requirePermission('access', 'read'), accessAnalyticsController.usage.bind(accessAnalyticsController));
/**
* @swagger
* /api/{version}/access/analytics/permissions:
* get:
* summary: Permissions analytics
* description: Get permissions analytics
* tags: [Access - Analytics]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: version
* required: true
* schema:
* type: string
* default: v1
* description: API version
* responses:
* 200:
* description: Permissions analytics
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/ApiResponse'
*/
router.get('/analytics/permissions', authenticate(), requirePermission('access', 'read'), accessAnalyticsController.permissions.bind(accessAnalyticsController));
return router;
};

View File

@@ -0,0 +1,313 @@
import { Router } from 'express';
import { authenticate } from '../../middlewares/auth.middleware';
import { authController } from './auth.controller';
import { changePasswordController } from './change-password.controller';
import { socialAuthController } from '../social/social.controller';
/**
* EN: Create and configure auth routes
* VI: Tạo và cấu hình routes cho auth
*/
export const createAuthRouter = (): Router => {
const router = Router();
// =============================================================================
// EN: Authentication Endpoints
// VI: Endpoints Xác Thực
// =============================================================================
/**
* @swagger
* /api/{version}/auth/register:
* post:
* summary: Register a new user
* description: Register a new user account with email and password
* tags: [Authentication]
* parameters:
* - in: path
* name: version
* required: true
* schema:
* type: string
* default: v1
* description: API version
* requestBody:
* required: true
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/RegisterRequest'
* responses:
* 201:
* description: User registered successfully
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/RegisterResponse'
* 400:
* description: Validation error or email already in use
* 500:
* description: Internal server error
*/
router.post('/register', authController.register.bind(authController));
/**
* @swagger
* /api/{version}/auth/login:
* post:
* summary: Login user
* description: Authenticate user with email and password to receive access tokens
* tags: [Authentication]
* parameters:
* - in: path
* name: version
* required: true
* schema:
* type: string
* default: v1
* description: API version
* requestBody:
* required: true
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/LoginRequest'
* responses:
* 200:
* description: Login successful
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/LoginResponse'
* 401:
* description: Invalid credentials
* 500:
* description: Internal server error
*/
router.post('/login', authController.login.bind(authController));
/**
* @swagger
* /api/{version}/auth/logout:
* post:
* summary: Logout user
* description: Invalidate current user session and tokens
* tags: [Authentication]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: version
* required: true
* schema:
* type: string
* default: v1
* description: API version
* responses:
* 200:
* description: Logout successful
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/ApiResponse'
* 401:
* description: Unauthorized
*/
router.post('/logout', authenticate(), authController.logout.bind(authController));
/**
* @swagger
* /api/{version}/auth/refresh:
* post:
* summary: Refresh access token
* description: Get a new access token using a valid refresh token
* tags: [Authentication]
* parameters:
* - in: path
* name: version
* required: true
* schema:
* type: string
* default: v1
* description: API version
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* required: [refreshToken]
* properties:
* refreshToken:
* type: string
* description: The refresh token
* responses:
* 200:
* description: Token refreshed successfully
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/LoginResponse'
* 401:
* description: Invalid or expired refresh token
*/
router.post('/refresh', authController.refreshToken.bind(authController));
/**
* @swagger
* /api/{version}/auth/change-password:
* post:
* summary: Change password
* description: Change the password for the currently authenticated user
* tags: [Authentication]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: version
* required: true
* schema:
* type: string
* default: v1
* description: API version
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* required: [currentPassword, newPassword]
* properties:
* currentPassword:
* type: string
* newPassword:
* type: string
* responses:
* 200:
* description: Password changed successfully
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/ApiResponse'
* 400:
* description: Validation error or wrong password
* 401:
* description: Unauthorized
*/
router.post('/change-password', authenticate(), changePasswordController.changePassword.bind(changePasswordController));
/**
* @swagger
* /api/{version}/auth/me:
* get:
* summary: Get current user
* description: Get information about the currently authenticated user
* tags: [Authentication]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: version
* required: true
* schema:
* type: string
* default: v1
* description: API version
* responses:
* 200:
* description: User information retrieved
* content:
* application/json:
* schema:
* allOf:
* - $ref: '#/components/schemas/ApiResponse'
* - type: object
* properties:
* data:
* type: object
* description: User payload from token
* 401:
* description: Unauthorized
*/
router.get('/me', authenticate(), (req, res) => {
res.json({
success: true,
data: req.user,
message: 'User information retrieved / Thông tin người dùng đã được lấy',
timestamp: new Date().toISOString(),
});
});
// =============================================================================
// EN: Social Authentication Endpoints
// VI: Endpoints Xác Thực Mạng Xã Hội
// =============================================================================
/**
* @swagger
* /api/{version}/auth/google:
* get:
* summary: Initiate Google OAuth
* description: Redirects to Google login page for OAuth2 authentication
* tags: [Authentication]
* parameters:
* - in: path
* name: version
* required: true
* schema:
* type: string
* default: v1
* description: API version
* responses:
* 302:
* description: Redirects to Google
*/
router.get('/google', socialAuthController.googleAuth.bind(socialAuthController));
router.get('/google/callback', socialAuthController.googleCallback.bind(socialAuthController));
/**
* @swagger
* /api/{version}/auth/facebook:
* get:
* summary: Initiate Facebook OAuth
* description: Redirects to Facebook login page for OAuth2 authentication
* tags: [Authentication]
* parameters:
* - in: path
* name: version
* required: true
* schema:
* type: string
* default: v1
* description: API version
* responses:
* 302:
* description: Redirects to Facebook
*/
router.get('/facebook', socialAuthController.facebookAuth.bind(socialAuthController));
router.get('/facebook/callback', socialAuthController.facebookCallback.bind(socialAuthController));
/**
* @swagger
* /api/{version}/auth/github:
* get:
* summary: Initiate GitHub OAuth
* description: Redirects to GitHub login page for OAuth2 authentication
* tags: [Authentication]
* parameters:
* - in: path
* name: version
* required: true
* schema:
* type: string
* default: v1
* description: API version
* responses:
* 302:
* description: Redirects to GitHub
*/
router.get('/github', socialAuthController.githubAuth.bind(socialAuthController));
router.get('/github/callback', socialAuthController.githubCallback.bind(socialAuthController));
return router;
};

View File

@@ -0,0 +1,619 @@
import { Router } from 'express';
import { authenticate } from '../../middlewares/auth.middleware';
import { requirePermission } from '../../middlewares/rbac.middleware';
import { complianceController } from './compliance';
import { policyGovernanceController } from './policy';
import { riskController } from './risk';
import { reportingController } from './reporting';
/**
* EN: Create and configure Governance routes
* VI: Tạo và cấu hình routes cho Governance
*/
export const createGovernanceRouter = (): Router => {
const router = Router();
// =============================================================================
// EN: Compliance Reports Endpoints
// VI: Endpoints Báo Cáo Tuân Thủ
// =============================================================================
/**
* @swagger
* /api/{version}/governance/compliance/reports:
* get:
* summary: List compliance reports
* description: List compliance reports
* tags: [Governance - Compliance]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: version
* required: true
* schema:
* type: string
* default: v1
* description: API version
* responses:
* 200:
* description: Reports list
* content:
* application/json:
* schema:
* allOf:
* - $ref: '#/components/schemas/ApiResponse'
* - type: object
* properties:
* data:
* type: array
* items:
* $ref: '#/components/schemas/ComplianceReport'
*/
router.get('/compliance/reports', authenticate(), requirePermission('governance', 'read'), complianceController.list.bind(complianceController));
/**
* @swagger
* /api/{version}/governance/compliance/reports/generate:
* post:
* summary: Generate compliance report
* description: Generate a new compliance report
* tags: [Governance - Compliance]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: version
* required: true
* schema:
* type: string
* default: v1
* description: API version
* responses:
* 201:
* description: Report generation started
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/ApiResponse'
*/
router.post('/compliance/reports/generate', authenticate(), requirePermission('governance', 'create'), complianceController.generate.bind(complianceController));
/**
* @swagger
* /api/{version}/governance/compliance/reports/{id}:
* get:
* summary: Get compliance report
* description: Get compliance report details
* tags: [Governance - Compliance]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: version
* required: true
* schema:
* type: string
* default: v1
* description: API version
* - in: path
* name: id
* required: true
* schema:
* type: string
* description: Report ID
* responses:
* 200:
* description: Report details
* content:
* application/json:
* schema:
* allOf:
* - $ref: '#/components/schemas/ApiResponse'
* - type: object
* properties:
* data:
* $ref: '#/components/schemas/ComplianceReport'
*/
router.get('/compliance/reports/:id', authenticate(), requirePermission('governance', 'read'), complianceController.get.bind(complianceController));
/**
* @swagger
* /api/{version}/governance/compliance/reports/{id}/export:
* get:
* summary: Export compliance report
* description: Export compliance report to file
* tags: [Governance - Compliance]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: version
* required: true
* schema:
* type: string
* default: v1
* description: API version
* - in: path
* name: id
* required: true
* schema:
* type: string
* description: Report ID
* - in: query
* name: format
* schema:
* type: string
* enum: [pdf, csv]
* default: pdf
* responses:
* 200:
* description: Export successful
* content:
* application/json:
* schema:
* type: object // File download
*/
router.get('/compliance/reports/:id/export', authenticate(), requirePermission('governance', 'read'), complianceController.export.bind(complianceController));
/**
* @swagger
* /api/{version}/governance/compliance/reports/{id}/publish:
* post:
* summary: Publish compliance report
* description: Publish a compliance report
* tags: [Governance - Compliance]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: version
* required: true
* schema:
* type: string
* default: v1
* description: API version
* - in: path
* name: id
* required: true
* schema:
* type: string
* description: Report ID
* responses:
* 200:
* description: Report published
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/ApiResponse'
*/
router.post('/compliance/reports/:id/publish', authenticate(), requirePermission('governance', 'update'), complianceController.publish.bind(complianceController));
// =============================================================================
// EN: Policy Governance Endpoints
// VI: Endpoints Quản Trị Policy
// =============================================================================
/**
* @swagger
* /api/{version}/governance/policies/templates:
* get:
* summary: List policy templates
* description: List available policy templates
* tags: [Governance - Policy]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: version
* required: true
* schema:
* type: string
* default: v1
* description: API version
* responses:
* 200:
* description: Templates list
* content:
* application/json:
* schema:
* allOf:
* - $ref: '#/components/schemas/ApiResponse'
* - type: object
* properties:
* data:
* type: array
* items:
* $ref: '#/components/schemas/PolicyTemplate'
*/
router.get('/policies/templates', authenticate(), requirePermission('governance', 'read'), policyGovernanceController.getTemplates.bind(policyGovernanceController));
/**
* @swagger
* /api/{version}/governance/policies/templates:
* post:
* summary: Create policy template
* description: Create a new policy template
* tags: [Governance - Policy]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: version
* required: true
* schema:
* type: string
* default: v1
* description: API version
* responses:
* 201:
* description: Template created
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/ApiResponse'
*/
router.post('/policies/templates', authenticate(), requirePermission('governance', 'create'), policyGovernanceController.createTemplate.bind(policyGovernanceController));
/**
* @swagger
* /api/{version}/governance/policies/{id}/versions:
* get:
* summary: Get policy versions
* description: Get version history of a policy
* tags: [Governance - Policy]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: version
* required: true
* schema:
* type: string
* default: v1
* description: API version
* - in: path
* name: id
* required: true
* schema:
* type: string
* description: Policy ID
* responses:
* 200:
* description: Policy versions
* content:
* application/json:
* schema:
* allOf:
* - $ref: '#/components/schemas/ApiResponse'
* - type: object
* properties:
* data:
* type: array
* items:
* $ref: '#/components/schemas/PolicyVersion'
*/
router.get('/policies/:id/versions', authenticate(), requirePermission('governance', 'read'), policyGovernanceController.getVersions.bind(policyGovernanceController));
/**
* @swagger
* /api/{version}/governance/policies/{id}/test:
* post:
* summary: Test policy
* description: Test a policy against a context
* tags: [Governance - Policy]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: version
* required: true
* schema:
* type: string
* default: v1
* description: API version
* - in: path
* name: id
* required: true
* schema:
* type: string
* description: Policy ID
* requestBody:
* content:
* application/json:
* schema:
* type: object
* properties:
* context:
* type: object
* responses:
* 200:
* description: Test result
* content:
* application/json:
* schema:
* allOf:
* - $ref: '#/components/schemas/ApiResponse'
* - type: object
* properties:
* data:
* type: object
* properties:
* allowed:
* type: boolean
*/
router.post('/policies/:id/test', authenticate(), requirePermission('governance', 'read'), policyGovernanceController.test.bind(policyGovernanceController));
// =============================================================================
// EN: Risk Management Endpoints
// VI: Endpoints Quản Lý Rủi Ro
// =============================================================================
/**
* @swagger
* /api/{version}/governance/risk/scores:
* get:
* summary: List risk scores
* description: List user risk scores
* tags: [Governance - Risk]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: version
* required: true
* schema:
* type: string
* default: v1
* description: API version
* responses:
* 200:
* description: Risk scores
* content:
* application/json:
* schema:
* allOf:
* - $ref: '#/components/schemas/ApiResponse'
* - type: object
* properties:
* data:
* type: array
* items:
* $ref: '#/components/schemas/RiskScore'
*/
router.get('/risk/scores', authenticate(), requirePermission('governance', 'read'), riskController.list.bind(riskController));
/**
* @swagger
* /api/{version}/governance/risk/scores/{userId}:
* get:
* summary: Get user risk score
* description: Get risk score for a specific user
* tags: [Governance - Risk]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: version
* required: true
* schema:
* type: string
* default: v1
* description: API version
* - in: path
* name: userId
* required: true
* schema:
* type: string
* description: User ID
* responses:
* 200:
* description: User risk score
* content:
* application/json:
* schema:
* allOf:
* - $ref: '#/components/schemas/ApiResponse'
* - type: object
* properties:
* data:
* $ref: '#/components/schemas/RiskScore'
*/
router.get('/risk/scores/:userId', authenticate(), requirePermission('governance', 'read'), riskController.getScore.bind(riskController));
/**
* @swagger
* /api/{version}/governance/risk/calculate:
* post:
* summary: Calculate risk score
* description: Trigger risk score calculation
* tags: [Governance - Risk]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: version
* required: true
* schema:
* type: string
* default: v1
* description: API version
* responses:
* 200:
* description: Calculation started
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/ApiResponse'
*/
router.post('/risk/calculate', authenticate(), requirePermission('governance', 'update'), riskController.calculate.bind(riskController));
/**
* @swagger
* /api/{version}/governance/risk/dashboard:
* get:
* summary: Risk dashboard
* description: Get risk dashboard data
* tags: [Governance - Risk]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: version
* required: true
* schema:
* type: string
* default: v1
* description: API version
* responses:
* 200:
* description: Dashboard data
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/ApiResponse'
*/
router.get('/risk/dashboard', authenticate(), requirePermission('governance', 'read'), riskController.dashboard.bind(riskController));
// =============================================================================
// EN: Reporting Endpoints
// VI: Endpoints Báo Cáo
// =============================================================================
/**
* @swagger
* /api/{version}/governance/reports/access-summary:
* get:
* summary: Access summary report
* description: Get access summary report
* tags: [Governance - Reports]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: version
* required: true
* schema:
* type: string
* default: v1
* description: API version
* responses:
* 200:
* description: Report data
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/ApiResponse'
*/
router.get('/reports/access-summary', authenticate(), requirePermission('governance', 'read'), reportingController.accessSummary.bind(reportingController));
/**
* @swagger
* /api/{version}/governance/reports/user-activity:
* get:
* summary: User activity report
* description: Get user activity report
* tags: [Governance - Reports]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: version
* required: true
* schema:
* type: string
* default: v1
* description: API version
* responses:
* 200:
* description: Report data
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/ApiResponse'
*/
router.get('/reports/user-activity', authenticate(), requirePermission('governance', 'read'), reportingController.userActivity.bind(reportingController));
/**
* @swagger
* /api/{version}/governance/reports/security-events:
* get:
* summary: Security events report
* description: Get security events report
* tags: [Governance - Reports]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: version
* required: true
* schema:
* type: string
* default: v1
* description: API version
* responses:
* 200:
* description: Report data
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/ApiResponse'
*/
router.get('/reports/security-events', authenticate(), requirePermission('governance', 'read'), reportingController.securityEvents.bind(reportingController));
/**
* @swagger
* /api/{version}/governance/reports/compliance-status:
* get:
* summary: Compliance status report
* description: Get compliance status report
* tags: [Governance - Reports]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: version
* required: true
* schema:
* type: string
* default: v1
* description: API version
* responses:
* 200:
* description: Report data
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/ApiResponse'
*/
router.get('/reports/compliance-status', authenticate(), requirePermission('governance', 'read'), reportingController.complianceStatus.bind(reportingController));
/**
* @swagger
* /api/{version}/governance/reports/risk-overview:
* get:
* summary: Risk overview report
* description: Get risk overview report
* tags: [Governance - Reports]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: version
* required: true
* schema:
* type: string
* default: v1
* description: API version
* responses:
* 200:
* description: Report data
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/ApiResponse'
*/
router.get('/reports/risk-overview', authenticate(), requirePermission('governance', 'read'), reportingController.riskOverview.bind(reportingController));
return router;
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,220 @@
import { Router } from 'express';
import { authenticate } from '../../middlewares/auth.middleware';
import { mfaController } from './mfa.controller';
/**
* EN: Create and configure MFA routes
* VI: Tạo và cấu hình routes cho MFA
*/
export const createMfaRouter = (): Router => {
const router = Router();
/**
* @swagger
* /api/{version}/mfa/totp/enable:
* post:
* summary: Enable TOTP
* description: Initiate process to enable TOTP (Time-based One-Time Password) for the user
* tags: [MFA]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: version
* required: true
* schema:
* type: string
* default: v1
* description: API version
* responses:
* 200:
* description: TOTP setup initiated (returns QR code URL or secret)
* content:
* application/json:
* schema:
* allOf:
* - $ref: '#/components/schemas/ApiResponse'
* - type: object
* properties:
* data:
* type: object
* properties:
* secret:
* type: string
* qrCode:
* type: string
* 401:
* description: Unauthorized
*/
router.post('/totp/enable', authenticate(), mfaController.enableTOTP.bind(mfaController));
/**
* @swagger
* /api/{version}/mfa/totp/verify:
* post:
* summary: Verify and Enable TOTP
* description: Verify the TOTP code and finalize enabling MFA
* tags: [MFA]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: version
* required: true
* schema:
* type: string
* default: v1
* description: API version
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* required: [code]
* properties:
* code:
* type: string
* description: The 6-digit TOTP code
* responses:
* 200:
* description: TOTP verified and enabled successfully
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/ApiResponse'
* 400:
* description: Invalid code
* 401:
* description: Unauthorized
*/
router.post('/totp/verify', authenticate(), mfaController.verifyAndEnableTOTP.bind(mfaController));
/**
* @swagger
* /api/{version}/mfa/totp/validate:
* post:
* summary: Validate TOTP
* description: Validate a TOTP code (for login or step-up auth)
* tags: [MFA]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: version
* required: true
* schema:
* type: string
* default: v1
* description: API version
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* required: [code]
* properties:
* code:
* type: string
* description: The 6-digit TOTP code
* responses:
* 200:
* description: Code valid
* content:
* application/json:
* schema:
* allOf:
* - $ref: '#/components/schemas/ApiResponse'
* - type: object
* properties:
* data:
* type: object
* properties:
* valid:
* type: boolean
* 401:
* description: Unauthorized
*/
router.post('/totp/validate', authenticate(), mfaController.verifyTOTP.bind(mfaController));
/**
* @swagger
* /api/{version}/mfa/disable:
* post:
* summary: Disable MFA
* description: Disable Multi-Factor Authentication for the user
* tags: [MFA]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: version
* required: true
* schema:
* type: string
* default: v1
* description: API version
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* required: [password]
* properties:
* password:
* type: string
* description: Current password for confirmation
* responses:
* 200:
* description: MFA disabled successfully
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/ApiResponse'
* 400:
* description: Invalid password
* 401:
* description: Unauthorized
*/
router.post('/disable', authenticate(), mfaController.disableMFA.bind(mfaController));
/**
* @swagger
* /api/{version}/mfa/devices:
* get:
* summary: Get MFA devices
* description: List registered MFA devices
* tags: [MFA]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: version
* required: true
* schema:
* type: string
* default: v1
* description: API version
* responses:
* 200:
* description: Devices listed successfully
* content:
* application/json:
* schema:
* allOf:
* - $ref: '#/components/schemas/ApiResponse'
* - type: object
* properties:
* data:
* type: array
* items:
* type: object
* 401:
* description: Unauthorized
*/
router.get('/devices', authenticate(), mfaController.getMFADevices.bind(mfaController));
return router;
};

View File

@@ -0,0 +1,197 @@
import { Router } from 'express';
import { authenticate } from '../../middlewares/auth.middleware';
import { oidcController } from './oidc.controller';
/**
* EN: Create and configure OIDC routes
* VI: Tạo và cấu hình routes cho OIDC
*/
export const createOidcRouter = (): Router => {
const router = Router();
/**
* @swagger
* /.well-known/openid-configuration:
* get:
* summary: OIDC Discovery
* description: Get OpenID Connect configuration and metadata
* tags: [OIDC]
* responses:
* 200:
* description: OIDC configuration
* content:
* application/json:
* schema:
* type: object
* properties:
* issuer:
* type: string
* authorization_endpoint:
* type: string
* token_endpoint:
* type: string
* userinfo_endpoint:
* type: string
* jwks_uri:
* type: string
*/
// Note: This route is usually mounted at root, not under /api/{version}
// If mounted under /api/{version}, adjust paths accordingly
router.get('/.well-known/openid-configuration', oidcController.discovery.bind(oidcController));
/**
* @swagger
* /api/{version}/oidc/authorize:
* get:
* summary: OIDC Authorization
* description: Authorization endpoint for OIDC flow
* tags: [OIDC]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: version
* required: true
* schema:
* type: string
* default: v1
* description: API version
* - in: query
* name: response_type
* required: true
* schema:
* type: string
* - in: query
* name: client_id
* required: true
* schema:
* type: string
* - in: query
* name: redirect_uri
* required: true
* schema:
* type: string
* - in: query
* name: scope
* schema:
* type: string
* - in: query
* name: state
* schema:
* type: string
* responses:
* 302:
* description: Redirect to callback
* 400:
* description: Invalid request
*/
router.get('/authorize', authenticate(), oidcController.authorize.bind(oidcController));
/**
* @swagger
* /api/{version}/oidc/token:
* post:
* summary: OIDC Token
* description: Token endpoint for OIDC flow
* tags: [OIDC]
* parameters:
* - in: path
* name: version
* required: true
* schema:
* type: string
* default: v1
* description: API version
* requestBody:
* required: true
* content:
* application/x-www-form-urlencoded:
* schema:
* type: object
* properties:
* grant_type:
* type: string
* code:
* type: string
* redirect_uri:
* type: string
* client_id:
* type: string
* client_secret:
* type: string
* responses:
* 200:
* description: ID Token and Access Token
* 400:
* description: Invalid request
*/
router.post('/token', oidcController.token.bind(oidcController));
/**
* @swagger
* /api/{version}/oidc/userinfo:
* get:
* summary: OIDC UserInfo
* description: Get claims about the authenticated end-user
* tags: [OIDC]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: version
* required: true
* schema:
* type: string
* default: v1
* description: API version
* responses:
* 200:
* description: User information
* content:
* application/json:
* schema:
* type: object
* properties:
* sub:
* type: string
* name:
* type: string
* email:
* type: string
* 401:
* description: Unauthorized
*/
router.get('/userinfo', authenticate(), oidcController.userinfo.bind(oidcController));
/**
* @swagger
* /api/{version}/oidc/jwks:
* get:
* summary: OIDC JWKS
* description: JSON Web Key Set for verifying signatures
* tags: [OIDC]
* parameters:
* - in: path
* name: version
* required: true
* schema:
* type: string
* default: v1
* description: API version
* responses:
* 200:
* description: JWKS
* content:
* application/json:
* schema:
* type: object
* properties:
* keys:
* type: array
* items:
* type: object
*/
router.get('/jwks', oidcController.jwks.bind(oidcController));
return router;
};

View File

@@ -0,0 +1,234 @@
import { Router } from 'express';
import { authenticate } from '../../middlewares/auth.middleware';
import { requirePermission } from '../../middlewares/rbac.middleware';
import { rbacController } from './rbac.controller';
/**
* EN: Create and configure RBAC routes
* VI: Tạo và cấu hình routes cho RBAC
*/
export const createRbacRouter = (): Router => {
const router = Router();
/**
* @swagger
* /api/{version}/rbac/permissions:
* get:
* summary: Get user permissions
* description: Get list of permissions assigned to the current user
* tags: [RBAC]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: version
* required: true
* schema:
* type: string
* default: v1
* description: API version
* responses:
* 200:
* description: Permissions retrieved successfully
* content:
* application/json:
* schema:
* allOf:
* - $ref: '#/components/schemas/ApiResponse'
* - type: object
* properties:
* data:
* type: array
* items:
* type: string
* 401:
* description: Unauthorized
*/
router.get('/permissions', authenticate(), rbacController.getUserPermissions.bind(rbacController));
/**
* @swagger
* /api/{version}/rbac/roles/assign:
* post:
* summary: Assign role to user
* description: Assign a specific role to a user. Requires 'rbac:assign' permission.
* tags: [RBAC]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: version
* required: true
* schema:
* type: string
* default: v1
* description: API version
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* required: [userId, roleId]
* properties:
* userId:
* type: string
* roleId:
* type: string
* responses:
* 200:
* description: Role assigned successfully
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/ApiResponse'
* 401:
* description: Unauthorized
* 403:
* description: Forbidden - Insufficient permissions
* 404:
* description: User or Role not found
*/
router.post('/roles/assign', authenticate(), requirePermission('rbac', 'assign'), rbacController.assignRole.bind(rbacController));
/**
* @swagger
* /api/{version}/rbac/roles/revoke:
* post:
* summary: Revoke role from user
* description: Revoke a specific role from a user. Requires 'rbac:revoke' permission.
* tags: [RBAC]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: version
* required: true
* schema:
* type: string
* default: v1
* description: API version
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* required: [userId, roleId]
* properties:
* userId:
* type: string
* roleId:
* type: string
* responses:
* 200:
* description: Role revoked successfully
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/ApiResponse'
* 401:
* description: Unauthorized
* 403:
* description: Forbidden - Insufficient permissions
* 404:
* description: User or Role not found
*/
router.post('/roles/revoke', authenticate(), requirePermission('rbac', 'revoke'), rbacController.revokeRole.bind(rbacController));
/**
* @swagger
* /api/{version}/rbac/permissions/grant:
* post:
* summary: Grant permission to role
* description: Grant a specific permission to a role. Requires 'rbac:grant' permission.
* tags: [RBAC]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: version
* required: true
* schema:
* type: string
* default: v1
* description: API version
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* required: [roleId, permissionId]
* properties:
* roleId:
* type: string
* permissionId:
* type: string
* responses:
* 200:
* description: Permission granted successfully
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/ApiResponse'
* 401:
* description: Unauthorized
* 403:
* description: Forbidden - Insufficient permissions
* 404:
* description: Role or Permission not found
*/
router.post('/permissions/grant', authenticate(), requirePermission('rbac', 'grant'), rbacController.grantPermission.bind(rbacController));
/**
* @swagger
* /api/{version}/rbac/permissions/check:
* get:
* summary: Check permission
* description: Check if the current user has a specific permission
* tags: [RBAC]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: version
* required: true
* schema:
* type: string
* default: v1
* description: API version
* - in: query
* name: resource
* required: true
* schema:
* type: string
* - in: query
* name: action
* required: true
* schema:
* type: string
* responses:
* 200:
* description: Permission check result
* content:
* application/json:
* schema:
* allOf:
* - $ref: '#/components/schemas/ApiResponse'
* - type: object
* properties:
* data:
* type: object
* properties:
* allowed:
* type: boolean
* 401:
* description: Unauthorized
* 403:
* description: Forbidden
*/
router.get('/permissions/check', authenticate(), rbacController.checkPermission.bind(rbacController));
return router;
};

View File

@@ -0,0 +1,115 @@
import { Router } from 'express';
import { authenticate } from '../../middlewares/auth.middleware';
import { sessionsController } from './sessions.controller';
/**
* EN: Create and configure session routes
* VI: Tạo và cấu hình routes cho session
*/
export const createSessionRouter = (): Router => {
const router = Router();
/**
* @swagger
* /api/{version}/sessions:
* get:
* summary: Get active sessions
* description: List all active sessions for the current user
* tags: [Sessions]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: version
* required: true
* schema:
* type: string
* default: v1
* description: API version
* responses:
* 200:
* description: Sessions retrieved successfully
* content:
* application/json:
* schema:
* allOf:
* - $ref: '#/components/schemas/ApiResponse'
* - type: object
* properties:
* data:
* type: array
* items:
* $ref: '#/components/schemas/Session'
* 401:
* description: Unauthorized
*/
router.get('/', authenticate(), sessionsController.getUserSessions.bind(sessionsController));
/**
* @swagger
* /api/{version}/sessions:
* delete:
* summary: Revoke all sessions
* description: Revoke all active sessions for the current user except the current one
* tags: [Sessions]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: version
* required: true
* schema:
* type: string
* default: v1
* description: API version
* responses:
* 200:
* description: All sessions revoked successfully
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/ApiResponse'
* 401:
* description: Unauthorized
*/
router.delete('/', authenticate(), sessionsController.revokeAllSessions.bind(sessionsController));
/**
* @swagger
* /api/{version}/sessions/{sessionId}:
* delete:
* summary: Revoke session
* description: Revoke a specific session
* tags: [Sessions]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: version
* required: true
* schema:
* type: string
* default: v1
* description: API version
* - in: path
* name: sessionId
* required: true
* schema:
* type: string
* description: Session ID to revoke
* responses:
* 200:
* description: Session revoked successfully
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/ApiResponse'
* 401:
* description: Unauthorized
* 404:
* description: Session not found
*/
router.delete('/:sessionId', authenticate(), sessionsController.revokeSession.bind(sessionsController));
return router;
};

View File

@@ -1,304 +1,57 @@
import { ApiResponse } from '@goodgo/types';
import { Router } from 'express';
import { createAuthRouter } from '../modules/auth/auth.routes';
import { createIdentityRouter } from '../modules/identity/identity.routes';
import { createAccessRouter } from '../modules/access/access.routes';
import { createGovernanceRouter } from '../modules/governance/governance.routes';
import { createSessionRouter } from '../modules/session/session.routes';
import { createOidcRouter } from '../modules/oidc/oidc.routes';
import { createRbacRouter } from '../modules/rbac/rbac.routes';
import { createMfaRouter } from '../modules/mfa/mfa.routes';
import { authenticate } from '../middlewares/auth.middleware';
import { dynamicRateLimit } from '../middlewares/rate-limit.middleware';
import { requirePermission } from '../middlewares/rbac.middleware';
import { zeroTrustMiddleware } from '../middlewares/zero-trust.middleware';
import { accessAnalyticsController } from '../modules/access/analytics';
import { accessRequestController } from '../modules/access/request';
import { accessReviewController } from '../modules/access/review';
import { authController } from '../modules/auth/auth.controller';
import { changePasswordController } from '../modules/auth/change-password.controller';
import { complianceController } from '../modules/governance/compliance';
import { policyGovernanceController } from '../modules/governance/policy';
import { reportingController } from '../modules/governance/reporting';
import { riskController } from '../modules/governance/risk';
import { HealthController } from '../modules/health/health.controller';
import { groupController } from '../modules/identity/group';
import { organizationController } from '../modules/identity/organization';
import { profileController } from '../modules/identity/profile';
import { userManagementController } from '../modules/identity/user';
import { verificationController } from '../modules/identity/verification';
import { MetricsController } from '../modules/metrics/metrics.controller';
import { mfaController } from '../modules/mfa/mfa.controller';
import { oidcController } from '../modules/oidc/oidc.controller';
import { rbacController } from '../modules/rbac/rbac.controller';
import { sessionsController } from '../modules/session/sessions.controller';
import { socialAuthController } from '../modules/social/social.controller';
/**
* EN: Create and configure main application router
* VI: Tạo và cấu hình router chính cho ứng dụng
*/
export const createRouter = (): Router => {
const router = Router();
const healthController = new HealthController();
const apiVersion = process.env.API_VERSION || 'v1';
// EN: Health check endpoints
// VI: Endpoints kiểm tra sức khỏe
/**
* @swagger
* /health:
* get:
* summary: Basic liveness probe
* description: Returns basic health status for liveness probes
* tags: [Health]
* responses:
* 200:
* description: Service is healthy
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/HealthResponse'
*/
router.get('/health', healthController.health);
/**
* @swagger
* /health/ready:
* get:
* summary: Readiness probe
* description: Checks if service is ready to handle requests (includes database connectivity)
* tags: [Health]
* responses:
* 200:
* description: Service is ready
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/ReadinessResponse'
* 503:
* description: Service is not ready
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/ErrorResponse'
*/
router.get('/health/ready', healthController.ready);
/**
* @swagger
* /health/live:
* get:
* summary: Liveness probe
* description: Basic liveness check for container orchestration
* tags: [Health]
* responses:
* 200:
* description: Service is alive
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/LivenessResponse'
*/
router.get('/health/live', healthController.live);
// EN: Apply zero-trust and rate limiting to all routes
// VI: Áp dụng zero-trust và rate limiting cho tất cả routes
router.use(zeroTrustMiddleware);
router.use(dynamicRateLimit);
// EN: Auth endpoints
// VI: Endpoints xác thực
router.post(`/api/${apiVersion}/auth/register`, authController.register.bind(authController));
router.post(`/api/${apiVersion}/auth/login`, authController.login.bind(authController));
router.post(`/api/${apiVersion}/auth/logout`, authenticate(), authController.logout.bind(authController));
router.post(`/api/${apiVersion}/auth/refresh`, authController.refreshToken.bind(authController));
// EN: Change password
// VI: Đổi mật khẩu
router.post(`/api/${apiVersion}/auth/change-password`, authenticate(), changePasswordController.changePassword.bind(changePasswordController));
// EN: Session management endpoints
// VI: Endpoints quản lý session
router.get(`/api/${apiVersion}/sessions`, authenticate(), sessionsController.getUserSessions.bind(sessionsController));
router.delete(`/api/${apiVersion}/sessions/:sessionId`, authenticate(), sessionsController.revokeSession.bind(sessionsController));
router.delete(`/api/${apiVersion}/sessions`, authenticate(), sessionsController.revokeAllSessions.bind(sessionsController));
// EN: Get current user
// VI: Lấy thông tin người dùng hiện tại
router.get(`/api/${apiVersion}/auth/me`, authenticate(), (req, res) => {
const response: ApiResponse = {
success: true,
data: req.user,
message: 'User information retrieved / Thông tin người dùng đã được lấy',
timestamp: new Date().toISOString(),
};
res.json(response);
// EN: Health Check
// VI: Kiểm tra trạng thái
router.get('/health', (req, res) => {
res.json({ status: 'ok', timestamp: new Date().toISOString() });
});
// EN: Social Auth endpoints
// VI: Endpoints xác thực mạng xã hội
router.get(`/api/${apiVersion}/auth/google`, socialAuthController.googleAuth.bind(socialAuthController));
router.get(`/api/${apiVersion}/auth/google/callback`, socialAuthController.googleCallback.bind(socialAuthController));
router.get(`/api/${apiVersion}/auth/facebook`, socialAuthController.facebookAuth.bind(socialAuthController));
router.get(`/api/${apiVersion}/auth/facebook/callback`, socialAuthController.facebookCallback.bind(socialAuthController));
router.get(`/api/${apiVersion}/auth/github`, socialAuthController.githubAuth.bind(socialAuthController));
router.get(`/api/${apiVersion}/auth/github/callback`, socialAuthController.githubCallback.bind(socialAuthController));
// EN: Auth Routes
// VI: Routes xác thực
router.use('/auth', createAuthRouter());
// EN: OIDC endpoints
// VI: Endpoints OIDC
router.get('/.well-known/openid-configuration', oidcController.discovery.bind(oidcController));
router.get(`/api/${apiVersion}/oidc/authorize`, authenticate(), oidcController.authorize.bind(oidcController));
router.post(`/api/${apiVersion}/oidc/token`, oidcController.token.bind(oidcController));
router.get(`/api/${apiVersion}/oidc/userinfo`, authenticate(), oidcController.userinfo.bind(oidcController));
router.get(`/api/${apiVersion}/oidc/jwks`, oidcController.jwks.bind(oidcController));
// EN: Session Routes
// VI: Routes phiên làm việc
router.use('/session', createSessionRouter());
// EN: RBAC endpoints
// VI: Endpoints RBAC
router.get(`/api/${apiVersion}/rbac/permissions`, authenticate(), rbacController.getUserPermissions.bind(rbacController));
router.post(`/api/${apiVersion}/rbac/roles/assign`, authenticate(), requirePermission('rbac', 'assign'), rbacController.assignRole.bind(rbacController));
router.post(`/api/${apiVersion}/rbac/roles/revoke`, authenticate(), requirePermission('rbac', 'revoke'), rbacController.revokeRole.bind(rbacController));
router.post(`/api/${apiVersion}/rbac/permissions/grant`, authenticate(), requirePermission('rbac', 'grant'), rbacController.grantPermission.bind(rbacController));
router.get(`/api/${apiVersion}/rbac/permissions/check`, authenticate(), rbacController.checkPermission.bind(rbacController));
// EN: OIDC Routes
// VI: Routes OpenID Connect
router.use('/oidc', createOidcRouter());
// EN: MFA endpoints
// VI: Endpoints MFA
router.post(`/api/${apiVersion}/mfa/totp/enable`, authenticate(), mfaController.enableTOTP.bind(mfaController));
router.post(`/api/${apiVersion}/mfa/totp/verify`, authenticate(), mfaController.verifyAndEnableTOTP.bind(mfaController));
router.post(`/api/${apiVersion}/mfa/totp/validate`, authenticate(), mfaController.verifyTOTP.bind(mfaController));
router.post(`/api/${apiVersion}/mfa/disable`, authenticate(), mfaController.disableMFA.bind(mfaController));
router.get(`/api/${apiVersion}/mfa/devices`, authenticate(), mfaController.getMFADevices.bind(mfaController));
// EN: RBAC Routes
// VI: Routes phân quyền
router.use('/rbac', createRbacRouter());
// =============================================================================
// EN: Identity Management Endpoints
// VI: Endpoints Quản Lý Danh Tính
// =============================================================================
// EN: MFA Routes
// VI: Routes xác thực đa yếu tố
router.use('/mfa', createMfaRouter());
// EN: User Management
// VI: Quản lý người dùng
router.get(`/api/${apiVersion}/identity/users`, authenticate(), requirePermission('identity', 'read'), userManagementController.list.bind(userManagementController));
router.get(`/api/${apiVersion}/identity/users/:id`, authenticate(), requirePermission('identity', 'read'), userManagementController.get.bind(userManagementController));
router.put(`/api/${apiVersion}/identity/users/:id`, authenticate(), requirePermission('identity', 'update'), userManagementController.update.bind(userManagementController));
router.delete(`/api/${apiVersion}/identity/users/:id`, authenticate(), requirePermission('identity', 'delete'), userManagementController.delete.bind(userManagementController));
router.post(`/api/${apiVersion}/identity/users/:id/deactivate`, authenticate(), requirePermission('identity', 'update'), userManagementController.deactivate.bind(userManagementController));
router.post(`/api/${apiVersion}/identity/users/:id/reactivate`, authenticate(), requirePermission('identity', 'update'), userManagementController.reactivate.bind(userManagementController));
router.post(`/api/${apiVersion}/identity/users/bulk-import`, authenticate(), requirePermission('identity', 'create'), userManagementController.bulkImport.bind(userManagementController));
router.get(`/api/${apiVersion}/identity/users/bulk-export`, authenticate(), requirePermission('identity', 'read'), userManagementController.bulkExport.bind(userManagementController));
// EN: Identity Routes
// VI: Routes định danh
router.use('/identity', createIdentityRouter());
// EN: Profile Management
// VI: Quản lý profile
router.get(`/api/${apiVersion}/identity/users/:id/profile`, authenticate(), profileController.get.bind(profileController));
router.put(`/api/${apiVersion}/identity/users/:id/profile`, authenticate(), profileController.update.bind(profileController));
router.post(`/api/${apiVersion}/identity/users/:id/profile/avatar`, authenticate(), profileController.uploadAvatar.bind(profileController));
router.delete(`/api/${apiVersion}/identity/users/:id/profile/avatar`, authenticate(), profileController.deleteAvatar.bind(profileController));
// EN: Access Routes
// VI: Routes truy cập
router.use('/access', createAccessRouter());
// EN: Identity Verification
// VI: Xác thực danh tính
router.post(`/api/${apiVersion}/identity/verification/email/request`, authenticate(), verificationController.requestEmail.bind(verificationController));
router.post(`/api/${apiVersion}/identity/verification/email/verify`, verificationController.verifyEmail.bind(verificationController));
router.post(`/api/${apiVersion}/identity/verification/phone/request`, authenticate(), verificationController.requestPhone.bind(verificationController));
router.post(`/api/${apiVersion}/identity/verification/phone/verify`, verificationController.verifyPhone.bind(verificationController));
router.get(`/api/${apiVersion}/identity/verification/:id/status`, authenticate(), verificationController.getStatus.bind(verificationController));
// EN: Organizations
// VI: Tổ chức
router.get(`/api/${apiVersion}/identity/organizations`, authenticate(), requirePermission('identity', 'read'), organizationController.list.bind(organizationController));
router.post(`/api/${apiVersion}/identity/organizations`, authenticate(), requirePermission('identity', 'create'), organizationController.create.bind(organizationController));
router.get(`/api/${apiVersion}/identity/organizations/:id`, authenticate(), requirePermission('identity', 'read'), organizationController.get.bind(organizationController));
router.put(`/api/${apiVersion}/identity/organizations/:id`, authenticate(), requirePermission('identity', 'update'), organizationController.update.bind(organizationController));
router.delete(`/api/${apiVersion}/identity/organizations/:id`, authenticate(), requirePermission('identity', 'delete'), organizationController.delete.bind(organizationController));
router.get(`/api/${apiVersion}/identity/organizations/:id/users`, authenticate(), requirePermission('identity', 'read'), organizationController.getUsers.bind(organizationController));
// EN: Groups
// VI: Nhóm
router.get(`/api/${apiVersion}/identity/organizations/:id/groups`, authenticate(), requirePermission('identity', 'read'), groupController.list.bind(groupController));
router.post(`/api/${apiVersion}/identity/organizations/:id/groups`, authenticate(), requirePermission('identity', 'create'), groupController.create.bind(groupController));
router.get(`/api/${apiVersion}/identity/groups/:id`, authenticate(), requirePermission('identity', 'read'), groupController.get.bind(groupController));
router.put(`/api/${apiVersion}/identity/groups/:id`, authenticate(), requirePermission('identity', 'update'), groupController.update.bind(groupController));
router.delete(`/api/${apiVersion}/identity/groups/:id`, authenticate(), requirePermission('identity', 'delete'), groupController.delete.bind(groupController));
router.get(`/api/${apiVersion}/identity/groups/:id/members`, authenticate(), requirePermission('identity', 'read'), groupController.getMembers.bind(groupController));
router.post(`/api/${apiVersion}/identity/groups/:id/members`, authenticate(), requirePermission('identity', 'update'), groupController.addMember.bind(groupController));
router.delete(`/api/${apiVersion}/identity/groups/:id/members/:userId`, authenticate(), requirePermission('identity', 'update'), groupController.removeMember.bind(groupController));
// =============================================================================
// EN: Access Management Endpoints
// VI: Endpoints Quản Lý Truy Cập
// =============================================================================
// EN: Access Requests
// VI: Yêu cầu truy cập
router.get(`/api/${apiVersion}/access/requests`, authenticate(), accessRequestController.list.bind(accessRequestController));
router.post(`/api/${apiVersion}/access/requests`, authenticate(), accessRequestController.create.bind(accessRequestController));
router.get(`/api/${apiVersion}/access/requests/:id`, authenticate(), accessRequestController.get.bind(accessRequestController));
router.put(`/api/${apiVersion}/access/requests/:id/approve`, authenticate(), requirePermission('access', 'approve'), accessRequestController.approve.bind(accessRequestController));
router.put(`/api/${apiVersion}/access/requests/:id/reject`, authenticate(), requirePermission('access', 'approve'), accessRequestController.reject.bind(accessRequestController));
router.delete(`/api/${apiVersion}/access/requests/:id`, authenticate(), accessRequestController.cancel.bind(accessRequestController));
// EN: Access Reviews
// VI: Đánh giá truy cập
router.get(`/api/${apiVersion}/access/reviews`, authenticate(), requirePermission('access', 'read'), accessReviewController.list.bind(accessReviewController));
router.post(`/api/${apiVersion}/access/reviews`, authenticate(), requirePermission('access', 'create'), accessReviewController.create.bind(accessReviewController));
router.get(`/api/${apiVersion}/access/reviews/:id`, authenticate(), requirePermission('access', 'read'), accessReviewController.get.bind(accessReviewController));
router.post(`/api/${apiVersion}/access/reviews/:id/start`, authenticate(), requirePermission('access', 'update'), accessReviewController.start.bind(accessReviewController));
router.post(`/api/${apiVersion}/access/reviews/:id/complete`, authenticate(), requirePermission('access', 'update'), accessReviewController.complete.bind(accessReviewController));
router.get(`/api/${apiVersion}/access/reviews/:id/items`, authenticate(), requirePermission('access', 'read'), accessReviewController.getItems.bind(accessReviewController));
router.put(`/api/${apiVersion}/access/reviews/:id/items/:itemId/review`, authenticate(), requirePermission('access', 'update'), accessReviewController.reviewItem.bind(accessReviewController));
// EN: Access Analytics
// VI: Phân tích truy cập
router.get(`/api/${apiVersion}/access/analytics/usage`, authenticate(), requirePermission('access', 'read'), accessAnalyticsController.usage.bind(accessAnalyticsController));
router.get(`/api/${apiVersion}/access/analytics/permissions`, authenticate(), requirePermission('access', 'read'), accessAnalyticsController.permissions.bind(accessAnalyticsController));
router.get(`/api/${apiVersion}/access/analytics/users/:id/summary`, authenticate(), requirePermission('access', 'read'), accessAnalyticsController.userSummary.bind(accessAnalyticsController));
router.get(`/api/${apiVersion}/access/analytics/risks`, authenticate(), requirePermission('access', 'read'), accessAnalyticsController.risks.bind(accessAnalyticsController));
// =============================================================================
// EN: Governance Endpoints
// VI: Endpoints Quản Trị
// =============================================================================
// EN: Compliance Reports
// VI: Báo cáo tuân thủ
router.get(`/api/${apiVersion}/governance/compliance/reports`, authenticate(), requirePermission('governance', 'read'), complianceController.list.bind(complianceController));
router.post(`/api/${apiVersion}/governance/compliance/reports/generate`, authenticate(), requirePermission('governance', 'create'), complianceController.generate.bind(complianceController));
router.get(`/api/${apiVersion}/governance/compliance/reports/:id`, authenticate(), requirePermission('governance', 'read'), complianceController.get.bind(complianceController));
router.get(`/api/${apiVersion}/governance/compliance/reports/:id/export`, authenticate(), requirePermission('governance', 'read'), complianceController.export.bind(complianceController));
router.post(`/api/${apiVersion}/governance/compliance/reports/:id/publish`, authenticate(), requirePermission('governance', 'update'), complianceController.publish.bind(complianceController));
// EN: Policy Governance
// VI: Quản trị policy
router.get(`/api/${apiVersion}/governance/policies/templates`, authenticate(), requirePermission('governance', 'read'), policyGovernanceController.getTemplates.bind(policyGovernanceController));
router.post(`/api/${apiVersion}/governance/policies/templates`, authenticate(), requirePermission('governance', 'create'), policyGovernanceController.createTemplate.bind(policyGovernanceController));
router.get(`/api/${apiVersion}/governance/policies/:id/versions`, authenticate(), requirePermission('governance', 'read'), policyGovernanceController.getVersions.bind(policyGovernanceController));
router.post(`/api/${apiVersion}/governance/policies/:id/test`, authenticate(), requirePermission('governance', 'read'), policyGovernanceController.test.bind(policyGovernanceController));
// EN: Risk Management
// VI: Quản lý rủi ro
router.get(`/api/${apiVersion}/governance/risk/scores`, authenticate(), requirePermission('governance', 'read'), riskController.list.bind(riskController));
router.get(`/api/${apiVersion}/governance/risk/scores/:userId`, authenticate(), requirePermission('governance', 'read'), riskController.getScore.bind(riskController));
router.post(`/api/${apiVersion}/governance/risk/calculate`, authenticate(), requirePermission('governance', 'update'), riskController.calculate.bind(riskController));
router.get(`/api/${apiVersion}/governance/risk/dashboard`, authenticate(), requirePermission('governance', 'read'), riskController.dashboard.bind(riskController));
// EN: Reporting
// VI: Báo cáo
router.get(`/api/${apiVersion}/governance/reports/access-summary`, authenticate(), requirePermission('governance', 'read'), reportingController.accessSummary.bind(reportingController));
router.get(`/api/${apiVersion}/governance/reports/user-activity`, authenticate(), requirePermission('governance', 'read'), reportingController.userActivity.bind(reportingController));
router.get(`/api/${apiVersion}/governance/reports/security-events`, authenticate(), requirePermission('governance', 'read'), reportingController.securityEvents.bind(reportingController));
router.get(`/api/${apiVersion}/governance/reports/compliance-status`, authenticate(), requirePermission('governance', 'read'), reportingController.complianceStatus.bind(reportingController));
router.get(`/api/${apiVersion}/governance/reports/risk-overview`, authenticate(), requirePermission('governance', 'read'), reportingController.riskOverview.bind(reportingController));
// EN: Metrics endpoint
// VI: Endpoint metrics
/**
* @swagger
* /metrics:
* get:
* summary: Get Prometheus metrics
* description: Returns application metrics in Prometheus format for monitoring
* tags: [Monitoring]
* responses:
* 200:
* description: Metrics in Prometheus format
* content:
* text/plain:
* schema:
* type: string
* example: "# HELP http_requests_total Total number of HTTP requests\n# TYPE http_requests_total counter\nhttp_requests_total{method=\"GET\",route=\"/health\",status=\"200\"} 42"
*/
const metricsController = new MetricsController();
router.get('/metrics', metricsController.getMetrics);
// EN: Governance Routes
// VI: Routes quản trị
router.use('/governance', createGovernanceRouter());
return router;
};