diff --git a/backend-services/comment-service-2/src/Middleware/Auth.ts b/backend-services/comment-service-2/src/Middleware/Auth.ts index 488ce165fca35dcfb1d4fb3384353967b6482e39..5977caa548954636d3959727966125b6fa8f0623 100644 --- a/backend-services/comment-service-2/src/Middleware/Auth.ts +++ b/backend-services/comment-service-2/src/Middleware/Auth.ts @@ -2,7 +2,7 @@ import jwt, { Secret, JwtPayload } from 'jsonwebtoken'; import { Request, Response, NextFunction } from 'express'; import Config from '../../config' -export const SECRET_KEY: Secret = Config.JWT_SECRET; +export const SECRET_KEY: Secret = process.env.JWT_PRIVATE_KEY || ""; export interface CustomJWTRequest extends Request { token?: string | JwtPayload | TokenData; diff --git a/backend-services/feed-service/Dockerfile b/backend-services/feed-service/Dockerfile index 9ce26ec214215609d4d3f84a8abd0ad28d17f335..816c12e152289dfb1258fb580208b575cd30b082 100644 --- a/backend-services/feed-service/Dockerfile +++ b/backend-services/feed-service/Dockerfile @@ -16,9 +16,7 @@ RUN unzip -d svc target/universal/feed-service-1.0.0.zip \ && rm svc/bin/*.bat \ && mv svc/bin/* svc/bin/start -# Delete the RUNNING_PID fle that incorrectly tells Play there is already an instance running -RUN rm -f svc/RUNNING_PID - EXPOSE 9000 -CMD svc/bin/start +# Delete the RUNNING_PID fle that incorrectly tells Play there is already an instance running +CMD rm -f svc/RUNNING_PID && svc/bin/start diff --git a/backend-services/feed-service/app/ApplicationStart.scala b/backend-services/feed-service/app/ApplicationStart.scala new file mode 100644 index 0000000000000000000000000000000000000000..5b8b61b91dc2b8e80407c95fe94a6a230491db75 --- /dev/null +++ b/backend-services/feed-service/app/ApplicationStart.scala @@ -0,0 +1,21 @@ +import scala.concurrent.Future +import javax.inject._ +import play.api.inject.ApplicationLifecycle + +import utils.ConfigHelper +import repositories.QuestionRepository + +// Creates an `ApplicationStart` object once at start-up and registers hook for shut-down. +@Singleton +class ApplicationStart @Inject() (lifecycle: ApplicationLifecycle) { + println("Starting...") + + if (ConfigHelper.getBoolean("enable.question.db.seeding")) { + QuestionRepository.seedDatabase() + } + + // Shut-down hook + lifecycle.addStopHook { () => + Future.successful(()) + } +} diff --git a/backend-services/feed-service/app/Module.scala b/backend-services/feed-service/app/Module.scala new file mode 100644 index 0000000000000000000000000000000000000000..9feab53ffa1dedaf2beec18a291d83c6fb8eb24a --- /dev/null +++ b/backend-services/feed-service/app/Module.scala @@ -0,0 +1,7 @@ +import com.google.inject.AbstractModule + +class Module extends AbstractModule { + override def configure() = { + bind(classOf[ApplicationStart]).asEagerSingleton() + } +} diff --git a/backend-services/feed-service/app/models/DailyQuestion.scala b/backend-services/feed-service/app/models/DailyQuestion.scala index b87277fddee8694e56ee0391ff7219b527cb4655..9aee375bf5ec27b60726b0e1abbde2d311abd4d5 100644 --- a/backend-services/feed-service/app/models/DailyQuestion.scala +++ b/backend-services/feed-service/app/models/DailyQuestion.scala @@ -7,7 +7,7 @@ import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.{Future, Await} import scala.concurrent.duration._ -import com.typesafe.config.ConfigFactory +import utils.ConfigHelper import org.bson.types.ObjectId import java.util.Date @@ -86,8 +86,8 @@ object DailyQuestion { } def getRandomTime(): Date = { - val minHour = ConfigFactory.load().getInt("dailyQuestions.min.hour") - val maxHour = ConfigFactory.load().getInt("dailyQuestions.max.hour") + val minHour = ConfigHelper.getInt("dailyQuestions.min.hour") + val maxHour = ConfigHelper.getInt("dailyQuestions.max.hour") val randomHour = Random.between(minHour, maxHour + 1) val randomMinute = Random.between(0, 60) diff --git a/backend-services/feed-service/app/models/User.scala b/backend-services/feed-service/app/models/User.scala index 9e2e40d15cf97d788a00d04225f1cdd40c24a30f..8650f71d5a17b7891bedff809e1959cc49bbace0 100644 --- a/backend-services/feed-service/app/models/User.scala +++ b/backend-services/feed-service/app/models/User.scala @@ -4,7 +4,7 @@ import models.HttpCall import play.api.libs.json.JsValue -import com.typesafe.config.ConfigFactory +import utils.ConfigHelper import org.bson.types.ObjectId import scala.concurrent.Future @@ -13,7 +13,7 @@ import scala.concurrent.ExecutionContext.Implicits.global object User { def getUserFriends(userId: ObjectId, jwt: String): Future[Seq[ObjectId]] = { - val friendServiceUri: String = ConfigFactory.load().getString("friend.service.uri") + val friendServiceUri: String = ConfigHelper.getString("friend.service.uri") val url: String = s"${friendServiceUri}friends" val queryStringParameters: Seq[(String, String)] = Seq(("userId", userId.toString())) @@ -27,7 +27,7 @@ object User { } def userExists(userId: ObjectId, jwt: String): Future[Boolean] = { - val userServiceUri: String = ConfigFactory.load().getString("user.service.uri") + val userServiceUri: String = ConfigHelper.getString("user.service.uri") val url: String = s"${userServiceUri}test/verifyUser" val queryStringParameters: Seq[(String, String)] = Seq(("userId", userId.toString())) diff --git a/backend-services/feed-service/app/models/actions/AuthenticationRequest.scala b/backend-services/feed-service/app/models/actions/AuthenticationRequest.scala index 231edd1fe59c544d00aa71c45eb138dc1b96383a..5b5c742ac8e9bc2f60dc162ae35c060db07c33e8 100644 --- a/backend-services/feed-service/app/models/actions/AuthenticationRequest.scala +++ b/backend-services/feed-service/app/models/actions/AuthenticationRequest.scala @@ -1,6 +1,6 @@ package models.actions -import com.typesafe.config.ConfigFactory +import utils.ConfigHelper import play.api.mvc.{ActionTransformer, Request, WrappedRequest} @@ -41,7 +41,7 @@ class AuthenticationTransformer @Inject() (implicit val executionContext: Execut * @return The user ID specified in the JWT's payload. */ def processJWT[A](request: Request[A]): (String, Option[ObjectId], Boolean) = { - val privateKey = ConfigFactory.load().getString("jwt.privateKey") + val privateKey = ConfigHelper.getString("jwt.privateKey") try { val authHeader = request.headers.get("Authorization").get diff --git a/backend-services/feed-service/app/repositories/DailyQuestionRepository.scala b/backend-services/feed-service/app/repositories/DailyQuestionRepository.scala index 5e85226a553204368882a08c76d6588cc4bd0d32..a417f40ffcc26903d8f21a308371864f5d6ff37c 100644 --- a/backend-services/feed-service/app/repositories/DailyQuestionRepository.scala +++ b/backend-services/feed-service/app/repositories/DailyQuestionRepository.scala @@ -3,7 +3,7 @@ package repositories import models.DailyQuestion import utils.MongoConnection -import com.typesafe.config.ConfigFactory +import utils.ConfigHelper import org.bson.types.ObjectId import scala.concurrent.ExecutionContext.Implicits.global @@ -11,8 +11,8 @@ import scala.concurrent.Future class DailyQuestionRepository extends Repository[DailyQuestion] ( - ConfigFactory.load().getString("mongo.questionService.db"), - ConfigFactory.load().getString("mongo.dailyQuestions.collection") + ConfigHelper.getString("mongo.questionService.db"), + ConfigHelper.getString("mongo.dailyQuestions.collection") ) { /** * Inserts a DailyQuestion record into the database. diff --git a/backend-services/feed-service/app/repositories/DailyRepository.scala b/backend-services/feed-service/app/repositories/DailyRepository.scala index 514860f212551d4ba8f6a6cbd670a19707dcfa2d..0aebdd642cb05655970d95541b8db528b211be2e 100644 --- a/backend-services/feed-service/app/repositories/DailyRepository.scala +++ b/backend-services/feed-service/app/repositories/DailyRepository.scala @@ -3,7 +3,7 @@ package repositories import models.Daily import utils.MongoConnection -import com.typesafe.config.ConfigFactory +import utils.ConfigHelper import org.bson.types.ObjectId import scala.concurrent.ExecutionContext.Implicits.global @@ -11,8 +11,8 @@ import scala.concurrent.Future class DailyRepository extends Repository[Daily] ( - ConfigFactory.load().getString("mongo.feedService.db"), - ConfigFactory.load().getString("mongo.dailies.collection") + ConfigHelper.getString("mongo.feedService.db"), + ConfigHelper.getString("mongo.dailies.collection") ) { /** * Inserts a Daily record into the database. diff --git a/backend-services/feed-service/app/repositories/QuestionRepository.scala b/backend-services/feed-service/app/repositories/QuestionRepository.scala index bc967143cd8354d50efba53383db99a8e3ccb481..053be38a1bffc55d644d8deff4ed8eed462d3f7c 100644 --- a/backend-services/feed-service/app/repositories/QuestionRepository.scala +++ b/backend-services/feed-service/app/repositories/QuestionRepository.scala @@ -3,7 +3,7 @@ package repositories import models.Question import utils.MongoConnection -import com.typesafe.config.ConfigFactory +import utils.ConfigHelper import org.bson.types.ObjectId import scala.concurrent.ExecutionContext.Implicits.global @@ -11,8 +11,8 @@ import scala.concurrent.Future class QuestionRepository extends Repository[Question] ( - ConfigFactory.load().getString("mongo.questionService.db"), - ConfigFactory.load().getString("mongo.questions.collection") + ConfigHelper.getString("mongo.questionService.db"), + ConfigHelper.getString("mongo.questions.collection") ) { /** * Inserts a Question record into the database. @@ -29,3 +29,22 @@ class QuestionRepository extends Repository[Question] ( }) } } + +object QuestionRepository { + def seedDatabase(): Unit = { + println("Seeding Question Database...") + + try { + Question.createQuestionAsync("What is your favourite food?") + Question.createQuestionAsync("What is your dream holiday destination?") + Question.createQuestionAsync("What us your favourite local spot?") + + println("Successfully seeded Question Database!") + } catch { + case ex: Throwable => { + println("Error seeding Question Database...") + println(ex) + } + } + } +} diff --git a/backend-services/feed-service/app/utils/ConfigHelper.scala b/backend-services/feed-service/app/utils/ConfigHelper.scala index da8e39576c7ec7ddff02e6d6582f7e79f74e0215..e13c8d370621bc0c2cb481f0c55621838413137a 100644 --- a/backend-services/feed-service/app/utils/ConfigHelper.scala +++ b/backend-services/feed-service/app/utils/ConfigHelper.scala @@ -4,6 +4,10 @@ import com.typesafe.config.{Config, ConfigFactory} object ConfigHelper { private val applicationConfig: Config = ConfigFactory.load("application.conf") + private val referenceConfig: Config = ConfigFactory.parseResources("reference.conf") + private val config: Config = referenceConfig.withFallback(applicationConfig).resolve() - def getString(key: String): String = applicationConfig.getString(key) + def getString(key: String): String = config.getString(key) + def getInt(key: String): Int = config.getInt(key) + def getBoolean(key: String): Boolean = config.getBoolean(key) } diff --git a/backend-services/feed-service/conf/application.conf b/backend-services/feed-service/conf/application.conf index df8dd7088faf0ab98ce052e1001560657b320b0b..9d3697fda5e7b0fe38d862498d1901870c20fc22 100644 --- a/backend-services/feed-service/conf/application.conf +++ b/backend-services/feed-service/conf/application.conf @@ -1,5 +1,5 @@ # MongoDB Connection Strings -mongodb.uri= "mongodb://localhost:27017/" +mongodb.uri = "mongodb://localhost:27017/" mongo.feedService.db = "feed-service" mongo.dailies.collection = "dailies" @@ -7,7 +7,10 @@ mongo.questionService.db = "question-service" mongo.questions.collection = "questions" mongo.dailyQuestions.collection = "daily-questions" -# JWT Authenticationn +# Whether to seed the Question DB +enable.question.db.seeding = false + +# JWT Authentication jwt.privateKey = "" # Daily Questions @@ -32,8 +35,5 @@ play.filters.enabled += play.filters.cors.CORSFilter # Use a custom error handler to not return HTML views play.http.errorHandler = "models.CustomErrorHandler" -# --- Configuration Values to be replaced with environment variables, if present --- -mongodb.uri = ${?MONGO_URI} -jwt.privateKey = ${?JWT_PRIVATE_KEY} -friend.service.uri = ${?FRIEND_SERVICE_URI} -user.service.uri = ${?USER_SERVICE_URI} +# Control which modules are loaded when Play starts. +play.modules.enabled += Module diff --git a/backend-services/feed-service/conf/reference.conf b/backend-services/feed-service/conf/reference.conf new file mode 100644 index 0000000000000000000000000000000000000000..2c86965b12c8edfd4c788f3c5b45436672125c20 --- /dev/null +++ b/backend-services/feed-service/conf/reference.conf @@ -0,0 +1,17 @@ +# --- Configuration Values to be replaced with environment variables, if present --- + +# MongoDB Connection Strings +mongodb.uri = ${?MONGO_URI} + +# Whether to seed the Question DB +enable.question.db.seeding = ${?ENABLE_QUESTION_DB_SEEDING} + +# JWT Authentication +jwt.privateKey = ${?JWT_PRIVATE_KEY} + +# External Service URIs +friend.service.uri = ${?FRIEND_SERVICE_URI} +user.service.uri = ${?USER_SERVICE_URI} + +# Application Secret Key - https://www.playframework.com/documentation/2.8.x/ApplicationSecret +play.http.secret.key=${?PLAY_HTTP_SECRET_KEY} diff --git a/backend-services/friend-service/src/Middleware/Auth.ts b/backend-services/friend-service/src/Middleware/Auth.ts index 488ce165fca35dcfb1d4fb3384353967b6482e39..4fe35e601e8a25a6a522fa7df47dfde3167ce0c0 100644 --- a/backend-services/friend-service/src/Middleware/Auth.ts +++ b/backend-services/friend-service/src/Middleware/Auth.ts @@ -2,7 +2,7 @@ import jwt, { Secret, JwtPayload } from 'jsonwebtoken'; import { Request, Response, NextFunction } from 'express'; import Config from '../../config' -export const SECRET_KEY: Secret = Config.JWT_SECRET; +export const SECRET_KEY: Secret = process.env.JWT_PRIVATE_KEY || "" ; export interface CustomJWTRequest extends Request { token?: string | JwtPayload | TokenData; diff --git a/backend-services/user-service/controllers/appController.js b/backend-services/user-service/controllers/appController.js index 622dc28eaeeb64f956f03fcc45df76783ada0f81..b6d24ddf8b88c6e44db0f1e5623b0cc108f5a123 100644 --- a/backend-services/user-service/controllers/appController.js +++ b/backend-services/user-service/controllers/appController.js @@ -33,7 +33,7 @@ export async function register(req, res) { const existUsername = new Promise((resolve, reject) => { UserModel.findOne({ username }, function (err, user) { if (err) reject(new Error(err)); - if (user) reject({ error: "Please use unique username" }); + if (user) reject({ error: "Username already exists!" }); resolve(); }); @@ -43,7 +43,7 @@ export async function register(req, res) { const existEmail = new Promise((resolve, reject) => { UserModel.findOne({ email }, function (err, email) { if (err) reject(new Error(err)); - if (email) reject({ error: "Please use unique Email" }); + if (email) reject({ error: "An account with that email address already exists!" }); resolve(); }); @@ -118,7 +118,7 @@ export async function login(req, res) { .compare(password, user.password) .then((passwordCheck) => { if (!passwordCheck) - return res.status(400).send({ error: "Don't have Password" }); + return res.status(400).send({ error: "Incorrect username or password!" }); // create jwt token const token = jwt.sign( diff --git a/backend-services/user-service/middleware/auth.js b/backend-services/user-service/middleware/auth.js index 2381df5d27802745e715ae1f2e1fe6316fab0a76..fe431c5cf22b0bcf5f64db812a4f1e33af441649 100644 --- a/backend-services/user-service/middleware/auth.js +++ b/backend-services/user-service/middleware/auth.js @@ -6,7 +6,7 @@ export default async function Auth(req, res, next){ const token = req.headers.authorization.split(" ")[1]; - const decodedToken = await jwt.verify(token, ENV.JWT_SECRET); + const decodedToken = await jwt.verify(token, process.env.JWT_PRIVATE_KEY); req.user = decodedToken; next(); diff --git a/backend-services/user-service/server.js b/backend-services/user-service/server.js index bd091dc38bf1e67d8a6b5122c6eaaddb1645d34e..59166872e0a6109f7b70def2aed6ae0928806829 100644 --- a/backend-services/user-service/server.js +++ b/backend-services/user-service/server.js @@ -3,6 +3,8 @@ import cors from 'cors'; import morgan from 'morgan'; import connect from './database/conn.js' import router from './router/route.js' +import bcrypt from "bcrypt"; +import UserModel from "./model/User.model.js"; const app = express(); @@ -22,12 +24,36 @@ app.get('/', (req, res) => { app.use('/api', router) // Start server only when we have valid connection -connect().then(() => { +connect().then(async () => { try { + // Database Seeding + if (process.env.ENABLE_USER_DB_SEEDING) { + try { + const password = process.env.SEEDED_ADMIN_PASSWORD + const hashedPassword = await bcrypt.hash(password, 10); + + const admin = new UserModel({ + username: "admin", + password: hashedPassword, + email: "admin@email.com", + profile: "", + admin: true + }) + + await admin.save() + console.log("Successfully seeded User Database!") + } + catch(error) { + console.log("Error seeding User Database...") + console.log(error) + } + } + app.listen(port, () => { console.log(`Server connected to http://localhost:${port}`); }) } catch (error) { + console.log(error) console.log('Cannot connect to the server') } }).catch(error => { diff --git a/daily-thought-frontend/src/components/navigation/NavBar.tsx b/daily-thought-frontend/src/components/navigation/NavBar.tsx index b37205fe4c3e320f0644e830d34464dbc928c49f..564a560c75a76fb8a0178b4a41e6f4800d07a3f0 100644 --- a/daily-thought-frontend/src/components/navigation/NavBar.tsx +++ b/daily-thought-frontend/src/components/navigation/NavBar.tsx @@ -63,7 +63,7 @@ const NavBar: FC<PropsWithChildren<NavBarProps>> = ({ children, user, rehydrateF </div> </div> )} - <div className="pl-5" onClick={() => Router.push('/')}> + <div className="pl-5 cursor-pointer" onClick={() => Router.push('/')}> <h1 className="ml-5 text-2xl font-bold tracking-tight text-c-green">Daily</h1> </div> diff --git a/docker-compose.yml b/docker-compose.yml index 2eb4be2e5ca352ec987cb9b66530b9a2ba8e1f0f..143c052c6b32477b0c76fd5b483f1f1dc24434e8 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -20,6 +20,7 @@ services: - MONGO_URI=mongodb://feed-mongo:27017/ - JWT_PRIVATE_KEY=yB/uX5KdyjHN9P34IE49HxAcrlQ4gfvpVJEzGbo5E/I= - FRIEND_SERVICE_URI=http://friend-service:9000/ + - ENABLE_QUESTION_DB_SEEDING=true user-service: build: @@ -30,6 +31,9 @@ services: - "9000" environment: - MONGO_URI=mongodb://user-mongo:27017/userdb + - JWT_PRIVATE_KEY=yB/uX5KdyjHN9P34IE49HxAcrlQ4gfvpVJEzGbo5E/I= + - ENABLE_USER_DB_SEEDING=true + - SEEDED_ADMIN_PASSWORD=doesntreallymatter friend-service: build: @@ -42,6 +46,7 @@ services: - MONGO_HOST=friend-mongo - MONGO_PORT=27017 - MONGO_DBNAME=friends + - JWT_PRIVATE_KEY=yB/uX5KdyjHN9P34IE49HxAcrlQ4gfvpVJEzGbo5E/I= comment-service: build: @@ -54,6 +59,7 @@ services: - MONGO_HOST=comment-mongo - MONGO_PORT=27017 - MONGO_DBNAME=comments + - JWT_PRIVATE_KEY=yB/uX5KdyjHN9P34IE49HxAcrlQ4gfvpVJEzGbo5E/I= frontend-service: build: