diff --git a/.drone.yml b/.drone.yml
index ec4c05c..a468b58 100644
--- a/.drone.yml
+++ b/.drone.yml
@@ -39,12 +39,14 @@ steps:
OVERWRITE: false
PRIVATE: false
ADMINS: lucasevard,emrekartal,arthurvalin,lucasdelanier
- CODEFIRST_CLIENTDRONE_ENV_POSTGRES_PASSWORD:
+ CODEFIRST_CLIENTDRONE_ENV_POSTGRES_ROOT_PASSWORD:
from_secret: db_root_password
CODEFIRST_CLIENTDRONE_ENV_POSTGRES_DB:
from_secret: db_database
CODEFIRST_CLIENTDRONE_ENV_POSTGRES_USER:
from_secret: db_user
+ CODEFIRST_CLIENTDRONE_ENV_POSTGRES_PASSWORD:
+ from_secret: db_password
- name: docker-image
image: plugins/docker
@@ -65,6 +67,12 @@ steps:
CONTAINERNAME: api
COMMAND: create
OVERWRITE: true
+ CODEFIRST_CLIENTDRONE_ENV_POSTGRES_DB:
+ from_secret: db_database
+ CODEFIRST_CLIENTDRONE_ENV_POSTGRES_USER:
+ from_secret: db_user
+ CODEFIRST_CLIENTDRONE_ENV_POSTGRES_PASSWORD:
+ from_secret: db_password
ADMINS: lucasevard,emrekartal,arthurvalin,lucasdelanier
depends_on: [docker-image]
diff --git a/.gitignore b/.gitignore
index c426c32..b3f8ec6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -33,4 +33,6 @@ out/
/.nb-gradle/
### VS Code ###
-.vscode/
\ No newline at end of file
+.vscode/
+
+!**/src/target/**
\ No newline at end of file
diff --git a/Sources/pom.xml b/Sources/pom.xml
index 0d8b30f..c569b14 100644
--- a/Sources/pom.xml
+++ b/Sources/pom.xml
@@ -32,6 +32,11 @@
ktor-server-core-jvm
${ktor_version}
+
+ org.postgresql
+ postgresql
+ 42.7.1
+
io.ktor
ktor-server-netty-jvm
diff --git a/Sources/src/main/kotlin/allin/Application.kt b/Sources/src/main/kotlin/allin/Application.kt
index 93119eb..bd62889 100644
--- a/Sources/src/main/kotlin/allin/Application.kt
+++ b/Sources/src/main/kotlin/allin/Application.kt
@@ -1,19 +1,28 @@
package allin
-import allin.model.User
+import allin.entities.UsersEntity
import allin.routing.BasicRouting
import allin.routing.BetRouter
+import allin.routing.ParticipationRouter
import allin.routing.UserRouter
+import allin.utils.*
import com.typesafe.config.ConfigFactory
import io.ktor.serialization.kotlinx.json.*
import io.ktor.server.application.*
+import io.ktor.server.auth.*
+import io.ktor.server.auth.jwt.*
import io.ktor.server.config.*
import io.ktor.server.engine.*
import io.ktor.server.netty.*
import io.ktor.server.plugins.contentnegotiation.*
-import allin.utils.TokenManager
-import io.ktor.server.auth.*
-import io.ktor.server.auth.jwt.*
+import org.ktorm.database.Database
+
+val db_database=System.getenv().get("POSTGRES_DB")
+val db_user=System.getenv().get("POSTGRES_USER")
+val db_password=System.getenv().get("POSTGRES_PASSWORD")
+val db_host=System.getenv().get("POSTGRES_HOST")
+
+val database = Database.connect("jdbc:postgresql://AllDev-postgresapi/$db_database", user = db_user, password = db_password)
fun main() {
embeddedServer(Netty, port = 8080, host = "0.0.0.0") {
@@ -27,9 +36,9 @@ private fun Application.extracted() {
authentication {
jwt {
verifier(tokenManager.verifyJWTToken())
- realm=config.property("realm").getString()
+ realm = config.property("realm").getString()
validate { jwtCredential ->
- if(jwtCredential.payload.getClaim("username").asString().isNotEmpty())
+ if (jwtCredential.payload.getClaim("username").asString().isNotEmpty())
JWTPrincipal(jwtCredential.payload)
else null
}
@@ -41,4 +50,6 @@ private fun Application.extracted() {
BasicRouting()
UserRouter()
BetRouter()
+ ParticipationRouter()
+ UsersEntity.createUserTable()
}
diff --git a/Sources/src/main/kotlin/allin/dto/UserDTO.kt b/Sources/src/main/kotlin/allin/dto/UserDTO.kt
index d59f39c..a440450 100644
--- a/Sources/src/main/kotlin/allin/dto/UserDTO.kt
+++ b/Sources/src/main/kotlin/allin/dto/UserDTO.kt
@@ -1,15 +1,4 @@
package allin.dto
-
-import allin.model.User
import kotlinx.serialization.Serializable
-
@Serializable
-data class UserDTO(val username: String,val email: String, val nbCoins: Int)
-@Serializable
-data class UserDTOWithToken(val username: String,val email: String, val nbCoins: Int, val token:String?)
-fun convertUserToUserDTO(user: User): UserDTO {
- return UserDTO(user.username, user.email, user.nbCoins)
-}
-fun convertUserToUserDTOToken(user: User): UserDTOWithToken {
- return UserDTOWithToken(user.username, user.email, user.nbCoins,user.token)
-}
+data class UserDTO(val username: String, val email: String, val nbCoins: Double, var token:String?)
diff --git a/Sources/src/main/kotlin/allin/entities/UserEntity.kt b/Sources/src/main/kotlin/allin/entities/UserEntity.kt
index d8f0996..765c0a3 100644
--- a/Sources/src/main/kotlin/allin/entities/UserEntity.kt
+++ b/Sources/src/main/kotlin/allin/entities/UserEntity.kt
@@ -1,11 +1,79 @@
package allin.entities
+import allin.database
+import allin.dto.UserDTO
+import allin.model.User
+import allin.utils.Execute
+import org.ktorm.dsl.*
+import org.ktorm.entity.*
+import org.ktorm.expression.SqlExpression
import org.ktorm.schema.Table
+import org.ktorm.schema.double
import org.ktorm.schema.int
import org.ktorm.schema.varchar
-object UserEntity : Table("user") {
+interface UserEntity : Entity {
+ val username: String
+ var email: String
+ var password: String
+ var nbCoins: Double
+}
+object UsersEntity : Table("utilisateur") {
val id = int("id").primaryKey()
val username = varchar("username")
val password = varchar("password")
-}
\ No newline at end of file
+ val nbCoins = double("coins")
+ val email = varchar("email")
+
+ fun getUserToUserDTO(): MutableList {
+ return database.from(UsersEntity).select().map {
+ row -> UserDTO(
+ row[username].toString(),
+ row[email].toString(),
+ row[nbCoins]?:0.0,
+ null
+ )
+ }.toMutableList()
+ }
+
+ fun createUserTable(){
+ val request="CREATE TABLE IF not exists utilisateur ( id SERIAL PRIMARY KEY, username VARCHAR(255), password VARCHAR(255),coins double precision,email VARCHAR(255))"
+ database.Execute(request)
+ }
+
+
+ fun getUserByUsernameAndPassword(login: String): Pair {
+ return database.from(UsersEntity)
+ .select()
+ .where { (username eq login) /*and (password eq passwordParam)*/ }
+ .map { row ->
+ Pair(
+ UserDTO(
+ row[username].toString(),
+ row[email].toString(),
+ row[nbCoins] ?: 0.0,
+ null
+ ),
+ row[password].toString()
+ )
+ }
+ .firstOrNull() ?: Pair(null, null)
+ }
+
+ fun addUserEntity(user : User){
+ database.insert(UsersEntity){
+ set(it.nbCoins,user.nbCoins)
+ set(it.username,user.username)
+ set(it.password,user.password)
+ set(it.email,user.email)
+ }
+ }
+ fun deleteUserByUsername(username: String): Boolean {
+ val deletedCount = database.delete(UsersEntity) {
+ it.username eq username
+ }
+ return deletedCount > 0
+ }
+}
+
+
diff --git a/Sources/src/main/kotlin/allin/ext/PipelineContextExt.kt b/Sources/src/main/kotlin/allin/ext/PipelineContextExt.kt
new file mode 100644
index 0000000..e111f9b
--- /dev/null
+++ b/Sources/src/main/kotlin/allin/ext/PipelineContextExt.kt
@@ -0,0 +1,24 @@
+package allin.ext
+
+import allin.dto.UserDTO
+import allin.entities.UsersEntity
+import allin.model.ApiMessage
+import io.ktor.http.*
+import io.ktor.server.application.*
+import io.ktor.server.auth.*
+import io.ktor.server.auth.jwt.*
+import io.ktor.server.response.*
+import io.ktor.util.pipeline.*
+
+suspend fun PipelineContext<*, ApplicationCall>.hasToken(content: suspend (principal: JWTPrincipal) -> Unit) =
+ call.principal()?.let { content(it) } ?: call.respond(HttpStatusCode.Unauthorized)
+
+suspend fun PipelineContext<*, ApplicationCall>.verifyUserFromToken(
+ principal: JWTPrincipal,
+ content: suspend (user: UserDTO, password: String) -> Unit
+) {
+ val username = principal.payload.getClaim("username").asString()
+ val userPassword = UsersEntity.getUserByUsernameAndPassword(username)
+ userPassword.first?.let { content(it, userPassword.second ?: "") }
+ ?: call.respond(HttpStatusCode.NotFound, ApiMessage.TokenUserNotFound)
+}
\ No newline at end of file
diff --git a/Sources/src/main/kotlin/allin/model/ApiMessage.kt b/Sources/src/main/kotlin/allin/model/ApiMessage.kt
new file mode 100644
index 0000000..9f47ea3
--- /dev/null
+++ b/Sources/src/main/kotlin/allin/model/ApiMessage.kt
@@ -0,0 +1,14 @@
+package allin.model
+
+object ApiMessage {
+ const val Welcome = "Welcome on AllIn's API !"
+ const val TokenUserNotFound = "User not found with the valid token !"
+ const val UserNotFound = "User not found."
+ const val BetNotFound = "Bet not found."
+ const val BetAlreadyExist = "Bet already exists."
+ const val IncorrectLoginPassword = "Login and/or password incorrect."
+ const val UserAlreadyExist = "Mail and/or username already exists."
+ const val InvalidMail = "Invalid mail."
+ const val ParticipationNotFound = "Participation not found."
+ const val NotEnoughCoins = "Not enough coins."
+}
\ No newline at end of file
diff --git a/Sources/src/main/kotlin/allin/model/Bet.kt b/Sources/src/main/kotlin/allin/model/Bet.kt
index 5d4bc13..16d7bb1 100644
--- a/Sources/src/main/kotlin/allin/model/Bet.kt
+++ b/Sources/src/main/kotlin/allin/model/Bet.kt
@@ -1,19 +1,36 @@
package allin.model
-import allin.dto.UserDTOWithToken
-import allin.serializer.DateSerializer
+import allin.serializer.ZonedDateTimeSerializer
import kotlinx.serialization.Serializable
-import java.util.*
+import java.time.ZonedDateTime
@Serializable
-data class Bet(val id: Int, val theme: String, val sentenceBet: String, @Serializable(DateSerializer::class) val endRegistration: Date, @Serializable(DateSerializer::class) var endBet : Date, var isPrivate : Boolean, var response : MutableList, val createdBy : String)
+data class Bet(
+ val id: String,
+ val theme: String,
+ val sentenceBet: String,
+ @Serializable(ZonedDateTimeSerializer::class) val endRegistration: ZonedDateTime,
+ @Serializable(ZonedDateTimeSerializer::class) var endBet: ZonedDateTime,
+ var isPrivate: Boolean,
+ var response: MutableList,
+ val createdBy: String
+)
@Serializable
-data class UpdatedBetData(val id: Int,@Serializable(DateSerializer::class) val endBet: Date, val isPrivate: Boolean, val response: MutableList)
+data class UpdatedBetData(
+ val id: String,
+ @Serializable(ZonedDateTimeSerializer::class) val endBet: ZonedDateTime,
+ val isPrivate: Boolean,
+ val response: MutableList
+)
@Serializable
-data class BetWithoutId(val theme: String, val sentenceBet: String, @Serializable(DateSerializer::class) val endRegistration: Date, @Serializable(DateSerializer::class) var endBet : Date, var isPrivate : Boolean, var response : MutableList, val createdBy : String)
-
-fun convertBetWithoutIdToBet(betWithoutId: BetWithoutId,id : Int, username : String): Bet {
- return Bet(id,betWithoutId.theme,betWithoutId.sentenceBet,betWithoutId.endRegistration, betWithoutId.endBet, betWithoutId.isPrivate, betWithoutId.response, username)
-}
+data class BetWithoutId(
+ val theme: String,
+ val sentenceBet: String,
+ @Serializable(ZonedDateTimeSerializer::class) val endRegistration: ZonedDateTime,
+ @Serializable(ZonedDateTimeSerializer::class) var endBet: ZonedDateTime,
+ var isPrivate: Boolean,
+ var response: MutableList,
+ val createdBy: String
+)
\ No newline at end of file
diff --git a/Sources/src/main/kotlin/allin/model/BetAction.kt b/Sources/src/main/kotlin/allin/model/BetAction.kt
index f830e2c..2f2298b 100644
--- a/Sources/src/main/kotlin/allin/model/BetAction.kt
+++ b/Sources/src/main/kotlin/allin/model/BetAction.kt
@@ -1,5 +1,5 @@
package allin.model
-import allin.dto.UserDTOWithToken
+import allin.dto.UserDTO
data class BetAction(val id:Int, val coins: Int, val user: String, val bet: Int)
-data class BetActionCompleted(val id:Int, val coins: Int, val user: UserDTOWithToken, val bet: Bet)
+data class BetActionCompleted(val id:Int, val coins: Int, val user: UserDTO, val bet: Bet)
diff --git a/Sources/src/main/kotlin/allin/model/Participation.kt b/Sources/src/main/kotlin/allin/model/Participation.kt
new file mode 100644
index 0000000..b2e8495
--- /dev/null
+++ b/Sources/src/main/kotlin/allin/model/Participation.kt
@@ -0,0 +1,19 @@
+package allin.model
+
+import kotlinx.serialization.Serializable
+
+@Serializable
+data class Participation(
+ val id: String,
+ val betId: String,
+ val username: String,
+ val answer: String,
+ val stake: Int
+)
+
+@Serializable
+data class ParticipationRequest(
+ val betId: String,
+ val answer: String,
+ val stake: Int
+)
\ No newline at end of file
diff --git a/Sources/src/main/kotlin/allin/model/User.kt b/Sources/src/main/kotlin/allin/model/User.kt
index d01dc50..ac0122e 100644
--- a/Sources/src/main/kotlin/allin/model/User.kt
+++ b/Sources/src/main/kotlin/allin/model/User.kt
@@ -1,10 +1,27 @@
package allin.model
-import allin.dto.UserDTO
import kotlinx.serialization.Serializable
+
+@Serializable
+data class User(
+ val id: String,
+ val username: String,
+ val email: String,
+ var password: String,
+ var nbCoins: Int = 500,
+ var token: String? = null
+)
+
@Serializable
-data class User(val username: String, val email: String, var password: String, var nbCoins: Int = 1000, var token: String? = null)
+data class UserRequest(
+ val username: String,
+ val email: String,
+ var password: String
+)
@Serializable
-data class CheckUser(val login: String,val password: String)
\ No newline at end of file
+data class CheckUser(
+ val login: String,
+ val password: String
+)
\ No newline at end of file
diff --git a/Sources/src/main/kotlin/allin/routing/BetRouter.kt b/Sources/src/main/kotlin/allin/routing/BetRouter.kt
index 997cee5..e54e685 100644
--- a/Sources/src/main/kotlin/allin/routing/BetRouter.kt
+++ b/Sources/src/main/kotlin/allin/routing/BetRouter.kt
@@ -1,65 +1,95 @@
package allin.routing
-import io.ktor.server.application.*
-import io.ktor.server.request.*
-import io.ktor.server.routing.*
-import allin.model.*
+
+import allin.ext.hasToken
+import allin.ext.verifyUserFromToken
+import allin.model.ApiMessage
+import allin.model.Bet
+import allin.model.UpdatedBetData
import allin.utils.AppConfig
import io.ktor.http.*
+import io.ktor.server.application.*
+import io.ktor.server.auth.*
+import io.ktor.server.request.*
import io.ktor.server.response.*
+import io.ktor.server.routing.*
+import java.util.*
val bets = mutableListOf()
-val tokenManagerBet= AppConfig.tokenManager
+val tokenManagerBet = AppConfig.tokenManager
-fun CreateId() : Int{
- return bets.size
-}
-
-fun Application.BetRouter(){
- routing{
- route("/bets/add"){
- post{
- val bet = call.receive()
- val id = CreateId()
+fun Application.BetRouter() {
+ routing {
+ route("/bets/add") {
+ post {
+ val bet = call.receive()
+ val id = UUID.randomUUID().toString()
val username = tokenManagerBet.getUsernameFromToken(bet.createdBy)
- val findbet = bets.find { it.id == id }
- if(findbet==null){
- val betWithId = convertBetWithoutIdToBet(bet,id,username)
+ bets.find { it.id == id }?.let {
+ call.respond(HttpStatusCode.Conflict, ApiMessage.BetAlreadyExist)
+ } ?: run {
+ val betWithId = Bet(
+ id,
+ bet.theme,
+ bet.sentenceBet,
+ bet.endRegistration,
+ bet.endBet,
+ bet.isPrivate,
+ bet.response,
+ username
+ )
bets.add(betWithId)
call.respond(HttpStatusCode.Created, betWithId)
}
- call.respond(HttpStatusCode.Conflict,"Bet already exist")
}
}
- route("/bets/gets"){
- get{
- // if(bets.size>0)
- call.respond(HttpStatusCode.Accepted, bets.toList())
- // else call.respond(HttpStatusCode.NoContent)
+
+ route("/bets/gets") {
+ get {
+ // if(bets.size>0)
+ call.respond(HttpStatusCode.Accepted, bets.toList())
+ // else call.respond(HttpStatusCode.NoContent)
+ }
+ }
+
+ route("/bets/get/{id}") {
+ get {
+ val id = call.parameters["id"] ?: ""
+ bets.find { it.id == id }?.let { bet ->
+ call.respond(HttpStatusCode.Accepted, bet)
+ } ?: call.respond(HttpStatusCode.NotFound, ApiMessage.BetNotFound)
}
}
- route("/bets/delete"){
- post{
- val idbet = call.receive