✨ Major Features Added: - AI Chat with conversation memory and university-specific knowledge base - Multi-tenant university support with white-label capabilities - Professional admin interface for knowledge base management - Advanced database schema with Prisma ORM - Comprehensive documentation and guides - Modern Next.js 15 + React 19 architecture - Bilingual support (English/Arabic) - Role-based access control - Real-time chat interface with loading states 🔧 Technical Improvements: - Fixed all linter errors and TypeScript issues - Cleaned up codebase and removed legacy files - Added comprehensive .gitignore - Updated README with detailed setup instructions - Optimized database schema and migrations - Enhanced error handling and user experience 📚 Documentation: - AI Conversation Memory Guide - AI Enhancement Summary - Developer Guide - User Guide - Complete setup and deployment instructions 🚀 Ready for GitHub deployment and production use!
17 KiB
17 KiB
White-Label University Portal - Developer Guide
📚 Table of Contents
- Architecture Overview
- Getting Started
- API Documentation
- Database Schema
- Multi-Tenant Architecture
- Performance Optimization
- Deployment Guide
- Testing
- Contributing
- Troubleshooting
🏗️ Architecture Overview
Technology Stack
- Frontend: Next.js 14, React 19, TypeScript, Tailwind CSS
- Backend: Next.js API Routes, Prisma ORM
- Database: PostgreSQL (primary), Redis (caching)
- AI: OpenRouter AI integration
- CDN: Multi-provider support (AWS S3, Cloudflare, Cloudinary)
- Deployment: Vercel, Docker, Kubernetes
System Architecture
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Frontend │ │ API Layer │ │ Database │
│ (Next.js) │◄──►│ (Next.js) │◄──►│ (PostgreSQL) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │ │
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ CDN Layer │ │ Cache Layer │ │ AI Services │
│ (Multi-CDN) │ │ (Redis) │ │ (OpenRouter) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
Multi-Tenant Design
The platform uses a database-per-tenant approach with shared application code:
- Data Isolation: Each university's data is completely isolated
- Shared Infrastructure: Common codebase and infrastructure
- Dynamic Configuration: University-specific settings and branding
- Scalable Architecture: Horizontal scaling support
🚀 Getting Started
Prerequisites
- Node.js: 18.x or higher
- PostgreSQL: 14.x or higher
- Redis: 6.x or higher
- Git: Latest version
- Docker: (optional, for containerized development)
Installation
-
Clone the Repository
git clone https://github.com/your-org/university-portal.git cd university-portal -
Install Dependencies
npm install -
Environment Setup
cp .env.example .env.local # Edit .env.local with your configuration -
Database Setup
npx prisma generate npx prisma db push npx prisma db seed -
Start Development Server
npm run dev
Environment Variables
# Database
DATABASE_URL="postgresql://user:password@localhost:5432/university_portal"
# Redis
REDIS_HOST="localhost"
REDIS_PORT="6379"
REDIS_PASSWORD=""
REDIS_DB="0"
# AI Services
OPENROUTER_API_KEY="your-api-key"
OPENROUTER_BASE_URL="https://openrouter.ai/api/v1"
# CDN Configuration
AWS_ACCESS_KEY_ID="your-access-key"
AWS_SECRET_ACCESS_KEY="your-secret-key"
AWS_REGION="us-east-1"
AWS_S3_BUCKET="your-bucket"
# Application
NEXT_PUBLIC_APP_URL="http://localhost:3000"
NEXTAUTH_SECRET="your-secret"
NEXTAUTH_URL="http://localhost:3000"
📡 API Documentation
Authentication
All API endpoints require university context via headers:
X-University-Id: university-id
X-University-Slug: university-slug
Core Endpoints
Universities
GET /api/universities
POST /api/universities
GET /api/universities/[slug]
PUT /api/universities/[slug]
DELETE /api/universities/[slug]
Content Management
GET /api/content
POST /api/content
GET /api/content/[id]
PUT /api/content/[id]
DELETE /api/content/[id]
Programs
GET /api/programs
POST /api/programs
GET /api/programs/[id]
PUT /api/programs/[id]
DELETE /api/programs/[id]
Domains
GET /api/domains
POST /api/domains
GET /api/domains/[id]
PUT /api/domains/[id]
DELETE /api/domains/[id]
POST /api/domains/[id]/validate
POST /api/domains/[id]/renew-ssl
Deployments
GET /api/deployments
POST /api/deployments
GET /api/deployments/[id]
POST /api/deployments/[id]/execute
POST /api/deployments/[id]/rollback
Response Format
All API responses follow a consistent format:
{
"success": true,
"data": {
// Response data
},
"message": "Operation completed successfully",
"timestamp": "2025-01-01T00:00:00.000Z"
}
Error Handling
{
"success": false,
"error": "Error message",
"code": "ERROR_CODE",
"timestamp": "2025-01-01T00:00:00.000Z"
}
🗄️ Database Schema
Core Models
University
model University {
id String @id @default(uuid())
slug String @unique
name String
shortName String?
domain String?
subdomain String?
branding Json
contact Json
features Json
ai Json
status UniversityStatus @default(ACTIVE)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
assets UniversityAsset[]
content UniversityContent[]
programs AcademicProgram[]
knowledge AIKnowledgeBase[]
domains DomainConfig[]
deployments DeploymentConfig[]
cdnConfig CDNConfig?
cdnAssets CDNAsset[]
}
UniversityContent
model UniversityContent {
id String @id @default(uuid())
universityId String
title String
content String
contentType ContentType
language Language @default(ENGLISH)
isPublished Boolean @default(false)
metadata Json?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
university University @relation(fields: [universityId], references: [id], onDelete: Cascade)
}
AcademicProgram
model AcademicProgram {
id String @id @default(uuid())
universityId String
title String
description String
level ProgramLevel
duration String
fees Json?
requirements Json?
isActive Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
university University @relation(fields: [universityId], references: [id], onDelete: Cascade)
}
Domain Management
DomainConfig
model DomainConfig {
id String @id @default(uuid())
universityId String
type DomainType
domain String
subdomain String?
sslStatus SSLStatus @default(PENDING)
sslExpiryDate DateTime?
dnsStatus DNSStatus @default(PENDING)
isActive Boolean @default(false)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
university University @relation(fields: [universityId], references: [id], onDelete: Cascade)
sslConfig SSLConfig?
dnsConfig DNSConfig?
analytics DomainAnalytics[]
}
Enums
enum UniversityStatus {
ACTIVE
INACTIVE
SUSPENDED
}
enum ContentType {
ABOUT
NEWS
EVENTS
RESEARCH
CAMPUS
}
enum Language {
ENGLISH
ARABIC
}
enum ProgramLevel {
UNDERGRADUATE
POSTGRADUATE
PHD
}
enum DomainType {
SUBDOMAIN
CUSTOM_DOMAIN
}
enum SSLStatus {
PENDING
ACTIVE
EXPIRED
ERROR
}
🏢 Multi-Tenant Architecture
Data Isolation
The platform ensures complete data isolation between universities:
// Data isolation middleware
export async function validateUniversityAccess(request: NextRequest): Promise<UniversityContext> {
const university = getUniversityFromHeaders(request);
if (!university) {
throw new Error('University context required');
}
// Verify university exists and is active
const dbUniversity = await prisma.university.findUnique({
where: { id: university.id, status: 'ACTIVE' },
});
if (!dbUniversity) {
throw new Error('University not found or inactive');
}
return university;
}
University Context
// University provider for React components
export function UniversityProvider({ children, initialUniversity }: UniversityProviderProps) {
const [university, setUniversity] = useState<University | null>(initialUniversity);
// Load university data based on cookies or headers
const loadUniversity = async () => {
const response = await fetch('/api/universities/current');
const data = await response.json();
setUniversity(data);
};
return (
<UniversityContext.Provider value={{ university, setUniversity }}>
{children}
</UniversityContext.Provider>
);
}
Dynamic Branding
// Dynamic branding component
export function DynamicBranding() {
const { university } = useUniversity();
if (!university) return null;
const { branding } = university;
return (
<div style={{
'--primary-color': branding.primaryColor,
'--secondary-color': branding.secondaryColor,
} as React.CSSProperties}>
<img src={branding.logo} alt={university.name} />
<h1>{university.name}</h1>
</div>
);
}
⚡ Performance Optimization
Caching Strategy
// Redis caching implementation
export class CacheManager {
async getUniversity(slug: string): Promise<any> {
const cacheKey = `university:${slug}`;
// Try cache first
const cached = await this.client.get(cacheKey);
if (cached) {
return JSON.parse(cached);
}
// Query database
const university = await prisma.university.findUnique({
where: { slug },
include: { assets: true, programs: true }
});
// Cache result
if (university) {
await this.client.setEx(cacheKey, 1800, JSON.stringify(university));
}
return university;
}
}
Query Optimization
// Optimized database queries
export class QueryOptimizer {
async getPrograms(universityId: string, options: any) {
const { page = 1, limit = 10, search } = options;
const skip = (page - 1) * limit;
const where = {
universityId,
isActive: true,
...(search && {
OR: [
{ title: { contains: search, mode: 'insensitive' } },
{ description: { contains: search, mode: 'insensitive' } },
],
}),
};
// Parallel queries for better performance
const [programs, total] = await Promise.all([
prisma.academicProgram.findMany({ where, skip, take: limit }),
prisma.academicProgram.count({ where }),
]);
return { programs, total, page, totalPages: Math.ceil(total / limit) };
}
}
CDN Integration
// Multi-provider CDN support
export class CDNManager {
async uploadAsset(file: File, path: string): Promise<CDNAsset> {
const config = await this.getCDNConfig();
let cdnUrl: string;
switch (config.provider) {
case 'AWS_S3':
cdnUrl = await this.uploadToS3(file, path, config);
break;
case 'CLOUDFLARE':
cdnUrl = await this.uploadToCloudflare(file, path, config);
break;
case 'CLOUDINARY':
cdnUrl = await this.uploadToCloudinary(file, path, config);
break;
default:
throw new Error('Unsupported CDN provider');
}
return { originalPath: path, cdnUrl, optimizedUrls: {} };
}
}
🚀 Deployment Guide
Environment Setup
-
Production Environment
# Set production environment variables export NODE_ENV=production export DATABASE_URL="postgresql://..." export REDIS_URL="redis://..." -
Database Migration
npx prisma migrate deploy npx prisma generate -
Build Application
npm run build
Docker Deployment
# Dockerfile
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
EXPOSE 3000
CMD ["npm", "start"]
# docker-compose.yml
version: '3.8'
services:
app:
build: .
ports:
- "3000:3000"
environment:
- DATABASE_URL=${DATABASE_URL}
- REDIS_URL=${REDIS_URL}
depends_on:
- postgres
- redis
postgres:
image: postgres:14
environment:
POSTGRES_DB: university_portal
POSTGRES_USER: ${DB_USER}
POSTGRES_PASSWORD: ${DB_PASSWORD}
redis:
image: redis:6-alpine
Vercel Deployment
-
Connect Repository
- Link your GitHub repository to Vercel
- Configure environment variables
-
Deploy
vercel --prod -
Custom Domain
- Add custom domain in Vercel dashboard
- Configure DNS records
🧪 Testing
Unit Tests
// Example unit test
import { describe, it, expect } from 'vitest';
import { CacheManager } from '../lib/cache';
describe('CacheManager', () => {
it('should cache and retrieve university data', async () => {
const cache = new CacheManager(config);
const university = { id: '1', name: 'Test University' };
await cache.set('university:test', university);
const retrieved = await cache.get('university:test');
expect(retrieved).toEqual(university);
});
});
Integration Tests
// Example integration test
import { describe, it, expect } from 'vitest';
import { createMocks } from 'node-mocks-http';
import handler from '../pages/api/universities';
describe('/api/universities', () => {
it('should return universities list', async () => {
const { req, res } = createMocks({
method: 'GET',
headers: { 'x-university-id': 'test-university' },
});
await handler(req, res);
expect(res._getStatusCode()).toBe(200);
expect(JSON.parse(res._getData())).toHaveProperty('data');
});
});
Load Testing
// Example load test
import { LoadTester } from '../lib/loadTesting';
const config = {
name: 'API Load Test',
duration: 300,
users: 50,
targetRPS: 10,
scenarios: [
{
name: 'Get Universities',
weight: 100,
requests: [
{
method: 'GET',
url: '/api/universities',
expectedStatus: 200,
},
],
},
],
};
const tester = new LoadTester(config);
const results = await tester.run();
🤝 Contributing
Development Workflow
-
Fork the Repository
git clone https://github.com/your-username/university-portal.git cd university-portal -
Create Feature Branch
git checkout -b feature/your-feature-name -
Make Changes
- Follow the coding standards
- Add tests for new features
- Update documentation
-
Submit Pull Request
git push origin feature/your-feature-name # Create PR on GitHub
Coding Standards
- TypeScript: Strict mode enabled
- ESLint: Airbnb configuration
- Prettier: Consistent formatting
- Conventional Commits: Standard commit messages
Code Review Process
-
Automated Checks
- Linting and formatting
- Type checking
- Unit tests
- Integration tests
-
Manual Review
- Code quality review
- Security review
- Performance review
-
Approval
- At least 2 approvals required
- All checks must pass
🔧 Troubleshooting
Common Issues
-
Database Connection
# Check database connection npx prisma db push # Reset database npx prisma migrate reset -
Redis Connection
# Check Redis connection redis-cli ping # Clear cache redis-cli flushall -
Build Issues
# Clear Next.js cache rm -rf .next npm run build
Debug Mode
# Enable debug logging
DEBUG=* npm run dev
# Database queries
DEBUG=prisma:query npm run dev
# Redis operations
DEBUG=redis npm run dev
Performance Monitoring
// Monitor query performance
const queryOptimizer = getQueryOptimizer();
const stats = queryOptimizer.getQueryStats();
console.log('Query Statistics:', stats);
// Monitor cache performance
const cache = getCacheInstance();
const cacheStats = await cache.getStats();
console.log('Cache Statistics:', cacheStats);
📞 Support
Getting Help
- Documentation: This developer guide
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- Email: dev-support@your-domain.com
Resources
- API Reference:
/api/docs - Database Schema:
prisma/schema.prisma - Component Library: Storybook documentation
- Performance Dashboard:
/admin/performance
Last Updated: January 2025
Version: 1.0
Platform: White-Label University Portal