Skip to content

StephenSouth13/MSC_REBUILD

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸŽ“ MSC Center - Life Long Learning Platform

MSC Center Logo

Next.js TypeScript Tailwind CSS Framer Motion PostgreSQL Golang Rust Sanity

πŸ“– Giα»›i thiệu

MSC Center lΓ  nền tαΊ£ng giΓ‘o dα»₯c trα»±c tuyαΊΏn hΓ ng Δ‘αΊ§u Việt Nam, chuyΓͺn cung cαΊ₯p cΓ‘c khΓ³a học chαΊ₯t lượng cao về cΓ΄ng nghệ, kinh doanh vΓ  phΓ‘t triển cΓ‘ nhΓ’n. Vα»›i slogan "Life Long Learning", chΓΊng tΓ΄i cam kαΊΏt Δ‘α»“ng hΓ nh cΓΉng học viΓͺn trong suα»‘t hΓ nh trΓ¬nh học tαΊ­p vΓ  phΓ‘t triển sα»± nghiệp.

🎯 Mα»₯c tiΓͺu dα»± Γ‘n

  • XΓ’y dα»±ng nền tαΊ£ng giΓ‘o dα»₯c trα»±c tuyαΊΏn hiện Δ‘αΊ‘i vΓ  thΓ’n thiện
  • KαΊΏt nα»‘i học viΓͺn vα»›i cΓ‘c mentor hΓ ng Δ‘αΊ§u trong ngΓ nh
  • Cung cαΊ₯p nα»™i dung chαΊ₯t lượng cao vΓ  cαΊ­p nhαΊ­t liΓͺn tα»₯c
  • TαΊ‘o cα»™ng Δ‘α»“ng học tαΊ­p tΓ­ch cα»±c vΓ  hα»— trợ lαΊ«n nhau

✨ TΓ­nh nΔƒng chΓ­nh

🎨 Frontend Features

  • 🌟 Modern UI/UX: ThiαΊΏt kαΊΏ hiện Δ‘αΊ‘i vα»›i Tailwind CSS vΓ  shadcn/ui
  • 🎭 Animations: Hiệu α»©ng mượt mΓ  vα»›i Framer Motion
  • πŸ“± Responsive Design: Tα»‘i Ζ°u cho mọi thiαΊΏt bα»‹ (Mobile, Tablet, Desktop)
  • πŸŒ™ Dark Mode: ChαΊΏ Δ‘α»™ tα»‘i/sΓ‘ng vα»›i theme switching
  • 🌐 Multilingual: Hα»— trợ Δ‘a ngΓ΄n ngα»― (TiαΊΏng Việt/English)
  • ⚑ Performance: Tα»‘i Ζ°u tα»‘c Δ‘α»™ tαΊ£i vΓ  SEO
  • β™Ώ Accessibility: TuΓ’n thα»§ chuαΊ©n WCAG 2.1

πŸ”§ Backend Features

  • πŸš€ Microservices: KiαΊΏn trΓΊc microservices vα»›i Golang vΓ  Rust
  • πŸ—„οΈ Database: PostgreSQL vα»›i Redis caching
  • πŸ” Authentication: JWT-based authentication system
  • πŸ“§ Email Service: Automated email notifications
  • πŸ“Š Analytics: Real-time analytics vΓ  reporting
  • πŸ” Search: Full-text search vα»›i Elasticsearch
  • πŸ“€ File Upload: Cloud storage integration

πŸ“ Content Management

  • πŸ“° Blog System: Hệ thα»‘ng blog vα»›i rich text editor
  • πŸ‘¨β€πŸ« Mentor Profiles: QuαΊ£n lΓ½ thΓ΄ng tin mentor chi tiαΊΏt
  • πŸŽ“ Course Management: QuαΊ£n lΓ½ khΓ³a học vΓ  bΓ i giαΊ£ng
  • πŸ’¬ Comments: Hệ thα»‘ng bΓ¬nh luαΊ­n vΓ  tΖ°Ζ‘ng tΓ‘c
  • πŸ“Š Analytics: Theo dΓ΅i engagement vΓ  performance

πŸ›  Tech Stack

```json { "frontend": { "framework": "Next.js 14 (App Router)", "language": "TypeScript 5.0", "styling": "Tailwind CSS 3.4", "ui_components": "shadcn/ui", "animations": "Framer Motion 11.0", "icons": "Lucide React", "forms": "React Hook Form + Zod", "state_management": "Zustand", "http_client": "Axios" }, "backend": { "api_gateway": "Golang (Gin Framework)", "analytics_service": "Rust (Warp Framework)", "database": "PostgreSQL 15", "cache": "Redis 7", "search": "Elasticsearch 8", "message_queue": "RabbitMQ", "file_storage": "AWS S3 / Cloudinary" }, "cms": { "headless_cms": "Sanity 3.0", "rich_text": "Portable Text", "media_management": "Sanity Assets", "preview": "Live Preview" }, "devops": { "containerization": "Docker & Docker Compose", "orchestration": "Kubernetes", "ci_cd": "GitHub Actions", "monitoring": "Prometheus + Grafana", "logging": "ELK Stack" } } ```

πŸš€ CΓ i Δ‘αΊ·t vΓ  ChαΊ‘y dα»± Γ‘n

πŸ“‹ YΓͺu cαΊ§u hệ thα»‘ng

  • Node.js: >= 18.0.0
  • npm/yarn/pnpm: Latest version
  • PostgreSQL: >= 15.0
  • Redis: >= 7.0
  • Docker: >= 24.0 (optional)
  • Go: >= 1.21 (for backend services)
  • Rust: >= 1.75 (for analytics service)

1️⃣ Clone Repository

```bash git clone https://github.com/msc-center/msc-website.git cd msc-website ```

2️⃣ CΓ i Δ‘αΊ·t Dependencies

```bash

Frontend dependencies

npm install

hoαΊ·c

yarn install

hoαΊ·c

pnpm install

Backend Go dependencies

cd backend-go go mod download cd ..

Backend Rust dependencies

cd backend-rust cargo build cd .. ```

3️⃣ CαΊ₯u hΓ¬nh Environment Variables

```bash

Copy environment template

cp .env.example .env.local

Chỉnh sα»­a file .env.local vα»›i thΓ΄ng tin cα»§a bαΊ‘n

```

4️⃣ Setup Database

```bash

Khởi tẑo PostgreSQL database

createdb msc_center

ChαΊ‘y migrations

npm run db:migrate

Seed data (optional)

npm run db:seed ```

5️⃣ ChαΊ‘y Development Server

```bash

Frontend (Next.js)

npm run dev

Backend Go service

cd backend-go && go run main.go

Backend Rust service

cd backend-rust && cargo run

HoαΊ·c chαΊ‘y tαΊ₯t cαΊ£ vα»›i Docker Compose

docker-compose up -dev ```

6️⃣ Truy cαΊ­p α»©ng dα»₯ng

πŸ“ CαΊ₯u trΓΊc dα»± Γ‘n

``` msc-center-website/ β”œβ”€β”€ πŸ“ app/ # Next.js App Router β”‚ β”œβ”€β”€ πŸ“ (auth)/ # Auth routes group β”‚ β”‚ β”œβ”€β”€ πŸ“ login/ β”‚ β”‚ └── πŸ“ register/ β”‚ β”œβ”€β”€ πŸ“ api/ # API routes β”‚ β”‚ β”œβ”€β”€ πŸ“ auth/ β”‚ β”‚ β”œβ”€β”€ πŸ“ blog/ β”‚ β”‚ └── πŸ“ contact/ β”‚ β”œβ”€β”€ πŸ“ chia-se/ # Blog pages β”‚ β”‚ β”œβ”€β”€ πŸ“ [slug]/ # Dynamic blog post β”‚ β”‚ └── πŸ“ category/ β”‚ β”œβ”€β”€ πŸ“ mentors/ # Mentor pages β”‚ β”‚ └── πŸ“ [id]/ # Dynamic mentor profile β”‚ β”œβ”€β”€ πŸ“ mscer/ # MSCer pages β”‚ β”‚ └── πŸ“ [id]/ # Dynamic MSCer profile β”‚ β”œβ”€β”€ πŸ“ profile/ # User profile dashboard β”‚ β”‚ β”œβ”€β”€ πŸ“ courses/ # Course management β”‚ β”‚ β”œβ”€β”€ πŸ“ settings/ # User settings β”‚ β”‚ └── πŸ“ feedback/ # Feedback system β”‚ β”œβ”€β”€ πŸ“„ globals.css # Global styles β”‚ β”œβ”€β”€ πŸ“„ layout.tsx # Root layout β”‚ β”œβ”€β”€ πŸ“„ page.tsx # Homepage β”‚ └── πŸ“„ not-found.tsx # 404 page β”œβ”€β”€ πŸ“ components/ # React components β”‚ β”œβ”€β”€ πŸ“ ui/ # shadcn/ui components β”‚ β”‚ β”œβ”€β”€ πŸ“„ button.tsx β”‚ β”‚ β”œβ”€β”€ πŸ“„ card.tsx β”‚ β”‚ β”œβ”€β”€ πŸ“„ input.tsx β”‚ β”‚ β”œβ”€β”€ πŸ“„ progress.tsx β”‚ β”‚ β”œβ”€β”€ πŸ“„ chart.tsx β”‚ β”‚ └── πŸ“„ ... β”‚ β”œβ”€β”€ πŸ“ sections/ # Page sections β”‚ β”‚ β”œβ”€β”€ πŸ“„ HeroVideo.tsx β”‚ β”‚ β”œβ”€β”€ πŸ“„ ProjectsSection.tsx β”‚ β”‚ β”œβ”€β”€ πŸ“„ MentorsSection.tsx β”‚ β”‚ └── πŸ“„ ... β”‚ β”œβ”€β”€ πŸ“ profile/ # Profile components β”‚ β”‚ β”œβ”€β”€ πŸ“„ ProfileDashboard.tsx β”‚ β”‚ β”œβ”€β”€ πŸ“„ CourseProgress.tsx β”‚ β”‚ β”œβ”€β”€ πŸ“„ SettingsPanel.tsx β”‚ β”‚ └── πŸ“„ FeedbackForm.tsx β”‚ β”œβ”€β”€ πŸ“„ Header.tsx # Site header β”‚ β”œβ”€β”€ πŸ“„ Footer.tsx # Site footer β”‚ β”œβ”€β”€ πŸ“„ FloatingButtons.tsx # Floating action buttons β”‚ └── πŸ“„ Chatbot.tsx # AI chatbot β”œβ”€β”€ πŸ“ data/ # Static data β”‚ β”œβ”€β”€ πŸ“„ mentors.ts # Mentors database β”‚ β”œβ”€β”€ πŸ“„ blog-posts.ts # Blog posts data β”‚ β”œβ”€β”€ πŸ“„ projects.ts # Projects data β”‚ └── πŸ“„ courses.ts # Courses data β”œβ”€β”€ πŸ“ lib/ # Utility libraries β”‚ β”œβ”€β”€ πŸ“„ utils.ts # Common utilities β”‚ β”œβ”€β”€ πŸ“„ sanity.ts # Sanity client β”‚ β”œβ”€β”€ πŸ“„ database.ts # Database connection β”‚ β”œβ”€β”€ πŸ“„ auth.ts # Authentication β”‚ └── πŸ“„ analytics.ts # Analytics tracking β”œβ”€β”€ πŸ“ hooks/ # Custom React hooks β”‚ β”œβ”€β”€ πŸ“„ use-theme.ts # Theme management β”‚ β”œβ”€β”€ πŸ“„ use-language.ts # Language switching β”‚ β”œβ”€β”€ πŸ“„ use-mobile.ts # Mobile detection β”‚ └── πŸ“„ use-user.ts # User data management β”œβ”€β”€ πŸ“ backend-go/ # Golang microservices β”‚ β”œβ”€β”€ πŸ“ cmd/ # Application entrypoints β”‚ β”œβ”€β”€ πŸ“ internal/ # Internal packages β”‚ β”‚ β”œβ”€β”€ πŸ“ api/ # API handlers β”‚ β”‚ β”œβ”€β”€ πŸ“ auth/ # Authentication β”‚ β”‚ β”œβ”€β”€ πŸ“ database/ # Database layer β”‚ β”‚ └── πŸ“ models/ # Data models β”‚ β”œβ”€β”€ πŸ“ pkg/ # Public packages β”‚ β”œβ”€β”€ πŸ“„ go.mod # Go modules β”‚ β”œβ”€β”€ πŸ“„ go.sum # Dependencies checksum β”‚ └── πŸ“„ main.go # Main application β”œβ”€β”€ πŸ“ backend-rust/ # Rust analytics service β”‚ β”œβ”€β”€ πŸ“ src/ # Source code β”‚ β”‚ β”œβ”€β”€ πŸ“ handlers/ # Request handlers β”‚ β”‚ β”œβ”€β”€ πŸ“ models/ # Data models β”‚ β”‚ β”œβ”€β”€ πŸ“ services/ # Business logic β”‚ β”‚ └── πŸ“„ main.rs # Main application β”‚ β”œβ”€β”€ πŸ“„ Cargo.toml # Rust dependencies β”‚ └── πŸ“„ Cargo.lock # Dependencies lock β”œβ”€β”€ πŸ“ sanity/ # Sanity CMS β”‚ β”œβ”€β”€ πŸ“ schemas/ # Content schemas β”‚ β”‚ β”œβ”€β”€ πŸ“„ post.ts # Blog post schema β”‚ β”‚ β”œβ”€β”€ πŸ“„ mentor.ts # Mentor schema β”‚ β”‚ β”œβ”€β”€ πŸ“„ course.ts # Course schema β”‚ β”‚ └── πŸ“„ author.ts # Author schema β”‚ β”œβ”€β”€ πŸ“„ sanity.config.ts # Sanity configuration β”‚ └── πŸ“„ sanity.cli.ts # CLI configuration β”œβ”€β”€ πŸ“ public/ # Static assets β”‚ β”œβ”€β”€ πŸ“ images/ # Images β”‚ β”œβ”€β”€ πŸ“ videos/ # Videos β”‚ β”œβ”€β”€ πŸ“ icons/ # Icons β”‚ └── πŸ“„ favicon.ico # Favicon β”œβ”€β”€ πŸ“ scripts/ # Utility scripts β”‚ β”œβ”€β”€ πŸ“„ setup.sh # Project setup β”‚ β”œβ”€β”€ πŸ“„ deploy.sh # Deployment script β”‚ └── πŸ“„ migrate.sh # Database migration β”œβ”€β”€ πŸ“ docs/ # Documentation β”‚ β”œβ”€β”€ πŸ“„ API.md # API documentation β”‚ β”œβ”€β”€ πŸ“„ DEPLOYMENT.md # Deployment guide β”‚ └── πŸ“„ CONTRIBUTING.md # Contributing guide β”œβ”€β”€ πŸ“„ package.json # Node.js dependencies β”œβ”€β”€ πŸ“„ tsconfig.json # TypeScript configuration β”œβ”€β”€ πŸ“„ tailwind.config.ts # Tailwind configuration β”œβ”€β”€ πŸ“„ next.config.mjs # Next.js configuration β”œβ”€β”€ πŸ“„ docker-compose.yml # Docker services β”œβ”€β”€ πŸ“„ Dockerfile # Docker image β”œβ”€β”€ πŸ“„ .env.example # Environment template β”œβ”€β”€ πŸ“„ .gitignore # Git ignore rules └── πŸ“„ README.md # This file ```

πŸ”Œ API Documentation

πŸ” Authentication Endpoints

```typescript // POST /api/auth/login interface LoginRequest { email: string; password: string; }

interface LoginResponse { user: User; token: string; refreshToken: string; }

// POST /api/auth/register interface RegisterRequest { email: string; password: string; fullName: string; phone?: string; }

// POST /api/auth/refresh interface RefreshRequest { refreshToken: string; }

// GET /api/auth/me interface UserResponse { id: string; email: string; fullName: string; avatar?: string; role: string; profile: UserProfile; } ```

πŸ‘€ Profile Endpoints

```typescript // GET /api/profile interface ProfileResponse { user: User; stats: { coursesCompleted: number; coursesInProgress: number; totalLearningHours: number; certificatesEarned: number; skillsAcquired: number; }; recentActivity: Activity[]; achievements: Achievement[]; }

// PUT /api/profile interface UpdateProfileRequest { fullName?: string; bio?: string; location?: string; website?: string; socialLinks?: SocialLinks; skills?: string[]; interests?: string[]; }

// POST /api/profile/avatar interface UploadAvatarRequest { file: File; } ```

πŸ“š Course Endpoints

```typescript // GET /api/courses/enrolled interface EnrolledCoursesResponse { courses: EnrolledCourse[]; pagination: Pagination; }

interface EnrolledCourse { id: string; title: string; instructor: string; progress: number; completedLessons: number; totalLessons: number; lastAccessed: string; certificate?: Certificate; }

// GET /api/courses/[id]/progress interface CourseProgressResponse { courseId: string; progress: number; completedLessons: Lesson[]; currentLesson?: Lesson; timeSpent: number; quiz_scores: QuizScore[]; }

// POST /api/courses/[id]/complete-lesson interface CompleteLessonRequest { lessonId: string; timeSpent: number; notes?: string; } ```

πŸ’¬ Feedback Endpoints

```typescript // POST /api/feedback interface FeedbackRequest { type: 'bug' | 'feature' | 'improvement' | 'general'; category: string; title: string; description: string; priority: 'low' | 'medium' | 'high' | 'urgent'; attachments?: File[]; }

// GET /api/feedback/my-feedback interface MyFeedbackResponse { feedback: Feedback[]; pagination: Pagination; }

interface Feedback { id: string; type: string; title: string; status: 'open' | 'in-progress' | 'resolved' | 'closed'; createdAt: string; updatedAt: string; responses: FeedbackResponse[]; } ```

πŸ—„οΈ Database Schema

PostgreSQL Tables

Users Table

```sql CREATE TABLE users ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), email VARCHAR(255) UNIQUE NOT NULL, password_hash VARCHAR(255) NOT NULL, full_name VARCHAR(255) NOT NULL, avatar_url TEXT, role VARCHAR(50) DEFAULT 'user', email_verified BOOLEAN DEFAULT false, phone VARCHAR(20), bio TEXT, location VARCHAR(255), website VARCHAR(255), date_of_birth DATE, gender VARCHAR(10), occupation VARCHAR(255), company VARCHAR(255), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, last_login TIMESTAMP, is_active BOOLEAN DEFAULT true ); ```

User Profiles Table

```sql CREATE TABLE user_profiles ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID REFERENCES users(id) ON DELETE CASCADE, skills TEXT[], interests TEXT[], learning_goals TEXT[], experience_level VARCHAR(50), preferred_language VARCHAR(10) DEFAULT 'vi', timezone VARCHAR(50), social_links JSONB DEFAULT '{}', privacy_settings JSONB DEFAULT '{}', notification_preferences JSONB DEFAULT '{}', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); ```

Courses Table

```sql CREATE TABLE courses ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), title VARCHAR(500) NOT NULL, slug VARCHAR(255) UNIQUE NOT NULL, description TEXT, instructor_id UUID REFERENCES users(id), category VARCHAR(100), level VARCHAR(50), duration_hours INTEGER, price DECIMAL(10,2), thumbnail_url TEXT, trailer_url TEXT, is_published BOOLEAN DEFAULT false, enrollment_count INTEGER DEFAULT 0, rating DECIMAL(3,2) DEFAULT 0, review_count INTEGER DEFAULT 0, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); ```

Course Enrollments Table

```sql CREATE TABLE course_enrollments ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID REFERENCES users(id) ON DELETE CASCADE, course_id UUID REFERENCES courses(id) ON DELETE CASCADE, enrolled_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, completed_at TIMESTAMP, progress DECIMAL(5,2) DEFAULT 0, last_accessed TIMESTAMP, time_spent INTEGER DEFAULT 0, -- in minutes certificate_issued BOOLEAN DEFAULT false, certificate_url TEXT, UNIQUE(user_id, course_id) ); ```

Lessons Table

```sql CREATE TABLE lessons ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), course_id UUID REFERENCES courses(id) ON DELETE CASCADE, title VARCHAR(500) NOT NULL, slug VARCHAR(255) NOT NULL, content TEXT, video_url TEXT, duration_minutes INTEGER, order_index INTEGER, is_preview BOOLEAN DEFAULT false, resources JSONB DEFAULT '[]', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); ```

Lesson Progress Table

```sql CREATE TABLE lesson_progress ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID REFERENCES users(id) ON DELETE CASCADE, lesson_id UUID REFERENCES lessons(id) ON DELETE CASCADE, completed BOOLEAN DEFAULT false, completed_at TIMESTAMP, time_spent INTEGER DEFAULT 0, -- in minutes notes TEXT, last_position INTEGER DEFAULT 0, -- video position in seconds UNIQUE(user_id, lesson_id) ); ```

User Achievements Table

```sql CREATE TABLE user_achievements ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID REFERENCES users(id) ON DELETE CASCADE, achievement_type VARCHAR(100) NOT NULL, achievement_name VARCHAR(255) NOT NULL, description TEXT, icon_url TEXT, earned_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, metadata JSONB DEFAULT '{}' ); ```

Feedback Table

```sql CREATE TABLE feedback ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID REFERENCES users(id) ON DELETE CASCADE, type VARCHAR(50) NOT NULL, category VARCHAR(100), title VARCHAR(500) NOT NULL, description TEXT NOT NULL, priority VARCHAR(20) DEFAULT 'medium', status VARCHAR(50) DEFAULT 'open', assigned_to UUID REFERENCES users(id), attachments TEXT[], metadata JSONB DEFAULT '{}', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); ```

Feedback Responses Table

```sql CREATE TABLE feedback_responses ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), feedback_id UUID REFERENCES feedback(id) ON DELETE CASCADE, responder_id UUID REFERENCES users(id), message TEXT NOT NULL, is_internal BOOLEAN DEFAULT false, attachments TEXT[], created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); ```

Indexes for Performance

```sql -- Users indexes CREATE INDEX idx_users_email ON users(email); CREATE INDEX idx_users_role ON users(role); CREATE INDEX idx_users_is_active ON users(is_active);

-- Course enrollments indexes CREATE INDEX idx_enrollments_user_id ON course_enrollments(user_id); CREATE INDEX idx_enrollments_course_id ON course_enrollments(course_id); CREATE INDEX idx_enrollments_progress ON course_enrollments(progress);

-- Lesson progress indexes CREATE INDEX idx_lesson_progress_user_id ON lesson_progress(user_id); CREATE INDEX idx_lesson_progress_lesson_id ON lesson_progress(lesson_id); CREATE INDEX idx_lesson_progress_completed ON lesson_progress(completed);

-- Feedback indexes CREATE INDEX idx_feedback_user_id ON feedback(user_id); CREATE INDEX idx_feedback_status ON feedback(status); CREATE INDEX idx_feedback_type ON feedback(type); CREATE INDEX idx_feedback_created_at ON feedback(created_at DESC);

-- Achievements indexes CREATE INDEX idx_achievements_user_id ON user_achievements(user_id); CREATE INDEX idx_achievements_type ON user_achievements(achievement_type); ```

πŸ“ CMS Sanity Configuration

1. CΓ i Δ‘αΊ·t Sanity

```bash

CΓ i Δ‘αΊ·t Sanity CLI

npm install -g @sanity/cli

TαΊ‘o project Sanity

sanity init

Chọn cαΊ₯u hΓ¬nh:

- Create new project

- Project name: MSC Center CMS

- Dataset: production

- Template: Blog (schema)

```

2. Sanity Schema Configuration

Blog Post Schema

```typescript // sanity/schemas/post.ts import { defineField, defineType } from 'sanity'

export default defineType({ name: 'post', title: 'BΓ i viαΊΏt', type: 'document', fields: [ defineField({ name: 'title', title: 'TiΓͺu đề', type: 'string', validation: Rule => Rule.required().max(100) }), defineField({ name: 'slug', title: 'Slug', type: 'slug', options: { source: 'title', maxLength: 96, }, validation: Rule => Rule.required() }), defineField({ name: 'author', title: 'TΓ‘c giαΊ£', type: 'reference', to: { type: 'author' }, validation: Rule => Rule.required() }), defineField({ name: 'mainImage', title: 'αΊ’nh Δ‘αΊ‘i diện', type: 'image', options: { hotspot: true, }, fields: [ { name: 'alt', type: 'string', title: 'Alt text', } ] }), defineField({ name: 'categories', title: 'Danh mα»₯c', type: 'array', of: [{ type: 'reference', to: { type: 'category' } }], }), defineField({ name: 'publishedAt', title: 'NgΓ y xuαΊ₯t bαΊ£n', type: 'datetime', validation: Rule => Rule.required() }), defineField({ name: 'excerpt', title: 'TΓ³m tαΊ―t', type: 'text', rows: 4, validation: Rule => Rule.max(200) }), defineField({ name: 'body', title: 'Nα»™i dung', type: 'blockContent', validation: Rule => Rule.required() }), defineField({ name: 'tags', title: 'Tags', type: 'array', of: [{ type: 'string' }], options: { layout: 'tags' } }), defineField({ name: 'featured', title: 'BΓ i viαΊΏt nα»•i bαΊ­t', type: 'boolean', initialValue: false }), defineField({ name: 'readingTime', title: 'Thời gian đọc (phΓΊt)', type: 'number', validation: Rule => Rule.min(1).max(60) }), ], preview: { select: { title: 'title', author: 'author.name', media: 'mainImage', }, prepare(selection) { const { author } = selection return { ...selection, subtitle: author && by ${author} } }, }, }) ```

Mentor Schema

```typescript // sanity/schemas/mentor.ts import { defineField, defineType } from 'sanity'

export default defineType({ name: 'mentor', title: 'Mentor', type: 'document', fields: [ defineField({ name: 'name', title: 'Họ vΓ  tΓͺn', type: 'string', validation: Rule => Rule.required() }), defineField({ name: 'slug', title: 'Slug', type: 'slug', options: { source: 'name', maxLength: 96, }, validation: Rule => Rule.required() }), defineField({ name: 'title', title: 'Chα»©c danh', type: 'string', validation: Rule => Rule.required() }), defineField({ name: 'company', title: 'CΓ΄ng ty', type: 'string', }), defineField({ name: 'avatar', title: 'αΊ’nh Δ‘αΊ‘i diện', type: 'image', options: { hotspot: true, }, validation: Rule => Rule.required() }), defineField({ name: 'bio', title: 'Tiểu sα»­', type: 'blockContent', }), defineField({ name: 'experience', title: 'Kinh nghiệm lΓ m việc', type: 'array', of: [ { type: 'object', fields: [ { name: 'company', type: 'string', title: 'CΓ΄ng ty' }, { name: 'position', type: 'string', title: 'Vα»‹ trΓ­' }, { name: 'duration', type: 'string', title: 'Thời gian' }, { name: 'description', type: 'text', title: 'MΓ΄ tαΊ£ cΓ΄ng việc' }, ] } ] }), defineField({ name: 'education', title: 'Học vαΊ₯n', type: 'array', of: [ { type: 'object', fields: [ { name: 'school', type: 'string', title: 'Trường' }, { name: 'degree', type: 'string', title: 'BαΊ±ng cαΊ₯p' }, { name: 'field', type: 'string', title: 'ChuyΓͺn ngΓ nh' }, { name: 'year', type: 'string', title: 'NΔƒm tα»‘t nghiệp' }, ] } ] }), defineField({ name: 'skills', title: 'Kα»Ή nΔƒng', type: 'array', of: [{ type: 'string' }], options: { layout: 'tags' } }), defineField({ name: 'achievements', title: 'ThΓ nh tα»±u', type: 'array', of: [{ type: 'string' }], }), defineField({ name: 'socialLinks', title: 'LiΓͺn kαΊΏt xΓ£ hα»™i', type: 'object', fields: [ { name: 'linkedin', type: 'url', title: 'LinkedIn' }, { name: 'github', type: 'url', title: 'GitHub' }, { name: 'website', type: 'url', title: 'Website' }, { name: 'twitter', type: 'url', title: 'Twitter' }, ] }), defineField({ name: 'specialties', title: 'ChuyΓͺn mΓ΄n', type: 'array', of: [{ type: 'string' }], options: { layout: 'tags' } }), defineField({ name: 'hourlyRate', title: 'GiΓ‘ theo giờ (USD)', type: 'number', }), defineField({ name: 'isActive', title: 'Đang hoαΊ‘t Δ‘α»™ng', type: 'boolean', initialValue: true }), ], preview: { select: { title: 'name', subtitle: 'title', media: 'avatar', }, }, }) ```

Course Schema

```typescript // sanity/schemas/course.ts import { defineField, defineType } from 'sanity'

export default defineType({ name: 'course', title: 'KhΓ³a học', type: 'document', fields: [ defineField({ name: 'title', title: 'TiΓͺu đề khΓ³a học', type: 'string', validation: Rule => Rule.required().max(200) }), defineField({ name: 'slug', title: 'Slug', type: 'slug', options: { source: 'title', maxLength: 96, }, validation: Rule => Rule.required() }), defineField({ name: 'description', title: 'MΓ΄ tαΊ£', type: 'text', rows: 4, validation: Rule => Rule.required().max(500) }), defineField({ name: 'instructor', title: 'GiαΊ£ng viΓͺn', type: 'reference', to: { type: 'mentor' }, validation: Rule => Rule.required() }), defineField({ name: 'thumbnail', title: 'αΊ’nh thumbnail', type: 'image', options: { hotspot: true, }, validation: Rule => Rule.required() }), defineField({ name: 'trailer', title: 'Video giα»›i thiệu', type: 'url', }), defineField({ name: 'category', title: 'Danh mα»₯c', type: 'reference', to: { type: 'category' }, validation: Rule => Rule.required() }), defineField({ name: 'level', title: 'CαΊ₯p Δ‘α»™', type: 'string', options: { list: [ { title: 'CΖ‘ bαΊ£n', value: 'beginner' }, { title: 'Trung cαΊ₯p', value: 'intermediate' }, { title: 'NΓ’ng cao', value: 'advanced' }, ] }, validation: Rule => Rule.required() }), defineField({ name: 'duration', title: 'Thời lượng (giờ)', type: 'number', validation: Rule => Rule.required().min(1).max(200) }), defineField({ name: 'price', title: 'GiΓ‘ (VND)', type: 'number', validation: Rule => Rule.required().min(0) }), defineField({ name: 'curriculum', title: 'ChΖ°Ζ‘ng trΓ¬nh học', type: 'array', of: [ { type: 'object', fields: [ { name: 'title', type: 'string', title: 'TiΓͺu đề bΓ i học' }, { name: 'duration', type: 'number', title: 'Thời lượng (phΓΊt)' }, { name: 'isPreview', type: 'boolean', title: 'Xem trΖ°α»›c miα»…n phΓ­' }, { name: 'videoUrl', type: 'url', title: 'Link video' }, { name: 'resources', type: 'array', of: [{ type: 'url' }], title: 'TΓ i liệu' }, ] } ] }), defineField({ name: 'skills', title: 'Kα»Ή nΔƒng học được', type: 'array', of: [{ type: 'string' }], options: { layout: 'tags' } }), defineField({ name: 'requirements', title: 'YΓͺu cαΊ§u', type: 'array', of: [{ type: 'string' }], }), defineField({ name: 'isPublished', title: 'Đã xuαΊ₯t bαΊ£n', type: 'boolean', initialValue: false }), defineField({ name: 'isFeatured', title: 'KhΓ³a học nα»•i bαΊ­t', type: 'boolean', initialValue: false }), ], preview: { select: { title: 'title', instructor: 'instructor.name', media: 'thumbnail', }, prepare(selection) { const { instructor } = selection return { ...selection, subtitle: instructor && by ${instructor} } }, }, }) ```

3. Sanity Client Setup

```typescript // lib/sanity.ts import { createClient } from '@sanity/client' import imageUrlBuilder from '@sanity/image-url'

export const client = createClient({ projectId: process.env.NEXT_PUBLIC_SANITY_PROJECT_ID!, dataset: process.env.NEXT_PUBLIC_SANITY_DATASET!, apiVersion: '2024-01-01', useCdn: process.env.NODE_ENV === 'production', token: process.env.SANITY_API_TOKEN, })

const builder = imageUrlBuilder(client)

export const urlFor = (source: any) => builder.image(source)

// GROQ Queries export const coursesQuery = *[_type == "course" && isPublished == true] | order(_createdAt desc) { _id, title, slug, description, instructor->{name, avatar, title}, thumbnail, category->{title, slug}, level, duration, price, skills, requirements, isFeatured }

export const courseBySlugQuery = *[_type == "course" && slug.current == $slug][0] { _id, title, slug, description, instructor->{name, avatar, title, bio}, thumbnail, trailer, category->{title, slug}, level, duration, price, curriculum, skills, requirements }

export const featuredCoursesQuery = *[_type == "course" && isFeatured == true && isPublished == true] | order(_createdAt desc) [0...6] { _id, title, slug, description, instructor->{name, avatar}, thumbnail, level, duration, price } ```

🐳 Docker Configuration

Dockerfile

```dockerfile

Dockerfile

FROM node:18-alpine AS base

Install dependencies only when needed

FROM base AS deps RUN apk add --no-cache libc6-compat WORKDIR /app

Install dependencies based on the preferred package manager

COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./ RUN
if [ -f yarn.lock ]; then yarn --frozen-lockfile;
elif [ -f package-lock.json ]; then npm ci;
elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && pnpm i --frozen-lockfile;
else echo "Lockfile not found." && exit 1;
fi

Rebuild the source code only when needed

FROM base AS builder WORKDIR /app COPY --from=deps /app/node_modules ./node_modules COPY . .

Environment variables must be present at build time

ARG NEXT_PUBLIC_SANITY_PROJECT_ID ARG NEXT_PUBLIC_SANITY_DATASET ARG DATABASE_URL ARG NEXTAUTH_SECRET ARG NEXTAUTH_URL

ENV NEXT_PUBLIC_SANITY_PROJECT_ID=$NEXT_PUBLIC_SANITY_PROJECT_ID ENV NEXT_PUBLIC_SANITY_DATASET=$NEXT_PUBLIC_SANITY_DATASET ENV DATABASE_URL=$DATABASE_URL ENV NEXTAUTH_SECRET=$NEXTAUTH_SECRET ENV NEXTAUTH_URL=$NEXTAUTH_URL

RUN yarn build

Production image, copy all the files and run next

FROM base AS runner WORKDIR /app

ENV NODE_ENV production ENV NEXT_TELEMETRY_DISABLED 1

RUN addgroup --system --gid 1001 nodejs RUN adduser --system --uid 1001 nextjs

COPY --from=builder /app/public ./public

Set the correct permission for prerender cache

RUN mkdir .next RUN chown nextjs:nodejs .next

Automatically leverage output traces to reduce image size

COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static

USER nextjs

EXPOSE 3000

ENV PORT 3000 ENV HOSTNAME "0.0.0.0"

CMD ["node", "server.js"] ```

Docker Compose

```yaml

docker-compose.yml

version: '3.8'

services:

Frontend Next.js Application

frontend: build: context: . dockerfile: Dockerfile args: NEXT_PUBLIC_SANITY_PROJECT_ID: ${NEXT_PUBLIC_SANITY_PROJECT_ID} NEXT_PUBLIC_SANITY_DATASET: ${NEXT_PUBLIC_SANITY_DATASET} DATABASE_URL: ${DATABASE_URL} NEXTAUTH_SECRET: ${NEXTAUTH_SECRET} NEXTAUTH_URL: ${NEXTAUTH_URL} ports: - "3000:3000" environment: - NODE_ENV=production - DATABASE_URL=postgresql://postgres:password@db:5432/msc_center - REDIS_URL=redis://redis:6379 - NEXT_PUBLIC_SANITY_PROJECT_ID=${NEXT_PUBLIC_SANITY_PROJECT_ID} - NEXT_PUBLIC_SANITY_DATASET=${NEXT_PUBLIC_SANITY_DATASET} depends_on: - db - redis networks: - msc-network

PostgreSQL Database

db: image: postgres:15-alpine environment: POSTGRES_DB: msc_center POSTGRES_USER: postgres POSTGRES_PASSWORD: password POSTGRES_INITDB_ARGS: "--encoding=UTF-8" volumes: - postgres_data:/var/lib/postgresql/data - ./scripts/init.sql:/docker-entrypoint-initdb.d/init.sql ports: - "5432:5432" networks: - msc-network

Redis Cache

redis: image: redis:7-alpine command: redis-server --appendonly yes volumes: - redis_data:/data ports: - "6379:6379" networks: - msc-network

Golang Backend API

backend-go: build: context: ./backend-go dockerfile: Dockerfile ports: - "8080:8080" environment: - DATABASE_URL=postgresql://postgres:password@db:5432/msc_center - REDIS_URL=redis://redis:6379 - JWT_SECRET=${JWT_SECRET} - SMTP_HOST=${SMTP_HOST} - SMTP_PORT=${SMTP_PORT} - SMTP_USER=${SMTP_USER} - SMTP_PASS=${SMTP_PASS} depends_on: - db - redis networks: - msc-network

Rust Analytics Service

backend-rust: build: context: ./backend-rust dockerfile: Dockerfile ports: - "3001:3001" environment: - DATABASE_URL=postgresql://postgres:password@db:5432/msc_center - REDIS_URL=redis://redis:6379 depends_on: - db - redis networks: - msc-network

Nginx Reverse Proxy

nginx: image: nginx:alpine ports: - "80:80" - "443:443" volumes: - ./nginx/nginx.conf:/etc/nginx/nginx.conf - ./nginx/ssl:/etc/nginx/ssl depends_on: - frontend - backend-go - backend-rust networks: - msc-network

volumes: postgres_data: redis_data:

networks: msc-network: driver: bridge ```

πŸš€ Deployment

Environment Variables

```bash

.env.example

Database

DATABASE_URL="postgresql://username:password@localhost:5432/msc_center" REDIS_URL="redis://localhost:6379"

NextAuth.js

NEXTAUTH_SECRET="your-secret-key-here" NEXTAUTH_URL="http://localhost:3000"

Sanity CMS

NEXT_PUBLIC_SANITY_PROJECT_ID="your-project-id" NEXT_PUBLIC_SANITY_DATASET="production" SANITY_API_TOKEN="your-api-token"

Email Service

SMTP_HOST="smtp.gmail.com" SMTP_PORT="587" SMTP_USER="[email protected]" SMTP_PASS="your-app-password"

Cloud Storage

CLOUDINARY_CLOUD_NAME="your-cloud-name" CLOUDINARY_API_KEY="your-api-key" CLOUDINARY_API_SECRET="your-api-secret"

Analytics

GOOGLE_ANALYTICS_ID="GA_MEASUREMENT_ID" FACEBOOK_PIXEL_ID="your-pixel-id"

Social Auth

GOOGLE_CLIENT_ID="your-google-client-id" GOOGLE_CLIENT_SECRET="your-google-client-secret" FACEBOOK_CLIENT_ID="your-facebook-client-id" FACEBOOK_CLIENT_SECRET="your-facebook-client-secret"

API Keys

JWT_SECRET="your-jwt-secret" ENCRYPTION_KEY="your-encryption-key"

External APIs

OPENAI_API_KEY="your-openai-api-key" STRIPE_SECRET_KEY="your-stripe-secret-key" STRIPE_PUBLISHABLE_KEY="your-stripe-publishable-key" ```

πŸ§ͺ Testing

Unit Tests

```bash

Run all tests

npm run test

Run tests in watch mode

npm run test:watch

Run tests with coverage

npm run test:coverage

Run specific test file

npm run test -- components/Header.test.tsx ```

E2E Tests

```bash

Run Playwright tests

npm run test:e2e

Run tests in headed mode

npm run test:e2e:headed

Run specific test

npm run test:e2e -- tests/auth.spec.ts ```

πŸ“Š Monitoring & Analytics

Application Monitoring

  • Error Tracking: Sentry integration
  • Performance: New Relic / DataDog
  • Uptime: Pingdom / UptimeRobot
  • Logs: ELK Stack (Elasticsearch, Logstash, Kibana)

Business Analytics

  • Web Analytics: Google Analytics 4
  • User Behavior: Hotjar / FullStory
  • A/B Testing: Optimizely / VWO
  • Conversion Tracking: Facebook Pixel

πŸ”’ Security

Security Measures

  • HTTPS: SSL/TLS encryption
  • Authentication: JWT with refresh tokens
  • Authorization: Role-based access control (RBAC)
  • Input Validation: Zod schema validation
  • SQL Injection: Parameterized queries
  • XSS Protection: Content Security Policy (CSP)
  • CSRF Protection: SameSite cookies
  • Rate Limiting: Express rate limit
  • Data Encryption: bcrypt for passwords

🀝 Contributing

Development Workflow

  1. Fork the repository
  2. Create a feature branch: git checkout -b feature/amazing-feature
  3. Commit your changes: git commit -m 'Add amazing feature'
  4. Push to the branch: git push origin feature/amazing-feature
  5. Open a Pull Request

Code Standards

  • TypeScript: Strict mode enabled
  • ESLint: Airbnb configuration with custom rules
  • Prettier: Code formatting with custom config
  • Husky: Pre-commit hooks for linting and testing
  • Conventional Commits: Standardized commit messages
  • Semantic Versioning: Version management

πŸ“ž Support & Contact

Getting Help

  • Documentation: Check this README and docs folder
  • Issues: Create a GitHub issue for bugs
  • Discussions: Use GitHub Discussions for questions
  • Email: [email protected]

MSC Center Contact

πŸ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.


MSC Center

πŸš€ Life Long Learning πŸš€

Made with ❀️ by MSC Center Team

Website β€’ Email β€’ Facebook β€’ LinkedIn

About

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 3

  •  
  •  
  •