+ {/* EN: Hero Title / VI: Tiêu đề Hero */}
+
+ {t('home.title')}
+
- {/* 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
-
-
{t('home.welcome', { email: user.email })}
-
{t('home.role', { role: user.role })}
+ {/* EN: Subtitle/Description / VI: Phụ đề/Mô tả */}
+
+ {t('home.description')}
+
+
+ {/* 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
+
+
+ {user.email?.[0].toUpperCase()}
+
+
{t('home.welcome', { email: user.email })}
+
+
+ {t('home.role', { role: user.role })}
+
+
+ ) : (
+ // EN: Login prompt for unauthenticated users / VI: Nhắc đăng nhập cho người dùng chưa xác thực
+
+
{t('home.pleaseLogin')}
+
+
+ )}
- ) : (
- // EN: Login prompt for unauthenticated users / VI: Nhắc đăng nhập cho người dùng chưa xác thực
-
-
{t('home.pleaseLogin')}
-
- )}
+
);
}
diff --git a/apps/web-client/src/i18n/messages/en.json b/apps/web-client/src/i18n/messages/en.json
index b1774120..99bd3b82 100644
--- a/apps/web-client/src/i18n/messages/en.json
+++ b/apps/web-client/src/i18n/messages/en.json
@@ -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"
}
}
-}
+}
\ No newline at end of file
diff --git a/apps/web-client/src/i18n/messages/vi.json b/apps/web-client/src/i18n/messages/vi.json
index 635eaf8e..2ef7a57d 100644
--- a/apps/web-client/src/i18n/messages/vi.json
+++ b/apps/web-client/src/i18n/messages/vi.json
@@ -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"
}
}
-}
+}
\ No newline at end of file
diff --git a/apps/web-client/src/styles/theme.css b/apps/web-client/src/styles/theme.css
index 6388cbf0..f576bae2 100644
--- a/apps/web-client/src/styles/theme.css
+++ b/apps/web-client/src/styles/theme.css
@@ -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);
+}
\ No newline at end of file
diff --git a/services/iam-service/src/docs/swagger.ts b/services/iam-service/src/docs/swagger.ts
index 50f0d052..aa14f49b 100644
--- a/services/iam-service/src/docs/swagger.ts
+++ b/services/iam-service/src/docs/swagger.ts
@@ -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 };
diff --git a/services/iam-service/src/modules/access/access.routes.ts b/services/iam-service/src/modules/access/access.routes.ts
new file mode 100644
index 00000000..1a077e88
--- /dev/null
+++ b/services/iam-service/src/modules/access/access.routes.ts
@@ -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;
+};
diff --git a/services/iam-service/src/modules/auth/auth.routes.ts b/services/iam-service/src/modules/auth/auth.routes.ts
new file mode 100644
index 00000000..22122884
--- /dev/null
+++ b/services/iam-service/src/modules/auth/auth.routes.ts
@@ -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;
+};
diff --git a/services/iam-service/src/modules/governance/governance.routes.ts b/services/iam-service/src/modules/governance/governance.routes.ts
new file mode 100644
index 00000000..59ca24a8
--- /dev/null
+++ b/services/iam-service/src/modules/governance/governance.routes.ts
@@ -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;
+};
diff --git a/services/iam-service/src/modules/identity/identity.routes.ts b/services/iam-service/src/modules/identity/identity.routes.ts
new file mode 100644
index 00000000..e30a3b27
--- /dev/null
+++ b/services/iam-service/src/modules/identity/identity.routes.ts
@@ -0,0 +1,1252 @@
+import { Router } from 'express';
+import { authenticate } from '../../middlewares/auth.middleware';
+import { requirePermission } from '../../middlewares/rbac.middleware';
+import { userManagementController } from './user';
+import { profileController } from './profile';
+import { verificationController } from './verification';
+import { organizationController } from './organization';
+import { groupController } from './group';
+
+/**
+ * EN: Create and configure Identity routes
+ * VI: Tạo và cấu hình routes cho Identity
+ */
+export const createIdentityRouter = (): Router => {
+ const router = Router();
+
+ // =============================================================================
+ // EN: User Management Endpoints
+ // VI: Endpoints Quản Lý Người Dùng
+ // =============================================================================
+
+ /**
+ * @swagger
+ * /api/{version}/identity/users:
+ * get:
+ * summary: List users
+ * description: Retrieve a list of users with pagination and filtering
+ * tags: [Identity - Users]
+ * security:
+ * - bearerAuth: []
+ * parameters:
+ * - in: path
+ * name: version
+ * required: true
+ * schema:
+ * type: string
+ * default: v1
+ * description: API version
+ * - in: query
+ * name: page
+ * schema:
+ * type: integer
+ * default: 1
+ * - in: query
+ * name: limit
+ * schema:
+ * type: integer
+ * default: 10
+ * - in: query
+ * name: search
+ * schema:
+ * type: string
+ * responses:
+ * 200:
+ * description: Users retrieved successfully
+ * content:
+ * application/json:
+ * schema:
+ * allOf:
+ * - $ref: '#/components/schemas/ApiResponse'
+ * - type: object
+ * properties:
+ * data:
+ * type: array
+ * items:
+ * $ref: '#/components/schemas/User'
+ * 401:
+ * description: Unauthorized
+ * 403:
+ * description: Forbidden
+ */
+ router.get('/users', authenticate(), requirePermission('identity', 'read'), userManagementController.list.bind(userManagementController));
+
+ /**
+ * @swagger
+ * /api/{version}/identity/users/{id}:
+ * get:
+ * summary: Get user details
+ * description: Retrieve detailed information about a specific user
+ * tags: [Identity - Users]
+ * 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: User ID
+ * responses:
+ * 200:
+ * description: User retrieved successfully
+ * content:
+ * application/json:
+ * schema:
+ * allOf:
+ * - $ref: '#/components/schemas/ApiResponse'
+ * - type: object
+ * properties:
+ * data:
+ * $ref: '#/components/schemas/User'
+ * 404:
+ * description: User not found
+ */
+ router.get('/users/:id', authenticate(), requirePermission('identity', 'read'), userManagementController.get.bind(userManagementController));
+
+ /**
+ * @swagger
+ * /api/{version}/identity/users/{id}:
+ * put:
+ * summary: Update user
+ * description: Update user information
+ * tags: [Identity - Users]
+ * 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: User ID
+ * requestBody:
+ * required: true
+ * content:
+ * application/json:
+ * schema:
+ * $ref: '#/components/schemas/UpdateUserRequest'
+ * responses:
+ * 200:
+ * description: User updated successfully
+ * content:
+ * application/json:
+ * schema:
+ * allOf:
+ * - $ref: '#/components/schemas/ApiResponse'
+ * - type: object
+ * properties:
+ * data:
+ * $ref: '#/components/schemas/User'
+ */
+ router.put('/users/:id', authenticate(), requirePermission('identity', 'update'), userManagementController.update.bind(userManagementController));
+
+ /**
+ * @swagger
+ * /api/{version}/identity/users/{id}:
+ * delete:
+ * summary: Delete user
+ * description: Delete a user account (soft delete or hard delete depending on policy)
+ * tags: [Identity - Users]
+ * 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: User ID
+ * responses:
+ * 200:
+ * description: User deleted successfully
+ * content:
+ * application/json:
+ * schema:
+ * $ref: '#/components/schemas/ApiResponse'
+ */
+ router.delete('/users/:id', authenticate(), requirePermission('identity', 'delete'), userManagementController.delete.bind(userManagementController));
+
+ /**
+ * @swagger
+ * /api/{version}/identity/users/{id}/deactivate:
+ * post:
+ * summary: Deactivate user
+ * description: Deactivate a user account without deleting it
+ * tags: [Identity - Users]
+ * 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: User ID
+ * responses:
+ * 200:
+ * description: User deactivated successfully
+ * content:
+ * application/json:
+ * schema:
+ * $ref: '#/components/schemas/ApiResponse'
+ */
+ router.post('/users/:id/deactivate', authenticate(), requirePermission('identity', 'update'), userManagementController.deactivate.bind(userManagementController));
+
+ /**
+ * @swagger
+ * /api/{version}/identity/users/{id}/reactivate:
+ * post:
+ * summary: Reactivate user
+ * description: Reactivate a previously deactivated user account
+ * tags: [Identity - Users]
+ * 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: User ID
+ * responses:
+ * 200:
+ * description: User reactivated successfully
+ * content:
+ * application/json:
+ * schema:
+ * $ref: '#/components/schemas/ApiResponse'
+ */
+ router.post('/users/:id/reactivate', authenticate(), requirePermission('identity', 'update'), userManagementController.reactivate.bind(userManagementController));
+
+ /**
+ * @swagger
+ * /api/{version}/identity/users/bulk-import:
+ * post:
+ * summary: Bulk import users
+ * description: Import multiple users from a file or JSON payload
+ * tags: [Identity - Users]
+ * 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: [users]
+ * properties:
+ * users:
+ * type: array
+ * items:
+ * $ref: '#/components/schemas/CreateUserRequest'
+ * responses:
+ * 200:
+ * description: Users imported successfully
+ * content:
+ * application/json:
+ * schema:
+ * allOf:
+ * - $ref: '#/components/schemas/ApiResponse'
+ * - type: object
+ * properties:
+ * data:
+ * type: object
+ * properties:
+ * imported:
+ * type: integer
+ * failed:
+ * type: integer
+ */
+ router.post('/users/bulk-import', authenticate(), requirePermission('identity', 'create'), userManagementController.bulkImport.bind(userManagementController));
+
+ /**
+ * @swagger
+ * /api/{version}/identity/users/bulk-export:
+ * get:
+ * summary: Bulk export users
+ * description: Export users to a file (CSV/JSON)
+ * tags: [Identity - Users]
+ * security:
+ * - bearerAuth: []
+ * parameters:
+ * - in: path
+ * name: version
+ * required: true
+ * schema:
+ * type: string
+ * default: v1
+ * description: API version
+ * - in: query
+ * name: format
+ * schema:
+ * type: string
+ * enum: [csv, json]
+ * default: json
+ * responses:
+ * 200:
+ * description: Export successful
+ * content:
+ * application/json:
+ * schema:
+ * type: object // File download
+ */
+ router.get('/users/bulk-export', authenticate(), requirePermission('identity', 'read'), userManagementController.bulkExport.bind(userManagementController));
+
+ // =============================================================================
+ // EN: Profile Management Endpoints
+ // VI: Endpoints Quản Lý Profile
+ // =============================================================================
+
+ /**
+ * @swagger
+ * /api/{version}/identity/users/{id}/profile:
+ * get:
+ * summary: Get user profile
+ * description: Get profile information for a specific user
+ * tags: [Identity - Profile]
+ * 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: User ID
+ * responses:
+ * 200:
+ * description: Profile retrieved successfully
+ * content:
+ * application/json:
+ * schema:
+ * allOf:
+ * - $ref: '#/components/schemas/ApiResponse'
+ * - type: object
+ * properties:
+ * data:
+ * $ref: '#/components/schemas/UserProfile'
+ */
+ router.get('/users/:id/profile', authenticate(), profileController.get.bind(profileController));
+
+ /**
+ * @swagger
+ * /api/{version}/identity/users/{id}/profile:
+ * put:
+ * summary: Update user profile
+ * description: Update profile information for a specific user
+ * tags: [Identity - Profile]
+ * 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: User ID
+ * requestBody:
+ * required: true
+ * content:
+ * application/json:
+ * schema:
+ * $ref: '#/components/schemas/UpdateProfileRequest'
+ * responses:
+ * 200:
+ * description: Profile updated successfully
+ * content:
+ * application/json:
+ * schema:
+ * allOf:
+ * - $ref: '#/components/schemas/ApiResponse'
+ * - type: object
+ * properties:
+ * data:
+ * $ref: '#/components/schemas/UserProfile'
+ */
+ router.put('/users/:id/profile', authenticate(), profileController.update.bind(profileController));
+
+ /**
+ * @swagger
+ * /api/{version}/identity/users/{id}/profile/avatar:
+ * post:
+ * summary: Upload avatar
+ * description: Upload a new avatar image for the user
+ * tags: [Identity - Profile]
+ * 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: User ID
+ * requestBody:
+ * content:
+ * multipart/form-data:
+ * schema:
+ * type: object
+ * properties:
+ * avatar:
+ * type: string
+ * format: binary
+ * responses:
+ * 200:
+ * description: Avatar uploaded successfully
+ * content:
+ * application/json:
+ * schema:
+ * $ref: '#/components/schemas/ApiResponse'
+ */
+ router.post('/users/:id/profile/avatar', authenticate(), profileController.uploadAvatar.bind(profileController));
+
+ /**
+ * @swagger
+ * /api/{version}/identity/users/{id}/profile/avatar:
+ * delete:
+ * summary: Delete avatar
+ * description: Remove the user's current avatar
+ * tags: [Identity - Profile]
+ * 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: User ID
+ * responses:
+ * 200:
+ * description: Avatar deleted successfully
+ * content:
+ * application/json:
+ * schema:
+ * $ref: '#/components/schemas/ApiResponse'
+ */
+ router.delete('/users/:id/profile/avatar', authenticate(), profileController.deleteAvatar.bind(profileController));
+
+ // =============================================================================
+ // EN: Verification Endpoints
+ // VI: Endpoints Xác Thực
+ // =============================================================================
+
+ /**
+ * @swagger
+ * /api/{version}/identity/verification/email/request:
+ * post:
+ * summary: Request email verification
+ * description: Send an email verification link/code to the user
+ * tags: [Identity - Verification]
+ * security:
+ * - bearerAuth: []
+ * parameters:
+ * - in: path
+ * name: version
+ * required: true
+ * schema:
+ * type: string
+ * default: v1
+ * description: API version
+ * responses:
+ * 200:
+ * description: Verification email sent
+ * content:
+ * application/json:
+ * schema:
+ * $ref: '#/components/schemas/ApiResponse'
+ */
+ router.post('/verification/email/request', authenticate(), verificationController.requestEmail.bind(verificationController));
+
+ /**
+ * @swagger
+ * /api/{version}/identity/verification/email/verify:
+ * post:
+ * summary: Verify email
+ * description: Verify user's email address using the token
+ * tags: [Identity - Verification]
+ * 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: [token]
+ * properties:
+ * token:
+ * type: string
+ * responses:
+ * 200:
+ * description: Email verified successfully
+ * content:
+ * application/json:
+ * schema:
+ * $ref: '#/components/schemas/ApiResponse'
+ */
+ router.post('/verification/email/verify', verificationController.verifyEmail.bind(verificationController));
+
+ /**
+ * @swagger
+ * /api/{version}/identity/verification/phone/request:
+ * post:
+ * summary: Request phone verification
+ * description: Send an SMS verification code to the user
+ * tags: [Identity - Verification]
+ * 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: [phoneNumber]
+ * properties:
+ * phoneNumber:
+ * type: string
+ * responses:
+ * 200:
+ * description: Verification SMS sent
+ * content:
+ * application/json:
+ * schema:
+ * $ref: '#/components/schemas/ApiResponse'
+ */
+ router.post('/verification/phone/request', authenticate(), verificationController.requestPhone.bind(verificationController));
+
+ /**
+ * @swagger
+ * /api/{version}/identity/verification/phone/verify:
+ * post:
+ * summary: Verify phone
+ * description: Verify user's phone number using the code
+ * tags: [Identity - Verification]
+ * 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, phoneNumber]
+ * properties:
+ * code:
+ * type: string
+ * phoneNumber:
+ * type: string
+ * responses:
+ * 200:
+ * description: Phone verified successfully
+ * content:
+ * application/json:
+ * schema:
+ * $ref: '#/components/schemas/ApiResponse'
+ */
+ router.post('/verification/phone/verify', verificationController.verifyPhone.bind(verificationController));
+
+ /**
+ * @swagger
+ * /api/{version}/identity/verification/{id}/status:
+ * get:
+ * summary: Get verification status
+ * description: Check the verification status of a user
+ * tags: [Identity - Verification]
+ * 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: User ID
+ * responses:
+ * 200:
+ * description: Verification status
+ * content:
+ * application/json:
+ * schema:
+ * allOf:
+ * - $ref: '#/components/schemas/ApiResponse'
+ * - type: object
+ * properties:
+ * data:
+ * type: object
+ * properties:
+ * emailVerified:
+ * type: boolean
+ * phoneVerified:
+ * type: boolean
+ */
+ router.get('/verification/:id/status', authenticate(), verificationController.getStatus.bind(verificationController));
+
+ // =============================================================================
+ // EN: Organization Endpoints
+ // VI: Endpoints Tổ Chức
+ // =============================================================================
+
+ /**
+ * @swagger
+ * /api/{version}/identity/organizations:
+ * get:
+ * summary: List organizations
+ * description: List organizations visible to the current user
+ * tags: [Identity - Organizations]
+ * security:
+ * - bearerAuth: []
+ * parameters:
+ * - in: path
+ * name: version
+ * required: true
+ * schema:
+ * type: string
+ * default: v1
+ * description: API version
+ * - in: query
+ * name: page
+ * schema:
+ * type: integer
+ * - in: query
+ * name: limit
+ * schema:
+ * type: integer
+ * responses:
+ * 200:
+ * description: Organizations list
+ * content:
+ * application/json:
+ * schema:
+ * allOf:
+ * - $ref: '#/components/schemas/ApiResponse'
+ * - type: object
+ * properties:
+ * data:
+ * type: array
+ * items:
+ * $ref: '#/components/schemas/Organization'
+ */
+ router.get('/organizations', authenticate(), requirePermission('identity', 'read'), organizationController.list.bind(organizationController));
+
+ /**
+ * @swagger
+ * /api/{version}/identity/organizations:
+ * post:
+ * summary: Create organization
+ * description: Create a new organization
+ * tags: [Identity - Organizations]
+ * 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/CreateOrganizationRequest'
+ * responses:
+ * 201:
+ * description: Organization created
+ * content:
+ * application/json:
+ * schema:
+ * allOf:
+ * - $ref: '#/components/schemas/ApiResponse'
+ * - type: object
+ * properties:
+ * data:
+ * $ref: '#/components/schemas/Organization'
+ */
+ router.post('/organizations', authenticate(), requirePermission('identity', 'create'), organizationController.create.bind(organizationController));
+
+ /**
+ * @swagger
+ * /api/{version}/identity/organizations/{id}:
+ * get:
+ * summary: Get organization
+ * description: Get organization details
+ * tags: [Identity - Organizations]
+ * 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: Organization ID
+ * responses:
+ * 200:
+ * description: Organization details
+ * content:
+ * application/json:
+ * schema:
+ * allOf:
+ * - $ref: '#/components/schemas/ApiResponse'
+ * - type: object
+ * properties:
+ * data:
+ * $ref: '#/components/schemas/Organization'
+ */
+ router.get('/organizations/:id', authenticate(), requirePermission('identity', 'read'), organizationController.get.bind(organizationController));
+
+ /**
+ * @swagger
+ * /api/{version}/identity/organizations/{id}:
+ * put:
+ * summary: Update organization
+ * description: Update organization details
+ * tags: [Identity - Organizations]
+ * 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: Organization ID
+ * requestBody:
+ * required: true
+ * content:
+ * application/json:
+ * schema:
+ * $ref: '#/components/schemas/UpdateOrganizationRequest'
+ * responses:
+ * 200:
+ * description: Organization updated
+ * content:
+ * application/json:
+ * schema:
+ * allOf:
+ * - $ref: '#/components/schemas/ApiResponse'
+ * - type: object
+ * properties:
+ * data:
+ * $ref: '#/components/schemas/Organization'
+ */
+ router.put('/organizations/:id', authenticate(), requirePermission('identity', 'update'), organizationController.update.bind(organizationController));
+
+ /**
+ * @swagger
+ * /api/{version}/identity/organizations/{id}:
+ * delete:
+ * summary: Delete organization
+ * description: Delete an organization
+ * tags: [Identity - Organizations]
+ * 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: Organization ID
+ * responses:
+ * 200:
+ * description: Organization deleted
+ * content:
+ * application/json:
+ * schema:
+ * $ref: '#/components/schemas/ApiResponse'
+ */
+ router.delete('/organizations/:id', authenticate(), requirePermission('identity', 'delete'), organizationController.delete.bind(organizationController));
+
+ /**
+ * @swagger
+ * /api/{version}/identity/organizations/{id}/users:
+ * get:
+ * summary: Get organization users
+ * description: List users in an organization
+ * tags: [Identity - Organizations]
+ * 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: Organization ID
+ * responses:
+ * 200:
+ * description: Users list
+ * content:
+ * application/json:
+ * schema:
+ * allOf:
+ * - $ref: '#/components/schemas/ApiResponse'
+ * - type: object
+ * properties:
+ * data:
+ * type: array
+ * items:
+ * $ref: '#/components/schemas/User'
+ */
+ router.get('/organizations/:id/users', authenticate(), requirePermission('identity', 'read'), organizationController.getUsers.bind(organizationController));
+
+ // =============================================================================
+ // EN: Group Endpoints
+ // VI: Endpoints Nhóm
+ // =============================================================================
+
+ /**
+ * @swagger
+ * /api/{version}/identity/organizations/{id}/groups:
+ * get:
+ * summary: List groups
+ * description: List groups in an organization
+ * tags: [Identity - Groups]
+ * 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: Organization ID
+ * responses:
+ * 200:
+ * description: Groups list
+ * content:
+ * application/json:
+ * schema:
+ * allOf:
+ * - $ref: '#/components/schemas/ApiResponse'
+ * - type: object
+ * properties:
+ * data:
+ * type: array
+ * items:
+ * $ref: '#/components/schemas/Group'
+ */
+ router.get('/organizations/:id/groups', authenticate(), requirePermission('identity', 'read'), groupController.list.bind(groupController));
+
+ /**
+ * @swagger
+ * /api/{version}/identity/organizations/{id}/groups:
+ * post:
+ * summary: Create group
+ * description: Create a new group in an organization
+ * tags: [Identity - Groups]
+ * 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: Organization ID
+ * requestBody:
+ * required: true
+ * content:
+ * application/json:
+ * schema:
+ * $ref: '#/components/schemas/CreateGroupRequest'
+ * responses:
+ * 201:
+ * description: Group created
+ * content:
+ * application/json:
+ * schema:
+ * allOf:
+ * - $ref: '#/components/schemas/ApiResponse'
+ * - type: object
+ * properties:
+ * data:
+ * $ref: '#/components/schemas/Group'
+ */
+ router.post('/organizations/:id/groups', authenticate(), requirePermission('identity', 'create'), groupController.create.bind(groupController));
+
+ /**
+ * @swagger
+ * /api/{version}/identity/groups/{id}:
+ * get:
+ * summary: Get group
+ * description: Get group details
+ * tags: [Identity - Groups]
+ * 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: Group ID
+ * responses:
+ * 200:
+ * description: Group details
+ * content:
+ * application/json:
+ * schema:
+ * allOf:
+ * - $ref: '#/components/schemas/ApiResponse'
+ * - type: object
+ * properties:
+ * data:
+ * $ref: '#/components/schemas/Group'
+ */
+ router.get('/groups/:id', authenticate(), requirePermission('identity', 'read'), groupController.get.bind(groupController));
+
+ /**
+ * @swagger
+ * /api/{version}/identity/groups/{id}:
+ * put:
+ * summary: Update group
+ * description: Update group details
+ * tags: [Identity - Groups]
+ * 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: Group ID
+ * requestBody:
+ * required: true
+ * content:
+ * application/json:
+ * schema:
+ * $ref: '#/components/schemas/UpdateGroupRequest'
+ * responses:
+ * 200:
+ * description: Group updated
+ * content:
+ * application/json:
+ * schema:
+ * allOf:
+ * - $ref: '#/components/schemas/ApiResponse'
+ * - type: object
+ * properties:
+ * data:
+ * $ref: '#/components/schemas/Group'
+ */
+ router.put('/groups/:id', authenticate(), requirePermission('identity', 'update'), groupController.update.bind(groupController));
+
+ /**
+ * @swagger
+ * /api/{version}/identity/groups/{id}:
+ * delete:
+ * summary: Delete group
+ * description: Delete a group
+ * tags: [Identity - Groups]
+ * 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: Group ID
+ * responses:
+ * 200:
+ * description: Group deleted
+ * content:
+ * application/json:
+ * schema:
+ * $ref: '#/components/schemas/ApiResponse'
+ */
+ router.delete('/groups/:id', authenticate(), requirePermission('identity', 'delete'), groupController.delete.bind(groupController));
+
+ /**
+ * @swagger
+ * /api/{version}/identity/groups/{id}/members:
+ * get:
+ * summary: Get group members
+ * description: List members of a group
+ * tags: [Identity - Groups]
+ * 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: Group ID
+ * responses:
+ * 200:
+ * description: Members list
+ * content:
+ * application/json:
+ * schema:
+ * allOf:
+ * - $ref: '#/components/schemas/ApiResponse'
+ * - type: object
+ * properties:
+ * data:
+ * type: array
+ * items:
+ * $ref: '#/components/schemas/User'
+ */
+ router.get('/groups/:id/members', authenticate(), requirePermission('identity', 'read'), groupController.getMembers.bind(groupController));
+
+ /**
+ * @swagger
+ * /api/{version}/identity/groups/{id}/members:
+ * post:
+ * summary: Add member to group
+ * description: Add a user to a group
+ * tags: [Identity - Groups]
+ * 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: Group ID
+ * requestBody:
+ * required: true
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * required: [userId]
+ * properties:
+ * userId:
+ * type: string
+ * responses:
+ * 200:
+ * description: Member added
+ * content:
+ * application/json:
+ * schema:
+ * $ref: '#/components/schemas/ApiResponse'
+ */
+ router.post('/groups/:id/members', authenticate(), requirePermission('identity', 'update'), groupController.addMember.bind(groupController));
+
+ /**
+ * @swagger
+ * /api/{version}/identity/groups/{id}/members/{userId}:
+ * delete:
+ * summary: Remove member from group
+ * description: Remove a user from a group
+ * tags: [Identity - Groups]
+ * 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: Group ID
+ * - in: path
+ * name: userId
+ * required: true
+ * schema:
+ * type: string
+ * description: User ID to remove
+ * responses:
+ * 200:
+ * description: Member removed
+ * content:
+ * application/json:
+ * schema:
+ * $ref: '#/components/schemas/ApiResponse'
+ */
+ router.delete('/groups/:id/members/:userId', authenticate(), requirePermission('identity', 'update'), groupController.removeMember.bind(groupController));
+
+ return router;
+};
diff --git a/services/iam-service/src/modules/mfa/mfa.routes.ts b/services/iam-service/src/modules/mfa/mfa.routes.ts
new file mode 100644
index 00000000..4c9eb348
--- /dev/null
+++ b/services/iam-service/src/modules/mfa/mfa.routes.ts
@@ -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;
+};
diff --git a/services/iam-service/src/modules/oidc/oidc.routes.ts b/services/iam-service/src/modules/oidc/oidc.routes.ts
new file mode 100644
index 00000000..1c144bb8
--- /dev/null
+++ b/services/iam-service/src/modules/oidc/oidc.routes.ts
@@ -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;
+};
diff --git a/services/iam-service/src/modules/rbac/rbac.routes.ts b/services/iam-service/src/modules/rbac/rbac.routes.ts
new file mode 100644
index 00000000..321a758f
--- /dev/null
+++ b/services/iam-service/src/modules/rbac/rbac.routes.ts
@@ -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;
+};
diff --git a/services/iam-service/src/modules/session/session.routes.ts b/services/iam-service/src/modules/session/session.routes.ts
new file mode 100644
index 00000000..fbf74e8d
--- /dev/null
+++ b/services/iam-service/src/modules/session/session.routes.ts
@@ -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;
+};
diff --git a/services/iam-service/src/routes/index.ts b/services/iam-service/src/routes/index.ts
index f000998a..6e6ae6b1 100644
--- a/services/iam-service/src/routes/index.ts
+++ b/services/iam-service/src/routes/index.ts
@@ -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;
};
-