CloudMentor is a classroom-ready AI learning assistant project for Web Development, DevOps, Cloud, and AI engineering students.
Students can:
- Paste study notes directly into the app
- Upload text-based files
- Store uploaded files in S3
- Generate summaries, interactive quizzes, flippable flashcards, and exact day-by-day study plans
- Save output and progress history in DynamoDB
- Run the app locally, on an EC2 machine, or as a real serverless AWS backend
- Deploy updates automatically with GitHub Actions
- React + Vite frontend development
- Modern dark/glass UI design
- API integration from React
- AWS Lambda backend design
- AWS SAM infrastructure-as-code
- API Gateway HTTP API
- S3 file upload using pre-signed URLs
- DynamoDB persistence
- OpenAI API integration from a backend
- EC2 hosting with Nginx
- GitHub Actions CI/CD
- CloudWatch/systemd logs and troubleshooting
Browser
↓
React frontend: http://localhost:5173
↓
SAM Local API: http://localhost:3000
↓
Lambda code running inside Docker
↓
OpenAI API
Local mode can use either:
STORAGE_MODE=local → uploads are saved in local temporary storage
STORAGE_MODE=s3 → uploads are saved in real AWS S3
Browser
↓
http://EC2_PUBLIC_IP
↓
Nginx serves React build files
↓
Nginx proxies /api/* to SAM Local API on 127.0.0.1:3000
↓
Lambda code runs through SAM Local on the EC2 machine
↓
OpenAI API + S3 + DynamoDB
In EC2 mode, the frontend uses:
VITE_API_BASE_URL=http://EC2_PUBLIC_IP/apiThe browser does not call localhost. It calls the EC2 public IP.
React frontend
↓
API Gateway HTTP API
↓
AWS Lambda
↓
OpenAI API
↓
S3 + DynamoDB
This is the true production-style serverless deployment. AWS SAM packages and pushes the Lambda code to AWS.
cloudmentor-serverless/
├── .github/
│ └── workflows/
│ ├── deploy-ec2.yml
│ └── deploy-serverless-backend.yml
├── backend/
│ ├── template.yaml
│ ├── package.json
│ ├── env.local.example.json
│ ├── env.ec2.example.json
│ ├── env.production.example.json
│ ├── events/
│ │ ├── summarize.json
│ │ └── upload-url.json
│ └── src/
│ ├── app.mjs
│ └── prompts.mjs
├── frontend/
│ ├── index.html
│ ├── package.json
│ ├── vite.config.js
│ ├── .env.example
│ ├── .env.ec2.example
│ ├── public/
│ │ └── cloudmentor-reference.png
│ └── src/
│ ├── App.jsx
│ ├── api.js
│ ├── main.jsx
│ └── styles.css
├── scripts/
│ ├── create-ec2-aws-resources.sh
│ ├── ec2-bootstrap.sh
│ └── ec2-deploy.sh
├── docs/
│ └── teaching-plan.md
├── .gitignore
└── README.md
GET /health
GET /history
POST /upload-url
PUT /local-upload/{key} # local storage mode only
POST /process-file
POST /summarize
POST /quiz
POST /flashcards
POST /study-plan
POST /save-progress
CloudMentor now returns structured data for the learning tools, not only plain Markdown.
Quiz → interactive MCQ cards with correct/wrong feedback and score
Flashcards → real flippable cards with hints, previous/next navigation
Study plan → exactly the number of days requested by the student, up to 30 days
Summary → readable Markdown summary
In mock mode, the backend generates structured demo data without OpenAI. In OpenAI mode, the prompt asks the model to return strict JSON for quiz, flashcards, and study plan so the React UI can render interactive components.
- Go to the OpenAI API platform.
- Create or select a project.
- Create an API key.
- Copy the key immediately and keep it safe.
- Do not place the OpenAI API key in the React frontend
.envfile.
Use the key only in one of these places:
backend/env.json # local development only, do not commit
backend/env.ec2.json # EC2 deployment only, do not commit
GitHub Actions secret # CI/CD
SAM parameter OpenAiApiKey # AWS Lambda deployment
Recommended model for this classroom version:
gpt-4.1-mini
You can replace it with another text-capable OpenAI model if required.
Install these on your laptop:
- Node.js 20 or newer
- npm
- Docker Desktop
- AWS CLI v2
- AWS SAM CLI
- Git
- OpenAI API key
- AWS account, only required if you want S3/DynamoDB or real Lambda deployment
Check versions:
node -v
npm -v
docker --version
aws --version
sam --version
git --versionConfigure AWS CLI if you plan to use S3/DynamoDB from local:
aws configureFor classroom testing without S3, AWS CLI configuration is not required.
This is the easiest first run for students.
cd cloudmentor-serverless/backend
npm install
cp env.local.example.json env.jsonEdit env.json. For local laptop mode, use this:
{
"CloudMentorFunction": {
"AI_MODE": "mock",
"OPENAI_API_KEY": "",
"OPENAI_MODEL": "gpt-4.1-mini",
"TABLE_NAME": "",
"MATERIALS_BUCKET": "",
"CORS_ORIGIN": "*",
"STORAGE_MODE": "local",
"LOCAL_DEV": "true"
}
}The backend folder also includes production-style examples:
backend/env.local.example.json # local laptop, mock AI, local file storage
backend/env.ec2.example.json # EC2 mode, S3/DynamoDB, usually mock first
backend/env.production.example.json # production-like values for OpenAI + S3 + DynamoDB
For real OpenAI mode, use:
"AI_MODE": "openai",
"OPENAI_API_KEY": "sk-proj-your-real-key"For the first local classroom run, use mock mode so students do not need API credit:
"AI_MODE": "mock",
"OPENAI_API_KEY": ""In mock mode, CloudMentor returns realistic demo summaries, quizzes, flashcards, and study plans without calling OpenAI.
When you are ready to use the real OpenAI API, change the backend env.json to:
"AI_MODE": "openai",
"OPENAI_API_KEY": "sk-proj-your-real-key"After changing backend/env.json, stop and restart SAM local:
sam local start-api --env-vars env.jsonStart the local Lambda API:
sam build
sam local start-api --env-vars env.jsonBackend URL:
http://localhost:3000
Test:
curl http://localhost:3000/healthcd cloudmentor-serverless/frontend
npm install
cp .env.example .envEdit .env. The file includes local, EC2, and API Gateway examples. For local laptop mode, keep only this active line:
VITE_API_BASE_URL=http://localhost:3000The same .env.example also includes commented production examples:
# EC2 mode
# VITE_API_BASE_URL=http://YOUR_EC2_PUBLIC_IP/api
# Full AWS serverless mode
# VITE_API_BASE_URL=https://YOUR_API_ID.execute-api.YOUR_REGION.amazonaws.comRun:
npm run devOpen:
http://localhost:5173
- Paste notes into the textarea.
- Click Generate Quiz and answer the MCQs. The UI will show correct and wrong answers immediately.
- Click Create Flashcards and flip through the cards using Previous/Next.
- Click Build Study Plan, set
Study daysto any number from 1 to 30, and verify the output contains exactly that many days. - Upload a
.txt,.md,.csv,.json,.yaml, or.logfile and confirm the extracted text loads into the notes area.
Use this when you want your laptop to run the API locally while uploaded files go to a real S3 bucket.
From the project root:
chmod +x scripts/create-ec2-aws-resources.sh
AWS_REGION=ap-southeast-1 \
BUCKET_NAME=cloudmentor-materials-yourname-dev \
TABLE_NAME=cloudmentor-history-dev \
CORS_ORIGIN='*' \
./scripts/create-ec2-aws-resources.shBucket names must be globally unique across AWS.
cd backend
cp env.ec2.example.json env.jsonEdit env.json:
{
"CloudMentorFunction": {
"AI_MODE": "mock",
"OPENAI_API_KEY": "",
"OPENAI_MODEL": "gpt-4.1-mini",
"TABLE_NAME": "cloudmentor-history-dev",
"MATERIALS_BUCKET": "cloudmentor-materials-yourname-dev",
"CORS_ORIGIN": "*",
"STORAGE_MODE": "s3",
"LOCAL_DEV": "false",
"AWS_REGION": "ap-southeast-1"
}
}Start backend:
npm install
sam build
sam local start-api --env-vars env.jsonFrontend remains:
VITE_API_BASE_URL=http://localhost:3000Now file uploads from your local browser are stored in S3.
Recommended EC2 setup:
AMI: Ubuntu Server 24.04 LTS or Ubuntu Server 22.04 LTS
Instance type: t3.medium or t3.small for smoother Docker/SAM usage
Storage: 20 GB gp3 or larger
Key pair: create or use an existing .pem key
Security group inbound rules:
SSH TCP 22 Your IP only
HTTP TCP 80 0.0.0.0/0
HTTPS TCP 443 Optional, if you add SSL later
You do not need to expose port 3000 publicly because Nginx will proxy /api to 127.0.0.1:3000 inside the EC2 machine.
Attach an IAM role to the EC2 instance with permission to use:
S3 bucket used by CloudMentor
DynamoDB table used by CloudMentor
CloudWatch logs, optional
For a quick classroom demo, you can use broader managed policies, but for production use a least-privilege policy limited to your exact S3 bucket and DynamoDB table.
From your laptop:
chmod 400 your-key.pem
ssh -i your-key.pem ubuntu@EC2_PUBLIC_IPUpdate the system:
sudo apt-get update -y
sudo apt-get upgrade -yCopy or clone the project onto EC2, then run:
cd cloudmentor-serverless
chmod +x scripts/ec2-bootstrap.sh
./scripts/ec2-bootstrap.shThe bootstrap script installs:
Node.js
npm
Docker
AWS CLI v2
AWS SAM CLI
Nginx
Git/Rsync/Unzip
Important: after bootstrap finishes, log out and SSH back in so Docker group permission is refreshed.
exit
ssh -i your-key.pem ubuntu@EC2_PUBLIC_IPOn your laptop or on EC2, run this from the project root after AWS CLI is configured or the EC2 role is attached:
AWS_REGION=ap-southeast-1 \
BUCKET_NAME=cloudmentor-materials-yourname-ec2 \
TABLE_NAME=cloudmentor-history-ec2 \
CORS_ORIGIN="http://EC2_PUBLIC_IP" \
./scripts/create-ec2-aws-resources.shThis script creates/configures:
Private S3 bucket
S3 block public access
S3 server-side encryption
S3 CORS for file upload
DynamoDB table with userId + createdAtId keys
From the project root on EC2:
export PUBLIC_HOST=EC2_PUBLIC_IP
export PUBLIC_FRONTEND_ORIGIN=http://EC2_PUBLIC_IP
export FRONTEND_API_BASE_URL=http://EC2_PUBLIC_IP/api
export AI_MODE=mock
export OPENAI_API_KEY=
export OPENAI_MODEL=gpt-4.1-mini
export AWS_REGION=ap-southeast-1
export STORAGE_MODE=s3
export MATERIALS_BUCKET=cloudmentor-materials-yourname-ec2
export TABLE_NAME=cloudmentor-history-ec2
./scripts/ec2-deploy.shThe script does this:
1. Writes backend/env.ec2.json
2. Installs backend packages
3. Runs sam build
4. Starts SAM local API as a systemd service on 127.0.0.1:3000
5. Builds the React frontend with VITE_API_BASE_URL=http://EC2_PUBLIC_IP/api
6. Copies frontend/dist to /var/www/cloudmentor
7. Configures Nginx to serve React from http://EC2_PUBLIC_IP
8. Configures Nginx to proxy /api/* to the backend
Open the app:
http://EC2_PUBLIC_IP
Test backend through Nginx:
curl http://EC2_PUBLIC_IP/api/healthCheck backend logs:
sudo journalctl -u cloudmentor-api -fRestart backend manually:
sudo systemctl restart cloudmentor-apiThis project includes:
.github/workflows/deploy-ec2.yml
When you push to the main branch, GitHub Actions will:
1. Connect to EC2 over SSH
2. Sync the latest code to /opt/cloudmentor or your configured app directory
3. Rebuild the backend with SAM
4. Restart the backend systemd service
5. Rebuild the React frontend
6. Copy the new frontend build to Nginx
7. Serve the latest app from http://EC2_PUBLIC_IP
Go to:
GitHub repository → Settings → Secrets and variables → Actions → New repository secret
Add:
EC2_HOST EC2 public IPv4 address, for example 13.229.xx.xx
EC2_USER ubuntu
EC2_SSH_KEY private key content, including BEGIN/END lines
EC2_APP_DIR /opt/cloudmentor
AI_MODE mock for classroom demo, openai for real OpenAI calls
OPENAI_API_KEY your OpenAI API key, required only when AI_MODE=openai
OPENAI_MODEL gpt-4.1-mini
AWS_REGION ap-southeast-1
MATERIALS_BUCKET cloudmentor-materials-yourname-ec2
TABLE_NAME cloudmentor-history-ec2
STORAGE_MODE s3
For EC2_SSH_KEY, paste the full private key text:
-----BEGIN OPENSSH PRIVATE KEY-----
...
-----END OPENSSH PRIVATE KEY-----
After secrets are configured:
git add .
git commit -m "Update CloudMentor"
git push origin mainThen open:
http://EC2_PUBLIC_IP
This project also includes:
.github/workflows/deploy-serverless-backend.yml
This workflow deploys the real AWS serverless backend with SAM:
API Gateway
AWS Lambda
S3 bucket
DynamoDB table
IAM permissions
Required GitHub repository secrets:
AWS_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY
AWS_REGION
OPENAI_API_KEY
OPENAI_MODEL
AI_MODE
CORS_ORIGIN
SAM_STACK_NAME
Run it from:
GitHub → Actions → Deploy Serverless Backend to AWS → Run workflow
Or push backend changes to main.
After deployment, get the API URL from CloudFormation/SAM output:
aws cloudformation describe-stacks \
--stack-name cloudmentor \
--query "Stacks[0].Outputs" \
--output tableIf you want EC2 frontend to call the real Lambda API instead of the EC2 SAM-local backend, set:
FRONTEND_API_BASE_URL=https://YOUR_API_ID.execute-api.YOUR_REGION.amazonaws.com
Then rerun:
./scripts/ec2-deploy.shFrom your laptop:
cd backend
npm install
sam build
sam deploy --guidedRecommended values:
Stack Name: cloudmentor
AWS Region: ap-southeast-1
Parameter OpenAiApiKey: your OpenAI API key
Parameter OpenAiModel: gpt-4.1-mini
Parameter CorsOrigin: * for classroom demo, or http://EC2_PUBLIC_IP when using EC2 frontend
Confirm changes before deploy: Y
Allow SAM CLI IAM role creation: Y
Disable rollback: N
Save arguments to samconfig.toml: Y
SAM deploy creates and updates the AWS resources. Your Lambda code is packaged from the backend folder and pushed to AWS through CloudFormation.
Expected outputs:
ApiBaseUrl = https://abc123.execute-api.ap-southeast-1.amazonaws.com
MaterialsBucketName = cloudmentor-cloudmentormaterialsbucket-xxxx
TableName = cloudmentor-CloudMentorTable-xxxx
VITE_API_BASE_URL=http://localhost:3000Frontend runs at:
http://localhost:5173
VITE_API_BASE_URL=http://EC2_PUBLIC_IP/apiFrontend runs at:
http://EC2_PUBLIC_IP
VITE_API_BASE_URL=https://YOUR_API_ID.execute-api.YOUR_REGION.amazonaws.comFrontend still runs at:
http://EC2_PUBLIC_IP
The React app is not running with npm run dev in EC2 production/classroom mode.
Instead:
npm run build
↓
frontend/dist
↓
/var/www/cloudmentor
↓
Nginx
↓
http://EC2_PUBLIC_IP
Nginx also proxies API calls:
http://EC2_PUBLIC_IP/api/health
↓
Nginx
↓
http://127.0.0.1:3000/health
↓
SAM local Lambda backend
sam local start-apiThis does not push anything to AWS. It runs the Lambda code locally inside Docker.
sam build
sam deployThis packages the backend code and deploys it to AWS Lambda through CloudFormation.
The Lambda handler is configured in backend/template.yaml:
CodeUri: .
Handler: src/app.handlerMeaning:
Take backend code from this folder
Use handler function from backend/src/app.mjs
CloudMentor stores any uploaded file under the demo size limit, but auto-loads text only from:
.txt
.md
.markdown
.csv
.json
.yaml
.yml
.log
PDF/DOCX files are stored, but this classroom version does not extract text from them yet. Students can add PDF extraction later using another Lambda, Amazon Textract, or a document parser.
- Never put the OpenAI API key in the React frontend.
- React
.envvalues are visible in the browser build. - Keep the OpenAI API key only in Lambda, EC2 server environment, SAM parameter, or GitHub secret.
- Do not commit
backend/env.json,backend/env.ec2.json, or any other backendenv.*.jsonfile that contains secrets. - For production, replace
CorsOrigin: *with the real frontend origin. - Use HTTPS for production.
- Add Cognito or another auth layer before real student usage.
- Add stricter file validation and malware scanning before production.
- Use least-privilege IAM instead of broad demo permissions.
Install AWS SAM CLI, then verify:
sam --versionRun:
sudo usermod -aG docker ubuntu
exitThen SSH back into the EC2 machine.
Check:
curl http://EC2_PUBLIC_IP/api/health
sudo systemctl status cloudmentor-api
sudo journalctl -u cloudmentor-api -fCheck S3 CORS:
aws s3api get-bucket-cors --bucket YOUR_BUCKET_NAMEMake sure CORS_ORIGIN matches:
http://EC2_PUBLIC_IP
For quick classroom testing, you can use:
*
Check:
aws dynamodb describe-table --table-name YOUR_TABLE_NAME --region YOUR_REGIONMake sure the EC2 IAM role or AWS CLI credentials can write to the table.
If you deployed the SAM backend:
cd backend
sam deleteIf you created EC2 classroom resources manually:
aws s3 rm s3://YOUR_BUCKET_NAME --recursive
aws s3api delete-bucket --bucket YOUR_BUCKET_NAME --region YOUR_REGION
aws dynamodb delete-table --table-name YOUR_TABLE_NAME --region YOUR_REGIONStop or terminate the EC2 instance if class is finished.
Suggested teaching sequence:
Day 1: React UI and API calls
Day 2: Lambda handler and routes
Day 3: OpenAI prompt design
Day 4: Upload flow and S3 pre-signed URLs
Day 5: DynamoDB history
Day 6: Local SAM and EC2 hosting
Day 7: GitHub Actions CI/CD
Day 8: Real AWS Lambda deployment with SAM
If npm install fails with a URL similar to:
packages.applied-caas-gateway1.internal.api.openai.orgthat means your package-lock.json or npm registry is pointing to an internal package mirror that your laptop cannot access.
Run this from both backend and frontend if needed:
npm config set registry https://registry.npmjs.org/
npm config delete proxy
npm config delete https-proxy
rm -rf node_modules package-lock.json
npm cache clean --force
npm installThe project includes .npmrc files that force the public npm registry:
registry=https://registry.npmjs.org/Do not commit backend/env.json because it contains your OpenAI API key.