Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
Yingming23 committed Oct 5, 2024
2 parents c8edbc1 + 754f2ca commit 7cbe59c
Show file tree
Hide file tree
Showing 18 changed files with 106 additions and 98 deletions.
23 changes: 11 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,22 +23,14 @@ git clone https://github.com/CS3219-AY2425S1/cs3219-ay2425s1-project-g39.git
cd cs3219-ay2425s1-project-g39
```

2. Install the required dependencies.
2. Add environment variables.

```bash
npm run install-all
```

> [!NOTE]
> The above command installs the dependencies for all of the services. No need to do it individually!
Add the provided secret .env folder in the root directory ```cs3219-ay2425s1-project-g39```

<!-- TODO: Replace with instructions to run on Docker -->

3. Run the frontend.
3. Run the docker containers.

```bash
cd frontend
npm run dev
docker compose up -d
```

Congratulations! You have successfully set up PeerPrep. :tada:
Expand All @@ -53,3 +45,10 @@ Sign up for an account with your email address and password, and use that to log

From here, just click on any of the questions to see their descriptions. You can filter for any topic or difficulty of your choosing using the selectors.

4. Stop and remove the docker containers and images.

```bash
docker compose up --rmi "all"
```


16 changes: 14 additions & 2 deletions compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,22 @@ services:
nginx:
build:
context: ./nginx
container_name: nginx
restart: always
volumes:
- ./nginx/default.conf:/tmp/default.conf
- /etc/letsencrypt:/etc/letsencrypt
- react-build:/app/dist
environment:
USER_SERVICE_ADDR: user-service:3001
QUESTION_SERVICE_ADDR: question-service:8000
ports:
- "80:80"
- "443:443"
depends_on:
- user-service
- frontend
- user-service
- question-service
healthcheck:
test:
[
Expand All @@ -29,6 +32,7 @@ services:
frontend:
build:
context: ./frontend
container_name: frontend-build
volumes:
- react-build:/app/dist
entrypoint: ["sh", "-c", "npm run build && exit 0"]
Expand All @@ -38,18 +42,26 @@ services:
context: ./user-service
container_name: user-service-backend
env_file:
- .env
- .env/.user_env
depends_on:
- user-service-mongo
volumes:
- ./user-service:/app
- /app/node_modules

user-service-mongo:
container_name: user-service-mongo-test
image: mongo:4.2
volumes:
- mongo-data:/data/db

question-service:
build:
context: ./question-service
container_name: question-service-backend
env_file:
- .env/.question_env

volumes:
mongo-data:
react-build:
91 changes: 36 additions & 55 deletions frontend/src/pages/Admin.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,10 @@
import {
Accordion,
AppShell,
Button,
Card,
Container,
FileInput,
Group,
Image as MantineImage,
MantineProvider,
Modal,
MultiSelect,
Portal,
Select,
Stack,
Text,
TextInput,
Textarea,
Title,
createTheme,
} from '@mantine/core';
import { Accordion, AppShell, Button, Card, Container, Group, Modal, MultiSelect, Select, Stack, Text, TextInput, Textarea, Title } from '@mantine/core';
import { useListState } from '@mantine/hooks';
import { Notifications, notifications } from '@mantine/notifications';
import '@mantine/notifications/styles.css';
import { useEffect, useMemo, useState } from 'react';


interface Question {
id: string;
_id: string;
Expand All @@ -34,7 +15,7 @@ interface Question {
images: string[];
}

const API_BASE_URL = 'http://localhost:8000';
const API_BASE_URL = 'http://localhost/api/questions';

const difficulties = ['Easy', 'Medium', 'Hard'];
const topics = [
Expand All @@ -56,12 +37,12 @@ function QuestionEditor() {
const [newDescription, setNewDescription] = useState('');
const [newDifficulty, setNewDifficulty] = useState<string | null>(null);
const [newTopic, setNewTopic] = useState<string[]>([]);
const [editingId, setEditingId] = useState<string | null>(null);
const [editingId, setEditingId] = useState<string | undefined>(undefined);
const [filterDifficulty, setFilterDifficulty] = useState<string | null>(null);
const [filterTopic, setFilterTopic] = useState<string[]>([]);
const [newImageFiles, setImageFiles] = useState<File[]>([]);
// const [newImageFiles, setImageFiles] = useState<File[]>([]);
const [newImageNames, setImageNames] = useState<string[]>([]);
const [imageSrc, setImageSrc] = useState('');
// const [imageSrc, setImageSrc] = useState('');

useEffect(() => {
fetchQuestions();
Expand All @@ -86,37 +67,37 @@ function QuestionEditor() {
);
};

const uploadImages = async () => {
const formData = new FormData();
newImageFiles.forEach((file) => {
formData.append('img', file);
});
// const uploadImages = async () => {
// const formData = new FormData();
// newImageFiles.forEach((file) => {
// formData.append('img', file);
// });

const response = await fetch(`${API_BASE_URL}/img`, {
method: 'POST',
body: formData,
});
// const response = await fetch(`${API_BASE_URL}/img`, {
// method: 'POST',
// body: formData,
// });

const data = await response.json();
// const data = await response.json();

if (!response.ok) {
console.error('Failed to upload image');
} else {
setImageNames([...newImageNames, data.filename]);
}
};
// if (!response.ok) {
// console.error('Failed to upload image');
// } else {
// setImageNames([...newImageNames, data.filename]);
// }
// };

const getImage = async (filename: string) => {
const response = await fetch(`${API_BASE_URL}/img/${filename}`);
if (!response.ok) {
console.error('Failed to fetch image');
} else {
const blob = await response.blob();
// Create an object URL from the blob
const url = URL.createObjectURL(blob);
setImageSrc(url);
}
};
// const getImage = async (filename: string) => {
// const response = await fetch(`${API_BASE_URL}/img/${filename}`);
// if (!response.ok) {
// console.error('Failed to fetch image');
// } else {
// const blob = await response.blob();
// // Create an object URL from the blob
// const url = URL.createObjectURL(blob);
// setImageSrc(url);
// }
// };

const addQuestion = async () => {
if (newTitle.trim() === '') {
Expand Down Expand Up @@ -280,13 +261,13 @@ function QuestionEditor() {
}, [questions, filterDifficulty, filterTopic]);

const resetForm = () => {
setEditingId(null);
setEditingId(undefined);
setNewTitle('');
setNewDescription('');
setNewDifficulty(null);
setNewTopic([]);
setImageNames([]);
setImageFiles([]);
// setImageFiles([]);
};

return (
Expand Down Expand Up @@ -464,4 +445,4 @@ function QuestionEditor() {
);
}

export default QuestionEditor;
export default QuestionEditor;
15 changes: 10 additions & 5 deletions frontend/src/router.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
import { createBrowserRouter } from 'react-router-dom';

import Admin from './pages/Admin';
import Landing from './pages/Landing';

// import Landing from './pages/Landing';

const router = createBrowserRouter([
// {
// path: '/',
// element: <Landing />,
// },
// {
// path: '/pages/',
// element: <Admin />,
// },
{
path: '/',
element: <Landing />,
},
{
path: '/pages/',
element: <Admin />,
},
]);
Expand Down
1 change: 1 addition & 0 deletions frontend/tsconfig.app.tsbuildinfo
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"root":["./src/app.tsx","./src/main.tsx","./src/router.tsx","./src/vite-env.d.ts","./src/components/modal/loginmodal.tsx","./src/components/modal/signupmodal.tsx","./src/pages/admin.tsx","./src/pages/landing.tsx"],"version":"5.6.2"}
1 change: 1 addition & 0 deletions frontend/tsconfig.node.tsbuildinfo
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"root":["./vite.config.ts"],"version":"5.6.2"}
8 changes: 7 additions & 1 deletion nginx/default.conf
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,18 @@ server {
}

location ~ ^/api/(user|auth) {

proxy_pass http://$USER_SERVICE_ADDR;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

location ~ ^/api/(questions) {
proxy_pass http://$QUESTION_SERVICE_ADDR;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}

# # HTTPS server block
Expand Down
3 changes: 2 additions & 1 deletion nginx/start.sh
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
#!/bin/bash
envsubst '\$USER_SERVICE_ADDR' < /tmp/default.conf > /etc/nginx/conf.d/default.conf && nginx -g 'daemon off;'
envsubst '\$USER_SERVICE_ADDR \$QUESTION_SERVICE_ADDR' < /tmp/default.conf > /etc/nginx/conf.d/default.conf
nginx -g 'daemon off;'
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
"scripts": {
"install-all": "npm install && run-p install-all:*",
"install-all:frontend": "cd frontend && npm install",
"install-all:question-service": "cd question-service && npm install"
"install-all:question-service": "cd question-service && npm install",
"install-all:user-service": "cd user-service && npm install"
},
"devDependencies": {
"@eslint/js": "^9.10.0",
Expand Down
2 changes: 1 addition & 1 deletion question-service/API.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Question Service API Documentation

endpoint: `http://localhost:8000`
endpoint: `http://localhost:8000/api`

## CREATE Route

Expand Down
2 changes: 1 addition & 1 deletion question-service/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ app.use((req, res, next) => {
next();
});

app.use('/', router);
app.use('/api/questions', router);

/**
* IMAGE HANDLING
Expand Down
11 changes: 6 additions & 5 deletions question-service/model/repository.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import Question from "./Question.js";
import "dotenv/config";
import { connect } from "mongoose";
import 'dotenv/config';
import { connect } from 'mongoose';
import Question from './Question.js';
import 'dotenv/config';

export async function connectToDB() {
let mongoDBUri = process.env.MONGO_URI;
let mongoDBUri = process.env.QUESTION_MONGO_CLOUD_URI;

await connect(mongoDBUri);
}
}
6 changes: 3 additions & 3 deletions question-service/server.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import http from "http";
import index from "./index.js";
import "dotenv/config";
import http from "http";
import index from './index.js';
import { connectToDB } from "./model/repository.js";

const port = process.env.PORT || 8000;
const port = process.env.QUESTION_PORT || 8000;

const server = http.createServer(index);

Expand Down
8 changes: 4 additions & 4 deletions user-service/controller/authController.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const loginUser = async (req, res) => {
user.lastLogin = new Date();
await updateUserById(user._id, { lastLogin: user.lastLogin });

const token = jwt.sign({ userId: user._id }, process.env.JWT_SECRET, { expiresIn: '1y' });
const token = jwt.sign({ userId: user._id }, process.env.USER_JWT_SECRET, { expiresIn: '1y' });
res.json({ token: token, user_id: user._id.toString() });
} catch (err) {
res.status(500).json({ error: err.message });
Expand Down Expand Up @@ -59,13 +59,13 @@ const forgotPassword = async (req, res) => {
port: 465,
secure: true,
auth: {
user: process.env.EMAIL_USER,
pass: process.env.EMAIL_PASS,
user: process.env.USER_EMAIL_USER,
pass: process.env.USER_EMAIL_PASS,
},
});

const mailOptions = {
from: process.env.EMAIL_USER,
from: process.env.USER_EMAIL_USER,
to: user.email,
subject: 'Password Reset',
text: `You are receiving this because you (or someone else) have requested the reset of the password for your account.\n\n
Expand Down
Loading

0 comments on commit 7cbe59c

Please sign in to comment.