Skip to content

Commit 258bd32

Browse files
authored
Merge pull request #35 from CS3219-AY2425S1/development
Question service implementation
2 parents e6a4efe + 7f3ae1b commit 258bd32

File tree

136 files changed

+36972
-6854
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

136 files changed

+36972
-6854
lines changed

.github/workflows/ci.yml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
name: ci
2+
3+
on:
4+
push:
5+
branches:
6+
- "*"
7+
pull_request:
8+
branches:
9+
- "*"
10+
11+
env:
12+
NODE_VERSION: 20
13+
14+
jobs:
15+
ci:
16+
runs-on: ubuntu-latest
17+
strategy:
18+
matrix:
19+
service: [frontend, backend/question-service, backend/user-service]
20+
steps:
21+
- name: Checkout code
22+
uses: actions/checkout@v4
23+
- name: Setting node version
24+
uses: actions/setup-node@v4
25+
with:
26+
node-version: ${{ env.NODE_VERSION }}
27+
- name: Install dependencies
28+
working-directory: ${{ matrix.service }}
29+
run: npm install
30+
- name: Linting
31+
working-directory: ${{ matrix.service }}
32+
run: npm run lint
33+
# - name: Tests
34+
# working-directory: ${{ matrix.service }}
35+
# run: npm test

.github/workflows/lint.yml

Lines changed: 0 additions & 46 deletions
This file was deleted.

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,12 @@ dist
1212
dist-ssr
1313
*.local
1414

15+
# Coverage
16+
coverage
17+
1518
# Editor directories and files
1619
.vscode/*
20+
!.vscode/settings.json
1721
!.vscode/extensions.json
1822
.idea
1923
.DS_Store

.vscode/settings.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"[typescript]": {
3+
"editor.defaultFormatter": "esbenp.prettier-vscode",
4+
"editor.formatOnSave": true,
5+
"prettier.tabWidth": 2
6+
}
7+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
MONGO_URI=MONGO_URI
2+
3+
FIREBASE_PROJECT_ID=FIREBASE_PROJECT_ID
4+
FIREBASE_PRIVATE_KEY=FIREBASE_PRIVATE_KEY
5+
FIREBASE_CLIENT_EMAIL=FIREBASE_CLIENT_EMAIL
6+
FIREBASE_STORAGE_BUCKET=FIREBASE_STORAGE_BUCKET
7+
8+
ORIGINS=http://localhost:5173,http://127.0.0.1:5173
9+
10+
USER_SERVICE_URL=USER_SERVICE_URL

backend/question-service/README.md

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,71 @@
11
# Question Service
2+
3+
## Setting-up MongoDB
4+
5+
> :notebook: If you are familiar to MongoDB and wish to use a local instance, please feel free to do so. This guide utilizes MongoDB Cloud Services.
6+
7+
1. Set up a MongoDB Shared Cluster by following the steps in this [Guide](../user-service/MongoDBSetup.md).
8+
9+
2. After setting up, go to the Database Deployment Page. You would see a list of the Databases you have set up. Select `Connect` on the cluster you just created earlier.
10+
11+
![alt text](../user-service/GuideAssets/ConnectCluster.png)
12+
13+
3. Select the `Drivers` option, as we have to link to a Node.js App (Question Service).
14+
15+
![alt text](../user-service/GuideAssets/DriverSelection.png)
16+
17+
4. Select `Node.js` in the `Driver` pull-down menu, and copy the connection string.
18+
19+
Notice, you may see `<password>` in this connection string. We will be replacing this with the admin account password that we created earlier on when setting up the Shared Cluster.
20+
21+
![alt text](../user-service/GuideAssets/ConnectionString.png)
22+
23+
5. In the `question-service` directory, create a copy of the `.env.sample` file and name it `.env`.
24+
25+
6. Update the `MONGO_URI` of the `.env` file, and paste the string we copied earlier in step 4.
26+
27+
## Setting-up Firebase
28+
29+
1. Go to https://console.firebase.google.com/u/0/.
30+
31+
2. Create a project and choose a project name. Navigate to `Storage` and click on it to activate it.
32+
33+
3. Select `Start in production mode` and your preferred cloud storage region.
34+
35+
4. After Storage is created, go to `Rules` section and set rule to:
36+
37+
```
38+
rules_version = '2';
39+
service firebase.storage {
40+
match /b/{bucket}/o {
41+
match /{allPaths=**} {
42+
allow read: if true;
43+
allow write: if request.auth != null;
44+
}
45+
}
46+
}
47+
```
48+
49+
This rule ensures that only verified users can upload images while ensuring that URLs of images are public. Remember to click `Publish` to save changes.
50+
51+
5. Go to `Settings`, `Project settings`, `Service accounts` and click `Generate new private key`. This will download a `.json` file, which will contain your credentials.
52+
53+
6. In `.env` of question service, replace:
54+
- `FIREBASE_PROJECT_ID` with `project_id` found in the downloaded json file.
55+
- `FIREBASE_PRIVATE_KEY` with `private_key` found in the downloaded json file.
56+
- `FIREBASE_CLIENT_EMAIL` with `client_email` found in the downloaded json file.
57+
- `FIREBASE_STORAGE_BUCKET` with the folder path of the Storage. It should look something like `gs://<appname>.appspot.com`.
58+
59+
## Running Question Service
60+
61+
1. Follow the instructions [here](https://nodejs.org/en/download/package-manager) to set up Node v20.
62+
63+
2. Open Command Line/Terminal and navigate into the `question-service` directory.
64+
65+
3. Run the command: `npm install`. This will install all the necessary dependencies.
66+
67+
4. Run the command `npm start` to start the Question Service in production mode, or use `npm run dev` for development mode, which includes features like automatic server restart when you make code changes.
68+
69+
5. To view Question Service documentation, go to http://localhost:3000/docs.
70+
71+
6. Using applications like Postman, you can interact with the Question Service on port 3000. If you wish to change this, please update the `.env` file.

backend/question-service/app.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import express, { Request, Response } from "express";
2+
import dotenv from "dotenv";
3+
import swaggerUi from "swagger-ui-express";
4+
import yaml from "yaml";
5+
import fs from "fs";
6+
import cors from "cors";
7+
8+
import connectDB from "./config/db.ts";
9+
import questionRoutes from "./src/routes/questionRoutes.ts";
10+
11+
dotenv.config();
12+
13+
const allowedOrigins = process.env.ORIGINS
14+
? process.env.ORIGINS.split(",")
15+
: ["http://localhost:5173", "http://127.0.0.1:5173"];
16+
17+
const file = fs.readFileSync("./swagger.yml", "utf-8");
18+
const swaggerDocument = yaml.parse(file);
19+
20+
const app = express();
21+
22+
if (process.env.NODE_ENV !== "test") {
23+
connectDB();
24+
}
25+
26+
app.use(cors({ origin: allowedOrigins, credentials: true }));
27+
app.options("*", cors({ origin: allowedOrigins, credentials: true }));
28+
29+
// To handle CORS Errors
30+
// app.use((req: Request, res: Response, next: NextFunction) => {
31+
// res.header("Access-Control-Allow-Origin", req.headers.origin); // "*" -> Allow all links to access
32+
33+
// res.header(
34+
// "Access-Control-Allow-Headers",
35+
// "Origin, X-Requested-With, Content-Type, Accept, Authorization",
36+
// );
37+
38+
// // Browsers usually send this before PUT or POST Requests
39+
// if (req.method === "OPTIONS") {
40+
// res.header("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, PATCH");
41+
// return res.status(200).json({});
42+
// }
43+
44+
// // Continue Route Processing
45+
// next();
46+
// });
47+
48+
app.use(express.json());
49+
app.use("/api/questions", questionRoutes);
50+
app.use("/docs", swaggerUi.serve, swaggerUi.setup(swaggerDocument));
51+
app.get("/", (req: Request, res: Response) => {
52+
res.status(200).json({ message: "Hello world from question service" });
53+
});
54+
55+
export default app;
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import mongoose from "mongoose";
2+
import dotenv from "dotenv";
3+
4+
dotenv.config();
5+
6+
const connectDB = async () => {
7+
try {
8+
if (process.env.MONGO_URI == undefined) {
9+
throw new Error("MONGO_URI is undefined");
10+
}
11+
12+
await mongoose.connect(process.env.MONGO_URI);
13+
console.log("MongoDB connected");
14+
} catch (error) {
15+
console.error(error);
16+
process.exit(1);
17+
}
18+
};
19+
20+
export default connectDB;
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import admin from "firebase-admin";
2+
3+
admin.initializeApp({
4+
credential: admin.credential.cert({
5+
projectId: process.env.FIREBASE_PROJECT_ID,
6+
privateKey: process.env.FIREBASE_PRIVATE_KEY?.replace(/\\n/g, "\n"),
7+
clientEmail: process.env.FIREBASE_CLIENT_EMAIL,
8+
} as admin.ServiceAccount),
9+
storageBucket: process.env.FIREBASE_STORAGE_BUCKET,
10+
});
11+
12+
const bucket = admin.storage().bucket();
13+
14+
export { bucket };
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import multer from "multer";
2+
3+
const storage = multer.memoryStorage();
4+
const upload = multer({ storage }).array("images[]");
5+
6+
export { upload };

0 commit comments

Comments
 (0)