diff --git a/.env.sample b/.env.sample index 6877ddca74..030ec204c3 100644 --- a/.env.sample +++ b/.env.sample @@ -1,4 +1,10 @@ # This is a sample environment configuration file. # Copy this file to .env and replace the placeholder values with your own. +QUESTION_DB_CLOUD_URI= +QUESTION_DB_LOCAL_URI=mongodb://question-db:27017/question QUESTION_DB_USERNAME=user -QUESTION_DB_PASSWORD=password \ No newline at end of file +QUESTION_DB_PASSWORD=password +QUESTION_CORS_ORIGIN=* +QUESTION_PORT=8081 + +NODE_ENV=development \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 162a839d39..4549daf7a7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -19,11 +19,13 @@ services: build: context: services/question dockerfile: Dockerfile - environment: - MONGODB_USERNAME: ${QUESTION_DB_USERNAME} - MONGODB_PASSWORD: ${QUESTION_DB_PASSWORD} ports: - 8081:8081 + environment: + DB_CLOUD_URI: ${QUESTION_DB_CLOUD_URI} + DB_LOCAL_URI: ${QUESTION_DB_LOCAL_URI} + DB_USERNAME: ${QUESTION_DB_USERNAME} + DB_PASSWORD: ${QUESTION_DB_PASSWORD} volumes: - /app/node_modules - ./services/question:/app diff --git a/services/question/.env.sample b/services/question/.env.sample new file mode 100644 index 0000000000..5613b8f4a7 --- /dev/null +++ b/services/question/.env.sample @@ -0,0 +1,9 @@ +# This is a sample environment configuration file. +# Copy this file to .env and replace the placeholder values with your own. +DB_CLOUD_URI= +DB_LOCAL_URI=mongodb://question-db:27017/question +DB_USERNAME=user +DB_PASSWORD=password +CORS_ORIGIN=* +PORT=8081 +NODE_ENV=development \ No newline at end of file diff --git a/services/question/eslint.config.mjs b/services/question/eslint.config.mjs index 04d352f27d..3f1c13ceb1 100644 --- a/services/question/eslint.config.mjs +++ b/services/question/eslint.config.mjs @@ -12,5 +12,13 @@ export default tseslint.config({ ...tseslint.configs.stylistic, eslintPluginPrettierRecommended, ], - rules: {}, + rules: { + // https://stackoverflow.com/questions/68816664/get-rid-of-error-delete-eslint-prettier-prettier-and-allow-use-double + 'prettier/prettier': [ + 'error', + { + 'endOfLine': 'auto', + } + ] + }, }); diff --git a/services/question/src/app.ts b/services/question/src/app.ts index f0dab0bdfe..ad51519cca 100644 --- a/services/question/src/app.ts +++ b/services/question/src/app.ts @@ -3,25 +3,23 @@ import morgan from 'morgan'; import cors from 'cors'; import router from './routes'; import questionRouter from './routes/questionRoutes'; -import { syncQuestions } from './setup'; -import { connectDb } from './models'; -import mongoose from 'mongoose'; import bodyParser from 'body-parser'; -import { corsOptions } from './config'; const app: Express = express(); -// Establish database connection -connectDb(); -mongoose.connection.once('open', async () => await syncQuestions()); - // Middleware app.use(morgan('dev')); app.use(bodyParser.urlencoded({ extended: false })); app.use(bodyParser.json()); -app.use(cors(corsOptions)); +app.use( + cors({ + origin: process.env.CORS_ORIGIN ?? true, + methods: ['GET', 'POST', 'DELETE', 'PUT', 'PATCH'], + allowedHeaders: ['Origin', 'X-Request-With', 'Content-Type', 'Accept', 'Authorization'], + }), +); // Routes app.use('/', router); diff --git a/services/question/src/config/index.ts b/services/question/src/config/index.ts deleted file mode 100644 index 37006cc1ee..0000000000 --- a/services/question/src/config/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -export const PORT = 8081; -export const MONGODB_USERNAME = process.env.MONGODB_USERNAME; -export const MONGODB_PASSWORD = process.env.MONGODB_PASSWORD; -export const MONGODB_URL = 'mongodb://question-db:27017'; -export const MONGODB_DATABASE = 'question'; - -export const corsOptions = { - origin: process.env.corsOrigin ?? true, - methods: ['GET', 'POST', 'DELETE', 'PUT', 'PATCH'], - allowedHeaders: ['Origin', 'X-Request-With', 'Content-Type', 'Accept', 'Authorization'], -}; diff --git a/services/question/src/config/questions.json b/services/question/src/data/questions.json similarity index 100% rename from services/question/src/config/questions.json rename to services/question/src/data/questions.json diff --git a/services/question/src/index.ts b/services/question/src/index.ts index cb83666a8b..99e6654e26 100644 --- a/services/question/src/index.ts +++ b/services/question/src/index.ts @@ -1,4 +1,20 @@ import app from './app'; -import { PORT } from './config'; +import { connectToDB, upsertManyQuestions } from './models'; +import { getDemoQuestions } from './utils/data'; -app.listen(PORT, () => console.log(`App is running on port ${PORT}.`)); +const port = process.env.PORT || 8081; + +connectToDB() + .then(async () => { + console.log('MongoDB connected successfully'); + return await getDemoQuestions(); + }) + .then(questions => upsertManyQuestions(questions)) + .then(() => { + console.log('Questions synced successfully'); + app.listen(port, () => console.log(`Question service is listening on port ${port}.`)); + }) + .catch(error => { + console.error('Failed to start server'); + console.error(error); + }); diff --git a/services/question/src/models/index.ts b/services/question/src/models/index.ts index 988460a157..b78620a016 100644 --- a/services/question/src/models/index.ts +++ b/services/question/src/models/index.ts @@ -1,18 +1,29 @@ import mongoose from 'mongoose'; -import { MONGODB_USERNAME, MONGODB_PASSWORD, MONGODB_URL, MONGODB_DATABASE } from '../config'; +import { IQuestion, Question } from './questionModel'; -export const connectDb = () => { - const connectionString = `${MONGODB_URL}/${MONGODB_DATABASE}`; +export async function connectToDB() { + const mongoURI = process.env.NODE_ENV === 'production' ? process.env.DB_CLOUD_URI : process.env.DB_LOCAL_URI; - mongoose - .connect(connectionString, { - authSource: 'admin', - user: MONGODB_USERNAME, - pass: MONGODB_PASSWORD, - }) - .then(() => console.log('MongoDB connected successfully')) - .catch(error => { - console.error('MongoDB connection error:', error.message); - process.exit(1); - }); -}; + if (!mongoURI) { + throw Error('MongoDB URI not specified'); + } else if (!process.env.DB_USERNAME || !process.env.DB_PASSWORD) { + throw Error('MongoDB credentials not specified'); + } + + await mongoose.connect(mongoURI, { + authSource: 'admin', + user: process.env.DB_USERNAME, + pass: process.env.DB_PASSWORD, + }); +} + +export async function upsertManyQuestions(questions: IQuestion[]) { + const ops = questions.map(item => ({ + updateOne: { + filter: { id: item.id }, + update: item, + upsert: true, + }, + })); + await Question.bulkWrite(ops); +} diff --git a/services/question/src/setup.ts b/services/question/src/setup.ts deleted file mode 100644 index 0950c2d516..0000000000 --- a/services/question/src/setup.ts +++ /dev/null @@ -1,29 +0,0 @@ -import fs from 'fs/promises'; -import { IQuestion, Question } from './models/questionModel'; - -const getQuestions = async (): Promise => { - const data = await fs.readFile('./src/config/questions.json', { encoding: 'utf8' }); - return JSON.parse(data); -}; - -export const syncQuestions = async () => { - try { - const questions = await getQuestions(); - const ops = questions.map(item => ({ - updateOne: { - filter: { id: item.id }, - update: item, - upsert: true, - }, - })); - await Question.bulkWrite(ops); - console.log('Questions synced successfully'); - } catch (error) { - if (error instanceof Error) { - console.log('Error syncing questions: ', error.message); - } else { - console.log('Error syncing questions'); - } - process.exit(1); - } -}; diff --git a/services/question/src/utils/data.ts b/services/question/src/utils/data.ts new file mode 100644 index 0000000000..2755c73ebd --- /dev/null +++ b/services/question/src/utils/data.ts @@ -0,0 +1,7 @@ +import fs from 'fs/promises'; +import { IQuestion } from '../models/questionModel'; + +export async function getDemoQuestions(): Promise { + const data = await fs.readFile('./src/data/questions.json', { encoding: 'utf8' }); + return JSON.parse(data); +}