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
- PostgreSQL now on dedicated server: 10.0.0.207:5432 - Database: parentflow (user: postgres, password: a3ppq) - Removed PostgreSQL from docker-compose.production.yml - Updated start-production.sh to check PostgreSQL connectivity - Updated migration scripts to use dedicated server - Created .env.admin.example for admin service configuration - Updated admin dashboard docs with PostgreSQL details - Redis, MongoDB, MinIO remain as Docker containers on 10.0.0.240 Infrastructure: - Application Server: 10.0.0.240 (PM2 + Docker services) - Database Server: 10.0.0.207 (PostgreSQL only) - Admin Server: 10.0.0.241 (future deployment) - Nginx Proxy: Separate server with SSL configured 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1682 lines
48 KiB
Markdown
1682 lines
48 KiB
Markdown
# Admin Dashboard Implementation Plan
|
|
|
|
**Created**: October 3, 2025
|
|
**Updated**: October 6, 2025 - Separate microservice architecture
|
|
**Status**: Planning Phase
|
|
**Priority**: High - Multi-tenancy & Administration
|
|
**Estimated Effort**: 6-8 weeks (120-160 hours)
|
|
|
|
---
|
|
|
|
## 📋 Executive Summary
|
|
|
|
### Overview
|
|
Create a comprehensive **standalone admin service** for managing ParentFlow applications across multiple platforms (web, iOS, Android) with role-based access control (RBAC), user management, system monitoring, and configuration. This service will be deployed independently and manage all ParentFlow instances centrally.
|
|
|
|
### Architecture Decision
|
|
The admin dashboard will be implemented as a **separate microservice** to:
|
|
- Manage both web and future mobile applications
|
|
- Scale independently from main application
|
|
- Provide centralized control across all platforms
|
|
- Enable separate deployment and security policies
|
|
- Support multi-tenant administration
|
|
|
|
### Key Features
|
|
1. **Invite Code Registration System**: Control user registration with invite codes
|
|
2. **User Management**: CRUD operations, data export, anonymization
|
|
3. **Role-Based Access Control**: Parent, Guest, Admin roles
|
|
4. **Multi-Platform Support**: Manage web, iOS, and Android users
|
|
5. **Multi-Profile Support**: Multiple families and account switching
|
|
6. **Analytics Dashboard**: Cross-platform usage metrics, AI/voice analytics
|
|
7. **System Health Monitoring**: Service status across all platforms
|
|
8. **LLM Configuration**: Model settings, API keys, pricing
|
|
9. **Content Management**: Legal pages, subscriptions
|
|
10. **Email Configuration**: Mailgun settings management
|
|
|
|
### Goals
|
|
- **Security**: Strict admin authentication and audit logging
|
|
- **Scalability**: Support growing user base
|
|
- **Flexibility**: Easy configuration updates without code changes
|
|
- **Compliance**: GDPR/COPPA data management tools
|
|
|
|
---
|
|
|
|
## 🏗️ Architecture Overview
|
|
|
|
### Microservice Architecture
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
│ ParentFlow Admin Service │
|
|
│ (Standalone Application) │
|
|
│ admin.parentflowapp.com │
|
|
│ │
|
|
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
|
|
│ │ Invite │ │ Users │ │ Analytics │ │
|
|
│ │ Codes │ │ Management │ │ Dashboard │ │
|
|
│ └──────────────┘ └──────────────┘ └──────────────┘ │
|
|
│ │
|
|
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
|
|
│ │ Platform │ │ System │ │ LLM │ │
|
|
│ │ Manager │ │ Health │ │ Settings │ │
|
|
│ └──────────────┘ └──────────────┘ └──────────────┘ │
|
|
└─────────────────────────────────────────────────────────────┘
|
|
│
|
|
Service-to-Service
|
|
Communication
|
|
│
|
|
┌─────────────────────┼─────────────────────┐
|
|
▼ ▼ ▼
|
|
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
|
|
│ ParentFlow │ │ ParentFlow │ │ ParentFlow │
|
|
│ Web │ │ iOS │ │ Android │
|
|
│ (NestJS) │ │ (Swift) │ │ (Kotlin) │
|
|
├──────────────┤ ├──────────────┤ ├──────────────┤
|
|
│ Validates │ │ Validates │ │ Validates │
|
|
│ invite codes │ │ invite codes │ │ invite codes │
|
|
│ with admin │ │ with admin │ │ with admin │
|
|
│ service │ │ service │ │ service │
|
|
└──────────────┘ └──────────────┘ └──────────────┘
|
|
│ │ │
|
|
▼ ▼ ▼
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
│ Shared Database Infrastructure │
|
|
│ │
|
|
│ • users (cross-platform) │
|
|
│ • invite_codes (registration control) │
|
|
│ • platform_sessions (web/ios/android tracking) │
|
|
│ • admin_audit_log (all admin actions) │
|
|
│ • system_configurations (per-platform settings) │
|
|
└─────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
### Admin Service Components
|
|
|
|
```
|
|
parentflow-admin-service/
|
|
├── src/
|
|
│ ├── modules/
|
|
│ │ ├── invite-codes/ # Invite code generation & validation
|
|
│ │ ├── user-management/ # Cross-platform user management
|
|
│ │ ├── platform-manager/ # Web/iOS/Android platform configs
|
|
│ │ ├── analytics/ # Unified analytics across platforms
|
|
│ │ ├── system-health/ # Monitor all platform services
|
|
│ │ ├── llm-config/ # AI configuration
|
|
│ │ ├── subscriptions/ # Subscription management
|
|
│ │ └── audit/ # Audit logging
|
|
│ ├── common/
|
|
│ │ ├── guards/ # Authentication guards
|
|
│ │ ├── interceptors/ # Service communication
|
|
│ │ └── decorators/ # Custom decorators
|
|
│ └── database/
|
|
│ └── migrations/ # Admin-specific migrations
|
|
├── admin-ui/ # React admin dashboard
|
|
│ ├── pages/
|
|
│ │ ├── invite-codes/ # Manage invite codes
|
|
│ │ ├── users/ # User management
|
|
│ │ ├── platforms/ # Platform-specific settings
|
|
│ │ └── analytics/ # Analytics dashboard
|
|
│ └── components/
|
|
│ └── shared/ # Shared UI components
|
|
└── docker-compose.admin.yml # Separate deployment
|
|
|
|
---
|
|
|
|
## 👥 Role-Based Access Control (RBAC)
|
|
|
|
### Role Definitions
|
|
|
|
#### 1. Parent (Default Role)
|
|
**Permissions**:
|
|
- Full access to their own families
|
|
- Create/manage children
|
|
- View all family data (activities, analytics)
|
|
- Invite guests to their families
|
|
- AI chat, voice commands
|
|
- Manage profile and settings
|
|
|
|
**Database**:
|
|
```typescript
|
|
enum UserRole {
|
|
PARENT = 'parent',
|
|
GUEST = 'guest',
|
|
ADMIN = 'admin',
|
|
}
|
|
```
|
|
|
|
#### 2. Guest (Limited Access)
|
|
**Permissions**:
|
|
- **CAN DO**:
|
|
- Add activities (feeding, sleep, diaper, medication) - manual or voice
|
|
- View real-time data for assigned family
|
|
- Receive notifications
|
|
- **CANNOT DO**:
|
|
- View historical data (>24 hours)
|
|
- View analytics/insights
|
|
- Edit or delete activities (except their own, within 1 hour)
|
|
- Manage children profiles
|
|
- Access AI chat
|
|
- Invite other users
|
|
- Change family settings
|
|
|
|
**Use Cases**:
|
|
- Nanny tracking daily activities
|
|
- Grandparents helping during visits
|
|
- Babysitter logging quick updates
|
|
|
|
#### 3. Admin (Full System Access)
|
|
**Permissions**:
|
|
- Access admin dashboard
|
|
- Manage all users (CRUD, anonymize, export)
|
|
- View system-wide analytics
|
|
- Configure LLM models
|
|
- Manage subscriptions and trials
|
|
- Edit legal pages
|
|
- Configure email settings
|
|
- View system health
|
|
- Audit logs access
|
|
|
|
**Security**:
|
|
- Admins CANNOT access user data without explicit audit trail
|
|
- All admin actions logged to `admin_audit_log`
|
|
- Require 2FA for admin accounts
|
|
|
|
---
|
|
|
|
## 🎟️ Invite Code Registration System
|
|
|
|
### Overview
|
|
Control user registration through invite codes to limit initial app access. This feature allows controlled rollout and beta testing across all platforms (web, iOS, Android).
|
|
|
|
### Features
|
|
- **Generate invite codes**: Batch or individual generation
|
|
- **Code types**: Single-use, multi-use, unlimited
|
|
- **Expiration dates**: Time-limited codes
|
|
- **Platform restrictions**: Limit codes to specific platforms
|
|
- **Usage tracking**: See who used which code
|
|
- **Enable/Disable**: Toggle registration requirement globally
|
|
|
|
### Database Schema
|
|
|
|
```sql
|
|
-- Invite codes table
|
|
CREATE TABLE invite_codes (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
code VARCHAR(20) UNIQUE NOT NULL, -- e.g., "BETA-2025-X7K9"
|
|
type VARCHAR(20) NOT NULL, -- 'single', 'multi', 'unlimited'
|
|
max_uses INTEGER DEFAULT 1, -- NULL for unlimited
|
|
current_uses INTEGER DEFAULT 0,
|
|
platform_restrictions VARCHAR(20)[], -- ['web', 'ios', 'android'] or NULL for all
|
|
expires_at TIMESTAMP,
|
|
metadata JSONB, -- Custom data: { "campaign": "beta", "referrer": "influencer1" }
|
|
created_by UUID REFERENCES users(id),
|
|
is_active BOOLEAN DEFAULT true,
|
|
created_at TIMESTAMP DEFAULT NOW(),
|
|
updated_at TIMESTAMP DEFAULT NOW()
|
|
);
|
|
|
|
CREATE INDEX idx_invite_codes_code ON invite_codes(code);
|
|
CREATE INDEX idx_invite_codes_active ON invite_codes(is_active) WHERE is_active = true;
|
|
|
|
-- Invite code usage tracking
|
|
CREATE TABLE invite_code_usage (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
invite_code_id UUID NOT NULL REFERENCES invite_codes(id),
|
|
used_by_user_id UUID NOT NULL REFERENCES users(id),
|
|
platform VARCHAR(20) NOT NULL, -- 'web', 'ios', 'android'
|
|
device_info JSONB,
|
|
ip_address INET,
|
|
used_at TIMESTAMP DEFAULT NOW()
|
|
);
|
|
|
|
CREATE INDEX idx_invite_code_usage_code ON invite_code_usage(invite_code_id);
|
|
CREATE INDEX idx_invite_code_usage_user ON invite_code_usage(used_by_user_id);
|
|
|
|
-- System configuration for registration
|
|
CREATE TABLE registration_config (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
require_invite_code BOOLEAN DEFAULT false,
|
|
registration_enabled BOOLEAN DEFAULT true,
|
|
custom_message TEXT, -- Message shown when registration is disabled
|
|
whitelist_domains VARCHAR(255)[], -- Email domains that bypass invite requirement
|
|
updated_by UUID REFERENCES users(id),
|
|
updated_at TIMESTAMP DEFAULT NOW()
|
|
);
|
|
```
|
|
|
|
### API Endpoints
|
|
|
|
#### Admin Endpoints
|
|
|
|
```typescript
|
|
// Admin Service API
|
|
POST /api/v1/admin/invite-codes // Generate codes
|
|
GET /api/v1/admin/invite-codes // List all codes
|
|
GET /api/v1/admin/invite-codes/:id // Get code details
|
|
PATCH /api/v1/admin/invite-codes/:id // Update code
|
|
DELETE /api/v1/admin/invite-codes/:id // Deactivate code
|
|
GET /api/v1/admin/invite-codes/:id/usage // Get usage history
|
|
|
|
// Batch operations
|
|
POST /api/v1/admin/invite-codes/batch // Generate multiple codes
|
|
POST /api/v1/admin/invite-codes/export // Export codes as CSV
|
|
|
|
// Configuration
|
|
GET /api/v1/admin/registration/config // Get registration settings
|
|
PATCH /api/v1/admin/registration/config // Update settings
|
|
```
|
|
|
|
#### Application Endpoints (Web/Mobile)
|
|
|
|
```typescript
|
|
// Public endpoint for all platforms
|
|
POST /api/v1/auth/validate-invite-code
|
|
{
|
|
"code": "BETA-2025-X7K9",
|
|
"platform": "ios"
|
|
}
|
|
|
|
// Response
|
|
{
|
|
"valid": true,
|
|
"message": "Code accepted",
|
|
"metadata": {
|
|
"campaign": "beta"
|
|
}
|
|
}
|
|
|
|
// Registration endpoint (modified)
|
|
POST /api/v1/auth/register
|
|
{
|
|
"email": "user@example.com",
|
|
"password": "...",
|
|
"inviteCode": "BETA-2025-X7K9", // Required if enabled
|
|
"platform": "web"
|
|
}
|
|
```
|
|
|
|
### Admin UI Components
|
|
|
|
```typescript
|
|
// pages/invite-codes/page.tsx
|
|
export default function InviteCodesPage() {
|
|
const [requireInvite, setRequireInvite] = useState(false);
|
|
|
|
return (
|
|
<div>
|
|
{/* Global toggle */}
|
|
<Card>
|
|
<h3>Registration Settings</h3>
|
|
<Switch
|
|
label="Require invite code for registration"
|
|
checked={requireInvite}
|
|
onChange={setRequireInvite}
|
|
/>
|
|
<Text>Currently: {requireInvite ? 'Invite only' : 'Open registration'}</Text>
|
|
</Card>
|
|
|
|
{/* Code generator */}
|
|
<Card>
|
|
<h3>Generate Invite Codes</h3>
|
|
<Form>
|
|
<Input label="Prefix" placeholder="BETA-2025" />
|
|
<Select label="Type">
|
|
<option value="single">Single use</option>
|
|
<option value="multi">Multi-use</option>
|
|
<option value="unlimited">Unlimited</option>
|
|
</Select>
|
|
<Input label="Max uses" type="number" />
|
|
<DatePicker label="Expires at" />
|
|
<MultiSelect label="Platform restrictions">
|
|
<option value="web">Web</option>
|
|
<option value="ios">iOS</option>
|
|
<option value="android">Android</option>
|
|
</MultiSelect>
|
|
<Input label="Quantity" type="number" defaultValue="1" />
|
|
<Button>Generate Codes</Button>
|
|
</Form>
|
|
</Card>
|
|
|
|
{/* Codes list */}
|
|
<DataTable
|
|
columns={[
|
|
{ key: 'code', label: 'Code' },
|
|
{ key: 'type', label: 'Type' },
|
|
{ key: 'uses', label: 'Uses', render: (row) => `${row.current_uses}/${row.max_uses || '∞'}` },
|
|
{ key: 'platforms', label: 'Platforms' },
|
|
{ key: 'expires_at', label: 'Expires' },
|
|
{ key: 'actions', label: 'Actions' }
|
|
]}
|
|
data={inviteCodes}
|
|
/>
|
|
</div>
|
|
);
|
|
}
|
|
```
|
|
|
|
### Implementation in Main Application
|
|
|
|
```typescript
|
|
// src/modules/auth/auth.service.ts (ParentFlow Web/API)
|
|
@Injectable()
|
|
export class AuthService {
|
|
|
|
async register(dto: RegisterDto) {
|
|
// Check if invite codes are required
|
|
const config = await this.getRegistrationConfig();
|
|
|
|
if (config.requireInviteCode) {
|
|
if (!dto.inviteCode) {
|
|
throw new BadRequestException('Invite code is required for registration');
|
|
}
|
|
|
|
// Validate with admin service
|
|
const validation = await this.adminService.validateInviteCode(
|
|
dto.inviteCode,
|
|
dto.platform || 'web'
|
|
);
|
|
|
|
if (!validation.valid) {
|
|
throw new BadRequestException(validation.message || 'Invalid invite code');
|
|
}
|
|
|
|
// Track usage
|
|
await this.adminService.recordInviteCodeUsage(
|
|
dto.inviteCode,
|
|
userId,
|
|
dto.platform
|
|
);
|
|
}
|
|
|
|
// Continue with normal registration...
|
|
}
|
|
|
|
private async getRegistrationConfig() {
|
|
// Cache this for performance
|
|
return await this.adminService.getRegistrationConfig();
|
|
}
|
|
}
|
|
```
|
|
|
|
### Mobile Integration
|
|
|
|
```swift
|
|
// iOS - RegistrationViewController.swift
|
|
func validateInviteCode(_ code: String) async throws -> Bool {
|
|
let response = try await AdminAPI.validateInviteCode(
|
|
code: code,
|
|
platform: "ios"
|
|
)
|
|
return response.valid
|
|
}
|
|
|
|
func register() async throws {
|
|
// Check if invite code is required
|
|
if registrationConfig.requireInviteCode {
|
|
guard let inviteCode = inviteCodeTextField.text, !inviteCode.isEmpty else {
|
|
throw RegistrationError.inviteCodeRequired
|
|
}
|
|
|
|
// Validate before proceeding
|
|
let isValid = try await validateInviteCode(inviteCode)
|
|
if !isValid {
|
|
throw RegistrationError.invalidInviteCode
|
|
}
|
|
}
|
|
|
|
// Continue with registration
|
|
}
|
|
```
|
|
|
|
### Analytics & Reporting
|
|
|
|
Track invite code effectiveness:
|
|
- **Conversion rate**: Codes generated vs. used
|
|
- **Platform distribution**: Usage by platform
|
|
- **Time to use**: Average time from generation to use
|
|
- **Referral tracking**: Which campaigns/sources are most effective
|
|
- **Geographic distribution**: Where users are registering from
|
|
|
|
---
|
|
|
|
## 🗄️ Database Schema Changes
|
|
|
|
### New Tables
|
|
|
|
#### 1. User Role Enhancement
|
|
```sql
|
|
-- Modify existing users table
|
|
ALTER TABLE users
|
|
ADD COLUMN global_role VARCHAR(20) DEFAULT 'parent',
|
|
ADD COLUMN is_admin BOOLEAN DEFAULT false,
|
|
ADD COLUMN admin_permissions JSONB DEFAULT '[]';
|
|
|
|
-- Add index for admin queries
|
|
CREATE INDEX idx_users_global_role ON users(global_role);
|
|
CREATE INDEX idx_users_is_admin ON users(is_admin) WHERE is_admin = true;
|
|
```
|
|
|
|
#### 2. Family Membership Roles
|
|
```sql
|
|
-- Modify family_memberships table
|
|
ALTER TABLE family_memberships
|
|
ADD COLUMN family_role VARCHAR(20) DEFAULT 'parent', -- parent or guest
|
|
ADD COLUMN permissions JSONB DEFAULT '{}',
|
|
ADD COLUMN invited_by UUID REFERENCES users(id),
|
|
ADD COLUMN access_granted_at TIMESTAMP DEFAULT NOW(),
|
|
ADD COLUMN access_expires_at TIMESTAMP NULL;
|
|
|
|
-- Constraints
|
|
ALTER TABLE family_memberships
|
|
ADD CONSTRAINT valid_family_role
|
|
CHECK (family_role IN ('parent', 'guest'));
|
|
```
|
|
|
|
#### 3. User Profiles (Multi-Profile Support)
|
|
```sql
|
|
CREATE TABLE user_profiles (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
profile_name VARCHAR(100) NOT NULL,
|
|
profile_type VARCHAR(20) NOT NULL, -- 'primary', 'work', 'personal'
|
|
default_family_id UUID REFERENCES families(id),
|
|
is_active BOOLEAN DEFAULT true,
|
|
created_at TIMESTAMP DEFAULT NOW(),
|
|
updated_at TIMESTAMP DEFAULT NOW(),
|
|
|
|
UNIQUE(user_id, profile_name)
|
|
);
|
|
|
|
CREATE INDEX idx_user_profiles_user_id ON user_profiles(user_id);
|
|
```
|
|
|
|
#### 4. Admin Audit Log
|
|
```sql
|
|
CREATE TABLE admin_audit_log (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
admin_user_id UUID NOT NULL REFERENCES users(id),
|
|
action VARCHAR(50) NOT NULL, -- 'user.create', 'user.delete', 'config.update'
|
|
target_entity VARCHAR(50), -- 'user', 'subscription', 'config'
|
|
target_id UUID,
|
|
changes JSONB, -- before/after values
|
|
ip_address INET,
|
|
user_agent TEXT,
|
|
created_at TIMESTAMP DEFAULT NOW()
|
|
);
|
|
|
|
CREATE INDEX idx_admin_audit_log_admin_user ON admin_audit_log(admin_user_id);
|
|
CREATE INDEX idx_admin_audit_log_created_at ON admin_audit_log(created_at DESC);
|
|
CREATE INDEX idx_admin_audit_log_action ON admin_audit_log(action);
|
|
```
|
|
|
|
#### 5. LLM Configuration
|
|
```sql
|
|
CREATE TABLE llm_config (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
provider VARCHAR(50) NOT NULL, -- 'azure', 'openai', 'anthropic'
|
|
service_type VARCHAR(50) NOT NULL, -- 'chat', 'whisper', 'embeddings'
|
|
endpoint_url TEXT NOT NULL,
|
|
api_key_encrypted TEXT NOT NULL, -- Encrypted with AES-256
|
|
model_name VARCHAR(100) NOT NULL,
|
|
deployment_name VARCHAR(100), -- For Azure
|
|
api_version VARCHAR(20),
|
|
price_per_1m_input_tokens DECIMAL(10, 4), -- e.g., 0.0015 for $1.50/1M
|
|
price_per_1m_output_tokens DECIMAL(10, 4),
|
|
max_tokens INTEGER DEFAULT 4000,
|
|
temperature DECIMAL(3, 2) DEFAULT 0.7,
|
|
is_active BOOLEAN DEFAULT true,
|
|
created_at TIMESTAMP DEFAULT NOW(),
|
|
updated_at TIMESTAMP DEFAULT NOW()
|
|
);
|
|
|
|
CREATE INDEX idx_llm_config_provider_service ON llm_config(provider, service_type);
|
|
```
|
|
|
|
#### 6. Subscription Plans
|
|
```sql
|
|
CREATE TABLE subscription_plans (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
name VARCHAR(100) NOT NULL UNIQUE,
|
|
description TEXT,
|
|
price_monthly DECIMAL(10, 2),
|
|
price_yearly DECIMAL(10, 2),
|
|
features JSONB, -- Feature flags: { "ai_queries": "unlimited", "voice_commands": 500 }
|
|
max_children INTEGER,
|
|
max_family_members INTEGER,
|
|
ai_query_limit INTEGER, -- NULL = unlimited
|
|
voice_command_limit INTEGER,
|
|
trial_period_days INTEGER DEFAULT 14,
|
|
is_active BOOLEAN DEFAULT true,
|
|
created_at TIMESTAMP DEFAULT NOW(),
|
|
updated_at TIMESTAMP DEFAULT NOW()
|
|
);
|
|
|
|
-- Add subscription to users
|
|
ALTER TABLE users
|
|
ADD COLUMN subscription_plan_id UUID REFERENCES subscription_plans(id),
|
|
ADD COLUMN subscription_status VARCHAR(20) DEFAULT 'trial', -- trial, active, expired, cancelled
|
|
ADD COLUMN subscription_started_at TIMESTAMP,
|
|
ADD COLUMN subscription_expires_at TIMESTAMP,
|
|
ADD COLUMN trial_ends_at TIMESTAMP;
|
|
```
|
|
|
|
#### 7. Email Configuration
|
|
```sql
|
|
CREATE TABLE email_config (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
provider VARCHAR(50) DEFAULT 'mailgun',
|
|
region VARCHAR(10) DEFAULT 'US', -- US or EU
|
|
api_key_encrypted TEXT NOT NULL,
|
|
domain VARCHAR(255) NOT NULL,
|
|
sender_email VARCHAR(255) NOT NULL,
|
|
sender_name VARCHAR(255) NOT NULL,
|
|
is_active BOOLEAN DEFAULT true,
|
|
created_at TIMESTAMP DEFAULT NOW(),
|
|
updated_at TIMESTAMP DEFAULT NOW()
|
|
);
|
|
```
|
|
|
|
#### 8. Legal Pages (CMS)
|
|
```sql
|
|
CREATE TABLE legal_pages (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
slug VARCHAR(100) NOT NULL UNIQUE, -- 'privacy', 'terms', 'eula', 'cookies'
|
|
title VARCHAR(255) NOT NULL,
|
|
content TEXT NOT NULL, -- Markdown or HTML
|
|
language VARCHAR(5) DEFAULT 'en',
|
|
version INTEGER DEFAULT 1,
|
|
is_published BOOLEAN DEFAULT false,
|
|
last_updated_by UUID REFERENCES users(id),
|
|
created_at TIMESTAMP DEFAULT NOW(),
|
|
updated_at TIMESTAMP DEFAULT NOW(),
|
|
|
|
UNIQUE(slug, language)
|
|
);
|
|
```
|
|
|
|
---
|
|
|
|
## 🔐 Security & Permissions
|
|
|
|
### Admin Guard Implementation
|
|
|
|
**File**: `src/common/guards/admin.guard.ts`
|
|
|
|
```typescript
|
|
import { Injectable, CanActivate, ExecutionContext, ForbiddenException } from '@nestjs/common';
|
|
import { JwtAuthGuard } from './jwt-auth.guard';
|
|
|
|
@Injectable()
|
|
export class AdminGuard extends JwtAuthGuard {
|
|
async canActivate(context: ExecutionContext): Promise<boolean> {
|
|
// First check JWT authentication
|
|
const isAuthenticated = await super.canActivate(context);
|
|
if (!isAuthenticated) {
|
|
return false;
|
|
}
|
|
|
|
const request = context.switchToHttp().getRequest();
|
|
const user = request.user;
|
|
|
|
// Check if user is admin
|
|
if (!user.isAdmin) {
|
|
throw new ForbiddenException('Admin access required');
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
```
|
|
|
|
### Family Role Guard
|
|
|
|
**File**: `src/common/guards/family-role.guard.ts`
|
|
|
|
```typescript
|
|
import { Injectable, CanActivate, ExecutionContext, ForbiddenException } from '@nestjs/common';
|
|
import { Reflector } from '@nestjs/core';
|
|
|
|
export const RequireFamilyRole = (...roles: string[]) =>
|
|
SetMetadata('familyRoles', roles);
|
|
|
|
@Injectable()
|
|
export class FamilyRoleGuard implements CanActivate {
|
|
constructor(private reflector: Reflector, private familyService: FamilyService) {}
|
|
|
|
async canActivate(context: ExecutionContext): Promise<boolean> {
|
|
const requiredRoles = this.reflector.get<string[]>('familyRoles', context.getHandler());
|
|
if (!requiredRoles) {
|
|
return true;
|
|
}
|
|
|
|
const request = context.switchToHttp().getRequest();
|
|
const user = request.user;
|
|
const familyId = request.params.familyId || request.body.familyId;
|
|
|
|
const membership = await this.familyService.getUserFamilyRole(user.id, familyId);
|
|
|
|
if (!membership) {
|
|
throw new ForbiddenException('Not a member of this family');
|
|
}
|
|
|
|
if (!requiredRoles.includes(membership.familyRole)) {
|
|
throw new ForbiddenException(`Requires family role: ${requiredRoles.join(' or ')}`);
|
|
}
|
|
|
|
// Attach to request for use in controllers
|
|
request.familyRole = membership.familyRole;
|
|
return true;
|
|
}
|
|
}
|
|
```
|
|
|
|
### Usage Example
|
|
|
|
```typescript
|
|
@Controller('api/v1/analytics')
|
|
export class AnalyticsController {
|
|
|
|
@Get(':childId/insights')
|
|
@UseGuards(JwtAuthGuard, FamilyRoleGuard)
|
|
@RequireFamilyRole('parent') // Guests cannot access analytics
|
|
async getInsights(@Param('childId') childId: string) {
|
|
// Only parents can access
|
|
}
|
|
|
|
@Post(':childId/activities')
|
|
@UseGuards(JwtAuthGuard, FamilyRoleGuard)
|
|
@RequireFamilyRole('parent', 'guest') // Both can add activities
|
|
async createActivity(@Param('childId') childId: string) {
|
|
// Both parents and guests can add
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 📊 Admin Dashboard Features
|
|
|
|
### 1. User Management
|
|
|
|
**Endpoint**: `GET /api/v1/admin/users`
|
|
|
|
**Features**:
|
|
- **List Users**: Paginated, searchable, filterable
|
|
- **User Details**: Full profile, activity history, subscription
|
|
- **Create User**: Manual user creation (for demos, test accounts)
|
|
- **Edit User**: Update profile, subscription, role
|
|
- **Delete User**: Soft delete with anonymization option
|
|
- **Anonymize User**: GDPR compliance - replace PII with anonymized data
|
|
- **Export User Data**: Full data export in JSON/CSV
|
|
- **Verify User**: Manual email verification override
|
|
- **Manage Subscription**: Change plan, extend trial, apply discount
|
|
|
|
**UI Components**:
|
|
```
|
|
maternal-web/app/admin/users/
|
|
├── page.tsx # User list with search/filter
|
|
├── [userId]/
|
|
│ ├── page.tsx # User detail view
|
|
│ ├── edit/page.tsx # Edit user form
|
|
│ └── export/page.tsx # Data export interface
|
|
└── create/page.tsx # Create user form
|
|
```
|
|
|
|
**Backend Module**:
|
|
```typescript
|
|
// src/modules/admin/user-management/user-management.service.ts
|
|
@Injectable()
|
|
export class UserManagementService {
|
|
|
|
async listUsers(filters: UserFilters, pagination: Pagination) {
|
|
// Query with filters, sorting, pagination
|
|
}
|
|
|
|
async getUserDetails(userId: string) {
|
|
// Full user details including:
|
|
// - Profile info
|
|
// - Families and roles
|
|
// - Subscription status
|
|
// - Activity stats
|
|
// - AI usage stats
|
|
}
|
|
|
|
async anonymizeUser(userId: string, adminId: string) {
|
|
// GDPR compliance
|
|
// Replace: name → "Anonymized User 12345"
|
|
// Replace: email → "anon_12345@maternal.local"
|
|
// Keep: aggregated analytics (anonymized)
|
|
// Log action in audit_log
|
|
}
|
|
|
|
async exportUserData(userId: string) {
|
|
// Full GDPR export
|
|
// - User profile
|
|
// - Children data
|
|
// - Activities
|
|
// - AI conversations
|
|
// - Photos (URLs)
|
|
return { json, csv };
|
|
}
|
|
|
|
async changeSubscription(userId: string, planId: string, adminId: string) {
|
|
// Update subscription
|
|
// Log in audit trail
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### 2. Role Management UI
|
|
|
|
**Components**:
|
|
|
|
```typescript
|
|
// components/admin/RoleSelector.tsx
|
|
export function RoleSelector({ userId, currentRole, onRoleChange }) {
|
|
return (
|
|
<Select value={currentRole} onChange={onRoleChange}>
|
|
<option value="parent">Parent (Full Access)</option>
|
|
<option value="guest">Guest (Limited Access)</option>
|
|
<option value="admin">Admin (System Access)</option>
|
|
</Select>
|
|
);
|
|
}
|
|
|
|
// components/admin/FamilyRoleManager.tsx
|
|
export function FamilyRoleManager({ userId, families }) {
|
|
// Show user's role in each family
|
|
// Allow changing role per family
|
|
return (
|
|
<Table>
|
|
{families.map(family => (
|
|
<TableRow key={family.id}>
|
|
<TableCell>{family.name}</TableCell>
|
|
<TableCell>
|
|
<RoleSelector
|
|
userId={userId}
|
|
familyId={family.id}
|
|
currentRole={family.userRole}
|
|
/>
|
|
</TableCell>
|
|
</TableRow>
|
|
))}
|
|
</Table>
|
|
);
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### 3. Multi-Profile Support
|
|
|
|
**User Stories**:
|
|
|
|
**Use Case 1**: Multiple Families, One Account
|
|
```
|
|
User: Sarah (Parent)
|
|
├── Profile: "Work Family" (childcare at work)
|
|
│ └── Family: "ABC Daycare"
|
|
│ ├── Child: Emma (not hers, she's a caregiver)
|
|
│ └── Role: Guest
|
|
└── Profile: "Home Family" (her own kids)
|
|
└── Family: "Smith Family"
|
|
├── Child: Liam
|
|
├── Child: Olivia
|
|
└── Role: Parent
|
|
```
|
|
|
|
**Use Case 2**: Multiple Accounts
|
|
```
|
|
User: John
|
|
├── Account 1: john@personal.com (Parent in "Doe Family")
|
|
└── Account 2: john@work.com (Guest in "Work Daycare")
|
|
```
|
|
|
|
**Implementation**:
|
|
|
|
```typescript
|
|
// components/layout/ProfileSwitcher.tsx
|
|
export function ProfileSwitcher() {
|
|
const { user, profiles, currentProfile, switchProfile } = useAuth();
|
|
|
|
return (
|
|
<Menu>
|
|
<MenuButton>
|
|
<Avatar src={currentProfile.avatar} />
|
|
{currentProfile.name}
|
|
</MenuButton>
|
|
<MenuList>
|
|
{profiles.map(profile => (
|
|
<MenuItem
|
|
key={profile.id}
|
|
onClick={() => switchProfile(profile.id)}
|
|
isActive={profile.id === currentProfile.id}
|
|
>
|
|
<Avatar src={profile.avatar} size="sm" />
|
|
<Box>
|
|
<Text>{profile.name}</Text>
|
|
<Text fontSize="xs" color="gray.500">
|
|
{profile.familyName} · {profile.role}
|
|
</Text>
|
|
</Box>
|
|
</MenuItem>
|
|
))}
|
|
<MenuDivider />
|
|
<MenuItem onClick={() => router.push('/add-profile')}>
|
|
+ Add Profile
|
|
</MenuItem>
|
|
</MenuList>
|
|
</Menu>
|
|
);
|
|
}
|
|
```
|
|
|
|
**Backend API**:
|
|
|
|
```typescript
|
|
// POST /api/v1/profiles
|
|
async createProfile(userId: string, dto: CreateProfileDto) {
|
|
// Create new profile
|
|
// Link to family
|
|
// Set default
|
|
}
|
|
|
|
// PATCH /api/v1/profiles/:id/switch
|
|
async switchProfile(userId: string, profileId: string) {
|
|
// Update user's active profile
|
|
// Return new auth context
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### 4. Analytics Dashboard
|
|
|
|
**Metrics to Track**:
|
|
|
|
#### User Analytics
|
|
- Total users (active, inactive, trial, paid)
|
|
- New users per day/week/month
|
|
- User growth rate
|
|
- Churn rate
|
|
- Average session duration
|
|
- Daily/Monthly Active Users (DAU/MAU)
|
|
|
|
#### Family & Children Analytics
|
|
- Total families
|
|
- Average children per family
|
|
- Average family size (members)
|
|
- Most active families
|
|
|
|
#### Feature Usage
|
|
- Activities logged per day (by type)
|
|
- AI queries per day
|
|
- Voice commands per day
|
|
- Photo uploads per day
|
|
- Most used features
|
|
|
|
#### AI/LLM Analytics
|
|
- Total AI queries (by model)
|
|
- Average tokens per query (input/output)
|
|
- Estimated costs per model
|
|
- Average response time
|
|
- Error rate
|
|
|
|
#### Voice Analytics
|
|
- Total voice commands
|
|
- Success rate (transcription accuracy)
|
|
- Most common voice intents
|
|
- Average processing time
|
|
|
|
**UI Components**:
|
|
|
|
```typescript
|
|
// app/admin/analytics/page.tsx
|
|
export default function AnalyticsDashboard() {
|
|
return (
|
|
<Grid columns={3}>
|
|
<StatCard
|
|
label="Total Users"
|
|
value={stats.totalUsers}
|
|
change={stats.userGrowth}
|
|
/>
|
|
<StatCard
|
|
label="Active Families"
|
|
value={stats.activeFamilies}
|
|
/>
|
|
<StatCard
|
|
label="AI Queries Today"
|
|
value={stats.aiQueriesToday}
|
|
cost={stats.estimatedCost}
|
|
/>
|
|
|
|
<Chart
|
|
title="User Growth"
|
|
data={stats.userGrowthData}
|
|
type="line"
|
|
/>
|
|
|
|
<Chart
|
|
title="Feature Usage"
|
|
data={stats.featureUsageData}
|
|
type="bar"
|
|
/>
|
|
|
|
<Chart
|
|
title="AI Cost Breakdown"
|
|
data={stats.aiCostData}
|
|
type="pie"
|
|
/>
|
|
</Grid>
|
|
);
|
|
}
|
|
```
|
|
|
|
**Backend**:
|
|
|
|
```typescript
|
|
// src/modules/admin/analytics/analytics-admin.service.ts
|
|
@Injectable()
|
|
export class AnalyticsAdminService {
|
|
|
|
async getSystemStats() {
|
|
return {
|
|
users: await this.getUserStats(),
|
|
families: await this.getFamilyStats(),
|
|
usage: await this.getUsageStats(),
|
|
ai: await this.getAIStats(),
|
|
voice: await this.getVoiceStats(),
|
|
};
|
|
}
|
|
|
|
async getAIStats() {
|
|
// Query from ai_conversations table
|
|
// Calculate token usage
|
|
// Estimate costs based on llm_config pricing
|
|
return {
|
|
totalQueries: 15234,
|
|
totalInputTokens: 2450000,
|
|
totalOutputTokens: 1850000,
|
|
estimatedCost: 5.47, // in USD
|
|
byModel: [
|
|
{ model: 'gpt-4o-mini', queries: 12000, cost: 3.20 },
|
|
{ model: 'gpt-4', queries: 3234, cost: 2.27 },
|
|
],
|
|
};
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### 5. System Health Monitoring
|
|
|
|
**Metrics**:
|
|
- Service status (backend, database, redis, mongodb)
|
|
- API response times (p50, p95, p99)
|
|
- Error rates
|
|
- Database connection pool status
|
|
- Redis cache hit/miss ratio
|
|
- Queue depths (background jobs)
|
|
- Memory/CPU usage
|
|
- Disk space
|
|
|
|
**Integration with Existing Health Endpoints**:
|
|
|
|
```typescript
|
|
// Reuse existing health endpoints
|
|
GET /health # Overall health
|
|
GET /health/liveness # Liveness probe
|
|
GET /health/readiness # Readiness probe
|
|
|
|
// New admin-specific endpoint
|
|
GET /api/v1/admin/system/health
|
|
|
|
// Response:
|
|
{
|
|
"status": "healthy",
|
|
"uptime": 345600, // seconds
|
|
"services": {
|
|
"postgres": { "status": "up", "responseTime": "15ms" },
|
|
"redis": { "status": "up", "responseTime": "2ms" },
|
|
"mongodb": { "status": "up", "responseTime": "18ms" },
|
|
"minio": { "status": "up", "responseTime": "45ms" },
|
|
"azureOpenAI": { "status": "up", "responseTime": "120ms" }
|
|
},
|
|
"metrics": {
|
|
"apiResponseTime": { "p50": 85, "p95": 250, "p99": 450 },
|
|
"errorRate": 0.02,
|
|
"requestsPerMinute": 1250,
|
|
"cacheHitRatio": 0.85
|
|
},
|
|
"resources": {
|
|
"memoryUsage": { "used": "2.1GB", "total": "4GB", "percentage": 52.5 },
|
|
"cpuUsage": 35.2,
|
|
"diskSpace": { "used": "45GB", "total": "100GB", "percentage": 45 }
|
|
}
|
|
}
|
|
```
|
|
|
|
**UI**:
|
|
|
|
```typescript
|
|
// app/admin/system/health/page.tsx
|
|
export default function SystemHealthPage() {
|
|
const { data, refetch } = useQuery('/api/v1/admin/system/health', {
|
|
refetchInterval: 10000, // Refresh every 10s
|
|
});
|
|
|
|
return (
|
|
<>
|
|
<HealthStatusBadge status={data.status} />
|
|
|
|
<ServiceStatusGrid services={data.services} />
|
|
|
|
<MetricsChart metrics={data.metrics} />
|
|
|
|
<ResourceUsageCards resources={data.resources} />
|
|
</>
|
|
);
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### 6. LLM Configuration Management
|
|
|
|
**UI Features**:
|
|
- Add/Edit LLM endpoints
|
|
- Manage API keys (encrypted storage)
|
|
- Set model names and deployments
|
|
- Configure pricing (per 1M tokens)
|
|
- Enable/disable models
|
|
- Test connections
|
|
|
|
**Backend**:
|
|
|
|
```typescript
|
|
// src/modules/admin/llm-config/llm-config.service.ts
|
|
@Injectable()
|
|
export class LLMConfigService {
|
|
|
|
async createConfig(dto: CreateLLMConfigDto, adminId: string) {
|
|
// Encrypt API key
|
|
const encryptedKey = await this.encryptionService.encrypt(dto.apiKey);
|
|
|
|
const config = await this.llmConfigRepository.save({
|
|
...dto,
|
|
apiKeyEncrypted: encryptedKey,
|
|
});
|
|
|
|
// Audit log
|
|
await this.auditService.log({
|
|
adminUserId: adminId,
|
|
action: 'llm_config.create',
|
|
targetId: config.id,
|
|
changes: { provider: dto.provider, serviceType: dto.serviceType },
|
|
});
|
|
|
|
return config;
|
|
}
|
|
|
|
async testConnection(configId: string) {
|
|
const config = await this.llmConfigRepository.findOne(configId);
|
|
const apiKey = await this.encryptionService.decrypt(config.apiKeyEncrypted);
|
|
|
|
try {
|
|
// Test API call
|
|
const response = await axios.post(config.endpointUrl, {
|
|
messages: [{ role: 'user', content: 'Test' }],
|
|
max_tokens: 10,
|
|
}, {
|
|
headers: { 'api-key': apiKey },
|
|
});
|
|
|
|
return { success: true, latency: response.duration };
|
|
} catch (error) {
|
|
return { success: false, error: error.message };
|
|
}
|
|
}
|
|
|
|
async getEstimatedCosts() {
|
|
// Calculate monthly costs based on usage
|
|
const usage = await this.getMonthlyTokenUsage();
|
|
|
|
return usage.map(model => ({
|
|
modelName: model.name,
|
|
inputTokens: model.inputTokens,
|
|
outputTokens: model.outputTokens,
|
|
estimatedCost: (
|
|
(model.inputTokens / 1000000) * model.pricePerMInput +
|
|
(model.outputTokens / 1000000) * model.pricePerMOutput
|
|
),
|
|
}));
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### 7. Content Management (Legal Pages)
|
|
|
|
**Features**:
|
|
- Create/Edit legal pages (Privacy Policy, Terms, EULA, Cookies)
|
|
- Markdown editor with preview
|
|
- Multi-language support
|
|
- Version history
|
|
- Publish/Unpublish
|
|
- SEO metadata
|
|
|
|
**UI**:
|
|
|
|
```typescript
|
|
// app/admin/pages/[slug]/edit/page.tsx
|
|
export default function EditLegalPage({ params }) {
|
|
const [content, setContent] = useState('');
|
|
const [language, setLanguage] = useState('en');
|
|
|
|
return (
|
|
<Form>
|
|
<Input label="Title" />
|
|
<Select label="Language" value={language} onChange={setLanguage}>
|
|
<option value="en">English</option>
|
|
<option value="es">Spanish</option>
|
|
<option value="fr">French</option>
|
|
</Select>
|
|
|
|
<MarkdownEditor
|
|
value={content}
|
|
onChange={setContent}
|
|
height="600px"
|
|
/>
|
|
|
|
<MarkdownPreview content={content} />
|
|
|
|
<ButtonGroup>
|
|
<Button variant="outline">Save Draft</Button>
|
|
<Button>Publish</Button>
|
|
</ButtonGroup>
|
|
</Form>
|
|
);
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### 8. Subscription Management
|
|
|
|
**Features**:
|
|
- Create subscription plans
|
|
- Set pricing (monthly/yearly)
|
|
- Define feature limits
|
|
- Trial period configuration
|
|
- Apply to users
|
|
- Discount codes
|
|
|
|
**Data Model**:
|
|
|
|
```typescript
|
|
interface SubscriptionPlan {
|
|
id: string;
|
|
name: string; // 'Free', 'Pro', 'Family'
|
|
description: string;
|
|
priceMonthly: number;
|
|
priceYearly: number;
|
|
features: {
|
|
aiQueries: 'unlimited' | number;
|
|
voiceCommands: 'unlimited' | number;
|
|
maxChildren: number;
|
|
maxFamilyMembers: number;
|
|
analytics: boolean;
|
|
prioritySupport: boolean;
|
|
};
|
|
trialPeriodDays: number;
|
|
}
|
|
```
|
|
|
|
**UI**:
|
|
|
|
```typescript
|
|
// app/admin/subscriptions/page.tsx
|
|
export default function SubscriptionPlansPage() {
|
|
return (
|
|
<>
|
|
<PlanList plans={plans} />
|
|
<Button onClick={openCreateDialog}>Create Plan</Button>
|
|
|
|
<CreatePlanDialog>
|
|
<Input label="Plan Name" />
|
|
<Input label="Monthly Price" type="number" prefix="$" />
|
|
<Input label="Yearly Price" type="number" prefix="$" />
|
|
<Input label="Trial Days" type="number" />
|
|
|
|
<FeatureLimits>
|
|
<Input label="Max Children" />
|
|
<Input label="AI Queries/Month" />
|
|
<Toggle label="Analytics Access" />
|
|
</FeatureLimits>
|
|
</CreatePlanDialog>
|
|
</>
|
|
);
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### 9. Email Configuration
|
|
|
|
**UI**:
|
|
|
|
```typescript
|
|
// app/admin/settings/email/page.tsx
|
|
export default function EmailSettingsPage() {
|
|
return (
|
|
<Form>
|
|
<Select label="Region">
|
|
<option value="US">US (api.mailgun.net)</option>
|
|
<option value="EU">EU (api.eu.mailgun.net)</option>
|
|
</Select>
|
|
|
|
<Input
|
|
label="API Key"
|
|
type="password"
|
|
placeholder="key-xxxxx"
|
|
/>
|
|
|
|
<Input label="Domain" placeholder="mg.maternal.com" />
|
|
|
|
<Input label="Sender Email" placeholder="noreply@maternal.com" />
|
|
|
|
<Input label="Sender Name" placeholder="Maternal App" />
|
|
|
|
<Button onClick={testEmail}>Send Test Email</Button>
|
|
<Button>Save Configuration</Button>
|
|
</Form>
|
|
);
|
|
}
|
|
```
|
|
|
|
**Backend**:
|
|
|
|
```typescript
|
|
// src/modules/admin/email-config/email-config.service.ts
|
|
@Injectable()
|
|
export class EmailConfigService {
|
|
|
|
async updateConfig(dto: UpdateEmailConfigDto, adminId: string) {
|
|
const encryptedKey = await this.encryptionService.encrypt(dto.apiKey);
|
|
|
|
const config = await this.emailConfigRepository.save({
|
|
...dto,
|
|
apiKeyEncrypted: encryptedKey,
|
|
});
|
|
|
|
// Update EmailService to use new config
|
|
await this.emailService.reloadConfig();
|
|
|
|
// Audit log
|
|
await this.auditService.log({
|
|
adminUserId: adminId,
|
|
action: 'email_config.update',
|
|
changes: { region: dto.region, domain: dto.domain },
|
|
});
|
|
|
|
return config;
|
|
}
|
|
|
|
async sendTestEmail(recipientEmail: string) {
|
|
try {
|
|
await this.emailService.sendEmail({
|
|
to: recipientEmail,
|
|
subject: 'Test Email from Maternal App Admin',
|
|
text: 'If you received this, email configuration is working!',
|
|
});
|
|
return { success: true };
|
|
} catch (error) {
|
|
return { success: false, error: error.message };
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 🚀 Deployment Strategy
|
|
|
|
### Admin Service Deployment
|
|
|
|
The admin service will be deployed as a **separate microservice**:
|
|
|
|
#### Infrastructure
|
|
```yaml
|
|
# docker-compose.admin.yml
|
|
version: '3.8'
|
|
|
|
services:
|
|
admin-api:
|
|
build: ./parentflow-admin-service
|
|
container_name: parentflow-admin-api
|
|
ports:
|
|
- "4000:4000" # Admin API on separate port
|
|
environment:
|
|
NODE_ENV: production
|
|
# Dedicated PostgreSQL Server
|
|
DATABASE_URL: postgresql://postgres:a3ppq@10.0.0.207:5432/parentflow
|
|
DATABASE_HOST: 10.0.0.207
|
|
DATABASE_PORT: 5432
|
|
DATABASE_NAME: parentflow
|
|
DATABASE_USER: postgres
|
|
DATABASE_PASSWORD: a3ppq
|
|
# Redis on 10.0.0.240
|
|
REDIS_HOST: 10.0.0.240
|
|
REDIS_PORT: 6379
|
|
# MongoDB on 10.0.0.240
|
|
MONGODB_URI: mongodb://parentflow_admin:parentflow_mongo_password_2024@10.0.0.240:27017/parentflow_ai_chat?authSource=admin
|
|
# Auth keys
|
|
JWT_SECRET: ${ADMIN_JWT_SECRET}
|
|
SERVICE_AUTH_KEY: ${SERVICE_AUTH_KEY} # For service-to-service auth
|
|
networks:
|
|
- parentflow-network
|
|
extra_hosts:
|
|
- "host.docker.internal:host-gateway" # For accessing host services
|
|
|
|
admin-ui:
|
|
build: ./parentflow-admin-service/admin-ui
|
|
container_name: parentflow-admin-ui
|
|
ports:
|
|
- "4001:3000" # Admin UI
|
|
environment:
|
|
REACT_APP_API_URL: http://admin-api:4000
|
|
REACT_APP_MAIN_APP_URL: https://web.parentflowapp.com
|
|
networks:
|
|
- parentflow-network
|
|
|
|
networks:
|
|
parentflow-network:
|
|
external: true
|
|
```
|
|
|
|
#### Service Communication
|
|
|
|
```typescript
|
|
// Service-to-service authentication
|
|
export class AdminServiceClient {
|
|
private readonly serviceKey: string;
|
|
private readonly adminApiUrl: string;
|
|
|
|
constructor() {
|
|
this.serviceKey = process.env.SERVICE_AUTH_KEY;
|
|
this.adminApiUrl = process.env.ADMIN_API_URL || 'http://localhost:4000';
|
|
}
|
|
|
|
async validateInviteCode(code: string, platform: string): Promise<ValidationResult> {
|
|
const response = await axios.post(
|
|
`${this.adminApiUrl}/api/v1/internal/validate-invite-code`,
|
|
{ code, platform },
|
|
{
|
|
headers: {
|
|
'X-Service-Auth': this.serviceKey,
|
|
'X-Service-Name': 'parentflow-web'
|
|
}
|
|
}
|
|
);
|
|
return response.data;
|
|
}
|
|
|
|
async getRegistrationConfig(): Promise<RegistrationConfig> {
|
|
// Cache this for 5 minutes
|
|
return this.cache.get('registration-config', async () => {
|
|
const response = await axios.get(
|
|
`${this.adminApiUrl}/api/v1/internal/registration-config`,
|
|
{
|
|
headers: {
|
|
'X-Service-Auth': this.serviceKey,
|
|
'X-Service-Name': 'parentflow-web'
|
|
}
|
|
}
|
|
);
|
|
return response.data;
|
|
}, 300); // 5 minutes TTL
|
|
}
|
|
}
|
|
```
|
|
|
|
#### Nginx Configuration
|
|
|
|
```nginx
|
|
# admin.parentflowapp.com
|
|
server {
|
|
listen 443 ssl http2;
|
|
server_name admin.parentflowapp.com;
|
|
|
|
# SSL configuration
|
|
ssl_certificate /etc/letsencrypt/live/admin.parentflowapp.com/fullchain.pem;
|
|
ssl_certificate_key /etc/letsencrypt/live/admin.parentflowapp.com/privkey.pem;
|
|
|
|
# IP whitelist for admin access (optional)
|
|
allow 10.0.0.0/24; # Office network
|
|
allow 192.168.1.0/24; # VPN
|
|
deny all;
|
|
|
|
# Admin UI
|
|
location / {
|
|
proxy_pass http://localhost:4001;
|
|
proxy_http_version 1.1;
|
|
proxy_set_header Upgrade $http_upgrade;
|
|
proxy_set_header Connection 'upgrade';
|
|
proxy_set_header Host $host;
|
|
proxy_cache_bypass $http_upgrade;
|
|
}
|
|
|
|
# Admin API
|
|
location /api/ {
|
|
proxy_pass http://localhost:4000;
|
|
proxy_http_version 1.1;
|
|
proxy_set_header X-Real-IP $remote_addr;
|
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
proxy_set_header X-Forwarded-Proto $scheme;
|
|
}
|
|
}
|
|
```
|
|
|
|
### Deployment Servers
|
|
|
|
```
|
|
Production Environment:
|
|
├── Admin Service
|
|
│ ├── Server: 10.0.0.241 (separate from main app)
|
|
│ ├── URL: admin.parentflowapp.com
|
|
│ ├── Ports: 4000 (API), 4001 (UI)
|
|
│ └── Database: Connects to dedicated PostgreSQL server
|
|
│
|
|
├── Main Applications
|
|
│ ├── Web: 10.0.0.240:3030 → web.parentflowapp.com
|
|
│ ├── API: 10.0.0.240:3020 → api.parentflowapp.com
|
|
│ └── Mobile APIs: Will connect to api.parentflowapp.com
|
|
│
|
|
├── Database Infrastructure
|
|
│ ├── PostgreSQL: 10.0.0.207:5432
|
|
│ │ ├── Database: parentflow
|
|
│ │ ├── User: postgres
|
|
│ │ └── Password: a3ppq
|
|
│ │
|
|
│ └── Docker on 10.0.0.240
|
|
│ ├── Redis: Port 6379
|
|
│ ├── MongoDB: Port 27017
|
|
│ └── MinIO: Ports 9000/9001
|
|
│
|
|
└── Nginx Proxy Server (Separate)
|
|
├── SSL certificates configured
|
|
└── Reverse proxy for all domains
|
|
```
|
|
|
|
---
|
|
|
|
## 🚀 Implementation Timeline
|
|
|
|
### Phase 1: Foundation (Week 1-2)
|
|
|
|
**Week 1: Database & Backend Core**
|
|
- [ ] Create database migrations for new tables
|
|
- [ ] Implement role enums and guards
|
|
- [ ] Create AdminGuard and FamilyRoleGuard
|
|
- [ ] Set up admin API module structure
|
|
- [ ] Implement audit logging service
|
|
|
|
**Week 2: User Management**
|
|
- [ ] User management endpoints (CRUD)
|
|
- [ ] Anonymization logic
|
|
- [ ] Data export functionality
|
|
- [ ] Subscription management endpoints
|
|
- [ ] Unit tests for user management
|
|
|
|
---
|
|
|
|
### Phase 2: Admin UI (Week 3-4)
|
|
|
|
**Week 3: Admin Dashboard Layout**
|
|
- [ ] Admin layout component
|
|
- [ ] Navigation sidebar
|
|
- [ ] User management UI (list, detail, edit)
|
|
- [ ] Role management UI
|
|
- [ ] Multi-profile UI components
|
|
|
|
**Week 4: Analytics & Monitoring**
|
|
- [ ] Analytics dashboard UI
|
|
- [ ] System health monitoring UI
|
|
- [ ] Charts and visualizations
|
|
- [ ] Real-time metric updates
|
|
|
|
---
|
|
|
|
### Phase 3: Configuration (Week 5-6)
|
|
|
|
**Week 5: LLM & Email Config**
|
|
- [ ] LLM configuration UI
|
|
- [ ] API key management (encrypted)
|
|
- [ ] Connection testing
|
|
- [ ] Email settings UI
|
|
- [ ] Test email functionality
|
|
|
|
**Week 6: Content & Subscriptions**
|
|
- [ ] Legal pages CMS
|
|
- [ ] Markdown editor integration
|
|
- [ ] Subscription plan management UI
|
|
- [ ] Trial period configuration
|
|
- [ ] Discount codes
|
|
|
|
---
|
|
|
|
### Phase 4: Security & Testing (Week 7-8)
|
|
|
|
**Week 7: Security Hardening**
|
|
- [ ] 2FA for admin accounts
|
|
- [ ] Admin session timeout (15 min)
|
|
- [ ] IP whitelisting option
|
|
- [ ] Audit log viewer UI
|
|
- [ ] Security testing
|
|
|
|
**Week 8: Final Testing & Documentation**
|
|
- [ ] Integration tests for all admin endpoints
|
|
- [ ] E2E tests for critical admin flows
|
|
- [ ] Performance testing
|
|
- [ ] Admin user documentation
|
|
- [ ] Developer API documentation
|
|
|
|
---
|
|
|
|
## ✅ Acceptance Criteria
|
|
|
|
### Security
|
|
- [ ] Only users with `isAdmin=true` can access admin dashboard
|
|
- [ ] All admin actions logged to audit trail
|
|
- [ ] API keys encrypted at rest (AES-256)
|
|
- [ ] Admin sessions expire after 15 minutes of inactivity
|
|
- [ ] 2FA required for admin accounts
|
|
|
|
### User Management
|
|
- [ ] Admin can create/edit/delete users
|
|
- [ ] Admin can anonymize user data (GDPR)
|
|
- [ ] Admin can export user data in JSON/CSV
|
|
- [ ] Admin can change user subscriptions
|
|
- [ ] Admin can verify user emails manually
|
|
|
|
### Role Management
|
|
- [ ] Parents have full access to their families
|
|
- [ ] Guests can only add activities, no historical data
|
|
- [ ] Admins have system-wide access
|
|
- [ ] Family roles can be changed per user per family
|
|
|
|
### Multi-Profile
|
|
- [ ] Users can switch between multiple families
|
|
- [ ] Users can manage multiple accounts
|
|
- [ ] Profile switcher in user menu
|
|
- [ ] Default profile saved per user
|
|
|
|
### Analytics
|
|
- [ ] Admin can view user growth metrics
|
|
- [ ] Admin can view feature usage stats
|
|
- [ ] Admin can view AI/LLM costs and usage
|
|
- [ ] Admin can view voice command analytics
|
|
- [ ] Real-time metrics with 10s refresh
|
|
|
|
### System Health
|
|
- [ ] Admin can view service status
|
|
- [ ] Admin can view API performance metrics
|
|
- [ ] Admin can view resource usage
|
|
- [ ] Alerts for service degradation
|
|
|
|
### Configuration
|
|
- [ ] Admin can add/edit LLM models
|
|
- [ ] Admin can test LLM connections
|
|
- [ ] Admin can view estimated AI costs
|
|
- [ ] Admin can update email settings
|
|
- [ ] Admin can edit legal pages
|
|
- [ ] Admin can create subscription plans
|
|
|
|
---
|
|
|
|
## 📚 Additional Features (Future Enhancements)
|
|
|
|
### Advanced Analytics
|
|
- [ ] Custom report builder
|
|
- [ ] Data export scheduler
|
|
- [ ] Cohort analysis
|
|
- [ ] Funnel visualization
|
|
- [ ] A/B testing dashboard
|
|
|
|
### Automation
|
|
- [ ] Automated user onboarding emails
|
|
- [ ] Trial expiration reminders
|
|
- [ ] Inactive user re-engagement
|
|
- [ ] Subscription renewal reminders
|
|
|
|
### Advanced Security
|
|
- [ ] IP whitelisting for admin access
|
|
- [ ] Admin activity anomaly detection
|
|
- [ ] Automated threat response
|
|
- [ ] SIEM integration
|
|
|
|
### Support Tools
|
|
- [ ] In-app user support chat
|
|
- [ ] Impersonate user (for debugging)
|
|
- [ ] Feature flag management
|
|
- [ ] Rollback deployments
|
|
|
|
---
|
|
|
|
## 🔗 Related Documentation
|
|
|
|
- [RBAC Implementation Guide](./RBAC_IMPLEMENTATION.md) (to be created)
|
|
- [Audit Logging Standards](./AUDIT_LOGGING.md) (to be created)
|
|
- [Data Anonymization Procedures](./DATA_ANONYMIZATION.md) (to be created)
|
|
- [API Gateway Architecture](./API_GATEWAY_ARCHITECTURE.md)
|
|
|
|
---
|
|
|
|
**Last Updated**: October 3, 2025
|
|
**Next Review**: After Phase 1 completion
|
|
**Owner**: Backend Team + Admin Team
|