feat: Complete production deployment pipeline with admin dashboard
Some checks failed
ParentFlow CI/CD Pipeline / Backend Tests (push) Has been cancelled
ParentFlow CI/CD Pipeline / Frontend Tests (push) Has been cancelled
ParentFlow CI/CD Pipeline / Security Scanning (push) Has been cancelled
ParentFlow CI/CD Pipeline / Build Docker Images (map[context:maternal-app/maternal-app-backend dockerfile:Dockerfile.production name:backend]) (push) Has been cancelled
ParentFlow CI/CD Pipeline / Build Docker Images (map[context:maternal-web dockerfile:Dockerfile.production name:frontend]) (push) Has been cancelled
ParentFlow CI/CD Pipeline / Deploy to Development (push) Has been cancelled
ParentFlow CI/CD Pipeline / Deploy to Production (push) Has been cancelled
CI/CD Pipeline / Lint and Test (push) Has been cancelled
CI/CD Pipeline / E2E Tests (push) Has been cancelled
CI/CD Pipeline / Build Application (push) Has been cancelled

- Add unified deployment script with Node.js 22 installation
- Create comprehensive database migration script (28 migrations + admin tables)
- Add production start/stop scripts for all services
- Integrate admin dashboard (parentflow-admin) into PM2 ecosystem
- Configure all services: Backend (3020), Frontend (3030), Admin (3335)
- Update ecosystem.config.js with admin dashboard configuration
- Add invite codes module for user registration management
This commit is contained in:
2025-10-06 22:43:28 +00:00
parent 560fd22023
commit 4e19b992df
37 changed files with 6767 additions and 675 deletions

View File

@@ -0,0 +1,183 @@
import axios from 'axios';
const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3020/api/v1';
class ApiClient {
private token: string | null = null;
private refreshToken: string | null = null;
constructor() {
// Initialize tokens from localStorage if available
if (typeof window !== 'undefined') {
this.token = localStorage.getItem('admin_access_token');
this.refreshToken = localStorage.getItem('admin_refresh_token');
}
}
setTokens(accessToken: string, refreshToken: string) {
this.token = accessToken;
this.refreshToken = refreshToken;
if (typeof window !== 'undefined') {
localStorage.setItem('admin_access_token', accessToken);
localStorage.setItem('admin_refresh_token', refreshToken);
}
}
clearTokens() {
this.token = null;
this.refreshToken = null;
if (typeof window !== 'undefined') {
localStorage.removeItem('admin_access_token');
localStorage.removeItem('admin_refresh_token');
}
}
private async request(method: string, endpoint: string, data?: any, options?: any) {
const config = {
method,
url: `${API_BASE_URL}${endpoint}`,
headers: {
'Content-Type': 'application/json',
...(this.token ? { Authorization: `Bearer ${this.token}` } : {}),
...options?.headers,
},
...options,
};
if (data) {
config.data = data;
}
try {
const response = await axios(config);
return response.data;
} catch (error: any) {
// Handle token refresh
if (error.response?.status === 401 && this.refreshToken) {
try {
const refreshResponse = await axios.post(`${API_BASE_URL}/auth/refresh`, {
refreshToken: this.refreshToken,
});
this.setTokens(refreshResponse.data.accessToken, refreshResponse.data.refreshToken);
// Retry original request
config.headers.Authorization = `Bearer ${this.token}`;
const response = await axios(config);
return response.data;
} catch (refreshError) {
this.clearTokens();
window.location.href = '/login';
throw refreshError;
}
}
throw error;
}
}
// Auth endpoints
async login(email: string, password: string) {
const response = await this.request('POST', '/admin/auth/login', { email, password });
this.setTokens(response.accessToken, response.refreshToken);
return response;
}
async logout() {
try {
await this.request('POST', '/admin/auth/logout');
} finally {
this.clearTokens();
}
}
async getCurrentAdmin() {
return this.request('GET', '/admin/auth/me');
}
// User management endpoints
async getUsers(params?: { page?: number; limit?: number; search?: string }) {
const queryString = params ? '?' + new URLSearchParams(params as any).toString() : '';
return this.request('GET', `/admin/users${queryString}`);
}
async getUserById(id: string) {
return this.request('GET', `/admin/users/${id}`);
}
async updateUser(id: string, data: any) {
return this.request('PATCH', `/admin/users/${id}`, data);
}
async deleteUser(id: string) {
return this.request('DELETE', `/admin/users/${id}`);
}
// Invite code endpoints
async getInviteCodes(params?: { page?: number; limit?: number; status?: string }) {
const queryString = params ? '?' + new URLSearchParams(params as any).toString() : '';
return this.request('GET', `/admin/invite-codes${queryString}`);
}
async createInviteCode(data: {
code: string;
maxUses?: number;
expiresAt?: string;
metadata?: any;
}) {
return this.request('POST', '/admin/invite-codes', data);
}
async updateInviteCode(id: string, data: any) {
return this.request('PATCH', `/admin/invite-codes/${id}`, data);
}
async deleteInviteCode(id: string) {
return this.request('DELETE', `/admin/invite-codes/${id}`);
}
// Analytics endpoints
async getAnalytics(params?: { startDate?: string; endDate?: string }) {
const queryString = params ? '?' + new URLSearchParams(params as any).toString() : '';
return this.request('GET', `/admin/analytics${queryString}`);
}
async getUserGrowth() {
return this.request('GET', '/admin/analytics/user-growth');
}
async getActivityStats() {
return this.request('GET', '/admin/analytics/activity-stats');
}
async getSystemHealth() {
return this.request('GET', '/admin/analytics/system-health');
}
// Family management
async getFamilies(params?: { page?: number; limit?: number; search?: string }) {
const queryString = params ? '?' + new URLSearchParams(params as any).toString() : '';
return this.request('GET', `/admin/families${queryString}`);
}
async getFamilyById(id: string) {
return this.request('GET', `/admin/families/${id}`);
}
// Activity logs
async getActivityLogs(params?: { page?: number; limit?: number; userId?: string }) {
const queryString = params ? '?' + new URLSearchParams(params as any).toString() : '';
return this.request('GET', `/admin/logs${queryString}`);
}
// System settings
async getSettings() {
return this.request('GET', '/admin/settings');
}
async updateSettings(data: any) {
return this.request('PATCH', '/admin/settings', data);
}
}
export const apiClient = new ApiClient();
export default apiClient;