Merge branch 'Stub_Db'

# Conflicts:
#	Sources/src/main/kotlin/allin/entities/UserEntity.kt
#	Sources/src/main/kotlin/allin/routing/UserRouter.kt
pull/10/head
Arthur VALIN 1 year ago
commit a877762d2d

@ -52,7 +52,7 @@ steps:
IMAGENAME: postgres:latest
CONTAINERNAME: postgresapi
COMMAND: create
OVERWRITE: false
OVERWRITE: true
PRIVATE: false
ADMINS: lucasevard,emrekartal,arthurvalin,lucasdelanier
CODEFIRST_CLIENTDRONE_ENV_POSTGRES_ROOT_PASSWORD:

2
.gitignore vendored

@ -36,3 +36,5 @@ out/
.vscode/
!**/src/target/**
**/src/target/**
/src/target

@ -47,6 +47,11 @@
<artifactId>ktorm-core</artifactId>
<version>3.2.0</version>
</dependency>
<dependency>
<groupId>org.ktorm</groupId>
<artifactId>ktorm-support-postgresql</artifactId>
<version>3.2.0</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>

@ -1,8 +1,11 @@
package allin
import allin.entities.*
import allin.data.AllInDataSource
import allin.data.mock.MockDataSource
import allin.data.postgres.PostgresDataSource
import allin.routing.*
import allin.utils.*
import allin.utils.TokenManager
import allin.utils.kronJob
import com.typesafe.config.ConfigFactory
import io.ktor.serialization.kotlinx.json.*
import io.ktor.server.application.*
@ -12,14 +15,23 @@ import io.ktor.server.config.*
import io.ktor.server.engine.*
import io.ktor.server.netty.*
import io.ktor.server.plugins.contentnegotiation.*
import org.ktorm.database.Database
import java.time.ZonedDateTime
import kotlin.time.ExperimentalTime
import kotlin.time.minutes
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")
@ExperimentalTime
val BET_VERIFY_DELAY = 1.minutes
val data_source = System.getenv()["DATA_SOURCE"]
private val allInDataSource: AllInDataSource = when (data_source) {
"mock" -> MockDataSource()
"postgres" -> PostgresDataSource()
else -> MockDataSource()
}
val Application.dataSource: AllInDataSource
get() = allInDataSource
val database = Database.connect("jdbc:postgresql://$db_host/$db_database", user = db_user, password = db_password)
fun main() {
embeddedServer(Netty, port = 8080, host = "0.0.0.0") {
@ -27,6 +39,7 @@ fun main() {
}.start(wait = true)
}
@OptIn(ExperimentalTime::class)
private fun Application.extracted() {
val config = HoconApplicationConfig(ConfigFactory.load())
val tokenManager = TokenManager.getInstance(config)
@ -41,16 +54,15 @@ private fun Application.extracted() {
}
}
}
install(ContentNegotiation) {
json()
}
install(ContentNegotiation) { json() }
BasicRouting()
UserRouter()
BetRouter()
ParticipationRouter()
BetDetailRouter()
UsersEntity.createUserTable()
BetsEntity.createBetsTable()
ResponsesEntity.createResponseTable()
ParticipationsEntity.createParticipationTable()
kronJob(BET_VERIFY_DELAY) {
dataSource.betDataSource.updateBetStatuses(ZonedDateTime.now())
}
}

@ -0,0 +1,12 @@
package allin.data
abstract class AllInDataSource {
abstract val userDataSource: UserDataSource
abstract val betDataSource: BetDataSource
abstract val participationDataSource: ParticipationDataSource
}

@ -0,0 +1,15 @@
package allin.data
import allin.model.Bet
import allin.model.UpdatedBetData
import java.time.ZonedDateTime
interface BetDataSource {
fun getAllBets(): List<Bet>
fun getBetById(id: String): Bet?
fun getBetsNotFinished(): List<Bet>
fun addBet(bet: Bet)
fun removeBet(id: String): Boolean
fun updateBet(data: UpdatedBetData): Boolean
fun updateBetStatuses(date: ZonedDateTime)
}

@ -0,0 +1,18 @@
package allin.data
import allin.model.Participation
interface ParticipationDataSource {
fun addParticipation(participation: Participation)
fun getParticipationFromBetId(betid: String): List<Participation>
fun getParticipationFromUserId(username: String, betid: String): List<Participation>
fun deleteParticipation(id: String): Boolean
}

@ -0,0 +1,21 @@
package allin.data
import allin.dto.UserDTO
import allin.model.User
interface UserDataSource {
fun getUserByUsername(username: String): Pair<UserDTO?, String?>
fun addUser(user: User)
fun deleteUser(username: String): Boolean
fun modifyUserCoins(username: String, amount: Int)
fun userExists(username: String, email: String): Boolean
}

@ -0,0 +1,44 @@
package allin.data.mock
import allin.data.BetDataSource
import allin.model.Bet
import allin.model.BetStatus
import allin.model.UpdatedBetData
import java.time.ZonedDateTime
class MockBetDataSource : BetDataSource {
override fun getAllBets(): List<Bet> = bets
override fun getBetById(id: String): Bet? =
bets.find { it.id == id }
override fun removeBet(id: String): Boolean =
bets.removeIf { it.id == id }
override fun updateBet(data: UpdatedBetData): Boolean {
return bets.find { it.id == data.id }?.let {
it.isPrivate = data.isPrivate
} != null
}
override fun getBetsNotFinished(): List<Bet> =
bets.filter { it.endBet >= ZonedDateTime.now() }
override fun addBet(bet: Bet) {
bets += bet
}
override fun updateBetStatuses(date: ZonedDateTime) {
bets.forEachIndexed { idx, bet ->
if (date >= bet.endRegistration) {
if (date >= bet.endBet) {
bets[idx] = bet.copy(status = BetStatus.WAITING)
} else {
bets[idx] = bet.copy(status = BetStatus.CLOSING)
}
}
}
}
private val bets by lazy { mutableListOf<Bet>() }
}

@ -0,0 +1,12 @@
package allin.data.mock
import allin.data.AllInDataSource
import allin.data.BetDataSource
import allin.data.ParticipationDataSource
import allin.data.UserDataSource
class MockDataSource : AllInDataSource() {
override val userDataSource: UserDataSource = MockUserDataSource()
override val betDataSource: BetDataSource = MockBetDataSource()
override val participationDataSource: ParticipationDataSource = MockParticipationDataSource()
}

@ -0,0 +1,22 @@
package allin.data.mock
import allin.data.ParticipationDataSource
import allin.model.Participation
class MockParticipationDataSource : ParticipationDataSource {
override fun addParticipation(participation: Participation) {
participations += participations
}
override fun getParticipationFromBetId(betid: String): List<Participation> =
participations.filter { it.betId == betid }
override fun getParticipationFromUserId(username: String, betid: String): List<Participation> =
participations.filter { it.betId == betid && it.username == username }
override fun deleteParticipation(id: String): Boolean =
participations.removeIf { it.id == id }
private val participations by lazy { mutableListOf<Participation>() }
}

@ -0,0 +1,41 @@
package allin.data.mock
import allin.data.UserDataSource
import allin.dto.UserDTO
import allin.model.User
class MockUserDataSource : UserDataSource {
override fun getUserByUsername(username: String): Pair<UserDTO?, String?> =
users.find { it.username == username }?.let {
Pair(
UserDTO(
id = it.id,
username = it.username,
email = it.email,
nbCoins = it.nbCoins,
token = it.token
),
it.password
)
} ?: Pair(null, null)
override fun addUser(user: User) {
users += user
}
override fun deleteUser(username: String): Boolean =
users.removeIf { it.username == username }
override fun modifyUserCoins(username: String, amount: Int) {
users.find { it.username == username }?.let {
it.nbCoins += amount
}
}
override fun userExists(username: String, email: String): Boolean =
users.any { it.username == username && it.email == email }
private val users by lazy {
mutableListOf<User>()
}
}

@ -0,0 +1,115 @@
package allin.data.postgres
import allin.data.BetDataSource
import allin.entities.BetsEntity
import allin.entities.NO_VALUE
import allin.entities.ResponsesEntity
import allin.entities.ResponsesEntity.response
import allin.entities.YES_VALUE
import allin.model.Bet
import allin.model.BetStatus
import allin.model.BetType
import allin.model.UpdatedBetData
import org.ktorm.database.Database
import org.ktorm.dsl.*
import java.time.ZoneId
import java.time.ZonedDateTime
import java.util.*
class PostgresBetDataSource(private val database: Database) : BetDataSource {
private fun QueryRowSet.toBet() =
Bet(
id = this[BetsEntity.id].toString(),
theme = this[BetsEntity.theme].toString(),
sentenceBet = this[BetsEntity.sentenceBet].toString(),
endRegistration = this[BetsEntity.endRegistration]!!.atZone(ZoneId.of("Europe/Paris")),
endBet = this[BetsEntity.endBet]!!.atZone(ZoneId.of("Europe/Paris")),
isPrivate = this[BetsEntity.isPrivate] ?: false,
status = this[BetsEntity.status] ?: BetStatus.IN_PROGRESS,
type = this[BetsEntity.type] ?: BetType.CUSTOM,
createdBy = this[BetsEntity.createdBy].toString(),
response = let {
val idBet = this[BetsEntity.id].toString()
val type = this[BetsEntity.type] ?: BetType.CUSTOM
if (type == BetType.CUSTOM) {
database.from(ResponsesEntity)
.select(response)
.where { ResponsesEntity.id eq UUID.fromString(idBet) }
.map { it[response].toString() }
} else {
listOf(YES_VALUE, NO_VALUE)
}
}
)
private fun Query.mapToBet() = this.map { it.toBet() }
override fun getAllBets(): List<Bet> =
database.from(BetsEntity).select().mapToBet()
override fun getBetById(id: String): Bet? =
database.from(BetsEntity).select().where {
BetsEntity.id eq UUID.fromString(id)
}.mapToBet().firstOrNull()
override fun getBetsNotFinished(): List<Bet> {
val currentTime = ZonedDateTime.now(ZoneId.of("Europe/Paris"))
return database.from(BetsEntity)
.select()
.where { BetsEntity.endBet greaterEq currentTime.toInstant() }
.mapToBet()
}
override fun addBet(bet: Bet) {
database.insert(BetsEntity) {
set(it.id, UUID.fromString(bet.id))
set(it.endBet, bet.endBet.toInstant())
set(it.endRegistration, bet.endRegistration.toInstant())
set(it.sentenceBet, bet.sentenceBet)
set(it.theme, bet.theme)
set(it.isPrivate, bet.isPrivate)
set(it.createdBy, bet.createdBy)
set(it.status, bet.status)
set(it.type, bet.type)
}
if (bet.type == BetType.CUSTOM) {
bet.response.forEach { selected ->
database.insert(ResponsesEntity) {
set(it.id, UUID.fromString(bet.id))
set(it.response, selected)
}
}
}
}
override fun removeBet(id: String): Boolean {
return database.delete(BetsEntity) { it.id eq UUID.fromString(id) } > 0
}
override fun updateBet(data: UpdatedBetData): Boolean {
return database.update(BetsEntity) {
set(BetsEntity.isPrivate, data.isPrivate)
where { BetsEntity.id eq UUID.fromString(data.id) }
} > 0
}
override fun updateBetStatuses(date: ZonedDateTime) {
database.update(BetsEntity) {
set(BetsEntity.status, BetStatus.WAITING)
where {
(date.toInstant() greaterEq BetsEntity.endRegistration) and
(date.toInstant() less BetsEntity.endBet)
}
}
database.update(BetsEntity) {
set(BetsEntity.status, BetStatus.CLOSING)
where {
(date.toInstant() greaterEq BetsEntity.endRegistration) and
(date.toInstant() greaterEq BetsEntity.endBet)
}
}
}
}

@ -0,0 +1,82 @@
package allin.data.postgres
import allin.data.AllInDataSource
import allin.data.BetDataSource
import allin.data.ParticipationDataSource
import allin.data.UserDataSource
import allin.utils.Execute
import org.ktorm.database.Database
class PostgresDataSource : AllInDataSource() {
private val database: Database
init {
val dbDatabase = System.getenv()["POSTGRES_DB"]
val dbUser = System.getenv()["POSTGRES_USER"]
val dbPassword = System.getenv()["POSTGRES_PASSWORD"]
val dbHost = System.getenv()["POSTGRES_HOST"]
database = Database.connect(
url = "jdbc:postgresql://$dbHost/$dbDatabase",
user = dbUser,
password = dbPassword
)
database.Execute(
"""
CREATE TABLE IF not exists utilisateur (
id uuid PRIMARY KEY,
username VARCHAR(255),
password VARCHAR(255),
coins double precision,
email VARCHAR(255)
)""".trimIndent()
)
database.Execute(
"""
CREATE TYPE betstatus AS ENUM
('InProgress', 'Waiting', 'Closing', 'Finished', 'Cancelled');
CREATE TYPE bettype AS ENUM
('Match', 'Binary', 'Custom');
CREATE TABLE IF not exists bet (
id uuid PRIMARY KEY,
theme VARCHAR(255),
endregistration timestamp,
endbet timestamp,
sentencebet varchar(500),
isprivate boolean,
createdby varchar(250),
status varchar(20),
type varchar(20)
)""".trimIndent()
)
database.Execute(
"""
CREATE TABLE IF NOT EXISTS participation (
id uuid PRIMARY KEY,
bet uuid,
username varchar(250),
answer varchar(250),
stake int
)""".trimIndent()
)
database.Execute(
"""
CREATE TABLE IF NOT EXISTS response (
id UUID,
response VARCHAR(250),
CONSTRAINT pk_response_id PRIMARY KEY (id,response)
)""".trimIndent()
)
}
override val userDataSource: UserDataSource = PostgresUserDataSource(database)
override val betDataSource: BetDataSource = PostgresBetDataSource(database)
override val participationDataSource: ParticipationDataSource = PostgresParticipationDataSource(database)
}

@ -0,0 +1,57 @@
package allin.data.postgres
import allin.data.ParticipationDataSource
import allin.entities.ParticipationsEntity
import allin.model.Participation
import org.ktorm.database.Database
import org.ktorm.dsl.*
import java.util.*
class PostgresParticipationDataSource(private val database: Database) : ParticipationDataSource {
private fun QueryRowSet.toParticipation() =
Participation(
id = this[ParticipationsEntity.id].toString(),
betId = this[ParticipationsEntity.betId].toString(),
username = this[ParticipationsEntity.username].toString(),
answer = this[ParticipationsEntity.answer].toString(),
stake = this[ParticipationsEntity.stake] ?: 0,
)
private fun Query.mapToParticipation() = this.map { it.toParticipation() }
override fun addParticipation(participation: Participation) {
database.insert(ParticipationsEntity) {
set(it.id, UUID.fromString(participation.id))
set(it.betId, UUID.fromString(participation.betId))
set(it.username, participation.username)
set(it.answer, participation.answer)
set(it.stake, participation.stake)
}
}
override fun getParticipationFromBetId(betid: String): List<Participation> {
return database.from(ParticipationsEntity)
.select()
.where { ParticipationsEntity.betId eq UUID.fromString(betid) }
.mapToParticipation()
}
override fun getParticipationFromUserId(username: String, betid: String): List<Participation> {
return database.from(ParticipationsEntity)
.select()
.where { (ParticipationsEntity.betId eq UUID.fromString(betid)) and (ParticipationsEntity.username eq username) }
.mapToParticipation()
}
fun getParticipationEntity(): List<Participation> {
return database.from(ParticipationsEntity).select().mapToParticipation()
}
override fun deleteParticipation(id: String): Boolean {
return database.delete(ParticipationsEntity) {
it.id eq UUID.fromString(id)
} > 0
}
}

@ -0,0 +1,59 @@
package allin.data.postgres
import allin.data.UserDataSource
import allin.dto.UserDTO
import allin.entities.UsersEntity
import allin.model.User
import org.ktorm.database.Database
import org.ktorm.dsl.*
import java.util.*
class PostgresUserDataSource(private val database: Database) : UserDataSource {
override fun getUserByUsername(username: String): Pair<UserDTO?, String?> =
database.from(UsersEntity)
.select()
.where { UsersEntity.username eq username }
.map { row ->
Pair(
UserDTO(
row[UsersEntity.id].toString(),
row[UsersEntity.username].toString(),
row[UsersEntity.email].toString(),
row[UsersEntity.nbCoins] ?: 0,
null
),
row[UsersEntity.password].toString()
)
}
.firstOrNull() ?: Pair(null, null)
override fun addUser(user: User) {
database.insert(UsersEntity) {
set(it.id, UUID.fromString(user.id))
set(it.nbCoins, user.nbCoins)
set(it.username, user.username)
set(it.password, user.password)
set(it.email, user.email)
}
}
override fun deleteUser(username: String): Boolean {
val deletedCount = database.delete(UsersEntity) {
it.username eq username
}
return deletedCount > 0
}
override fun userExists(username: String, email: String): Boolean {
return database.from(UsersEntity).select(UsersEntity.username, UsersEntity.email).where {
(UsersEntity.username eq username) and (UsersEntity.email eq email)
}.totalRecords > 0
}
override fun modifyUserCoins(username: String, amount: Int) {
database.update(UsersEntity) {
set(UsersEntity.nbCoins, UsersEntity.nbCoins - amount)
where { UsersEntity.username eq username }
}
}
}

@ -1,15 +1,11 @@
package allin.entities
import allin.database
import allin.entities.ResponsesEntity.getResponse
import allin.model.Bet
import allin.utils.Execute
import org.ktorm.dsl.*
import allin.model.BetStatus
import allin.model.BetType
import org.ktorm.entity.Entity
import org.ktorm.schema.*
import java.time.ZoneId
import org.ktorm.support.postgresql.pgEnum
import java.time.ZonedDateTime
import java.util.UUID.fromString
interface BetEntity : Entity<BetEntity> {
@ -18,6 +14,8 @@ interface BetEntity : Entity<BetEntity> {
val endRegistration: ZonedDateTime
val endBet: ZonedDateTime
val isPrivate: Boolean
val status: BetStatus
val type: BetType
val createdBy: String
}
@ -28,57 +26,7 @@ object BetsEntity : Table<BetEntity>("bet") {
val endRegistration = timestamp("endregistration")
val endBet = timestamp("endbet")
val isPrivate = boolean("isprivate")
val status = pgEnum<BetStatus>("status").bindTo { it.status }
val type = pgEnum<BetType>("type").bindTo { it.type }
val createdBy = varchar("createdby")
fun getBets(): MutableList<Bet> {
return database.from(BetsEntity).select().map {
row -> Bet(
row[id].toString(),
row[theme].toString(),
row[sentenceBet].toString(),
row[endRegistration]!!.atZone(ZoneId.of("Europe/Paris")),
row[endBet]!!.atZone(ZoneId.of("Europe/Paris")),
row[isPrivate]?: false,
getResponse(fromString(row[id].toString())),
row[createdBy].toString()
)
}.toMutableList()
}
fun getBetsNotFinished(): MutableList<Bet> {
val currentTime = ZonedDateTime.now(ZoneId.of("Europe/Paris"))
return database.from(BetsEntity)
.select()
.where { endBet greaterEq currentTime.toInstant() }
.map { row ->
Bet(
row[id].toString(),
row[theme].toString(),
row[sentenceBet].toString(),
row[endRegistration]!!.atZone(ZoneId.of("Europe/Paris")),
row[endBet]!!.atZone(ZoneId.of("Europe/Paris")),
row[isPrivate] ?: false,
getResponse(fromString(row[id].toString())),
row[createdBy].toString()
)
}.toMutableList()
}
fun createBetsTable(){
val request="CREATE TABLE IF not exists bet ( id uuid PRIMARY KEY, theme VARCHAR(255), endregistration timestamp,endbet timestamp,sentencebet varchar(500),isprivate boolean, createdby varchar(250))"
database.Execute(request)
}
fun addBetEntity(bet : Bet) {
database.insert(BetsEntity) {
set(it.id, fromString(bet.id))
set(it.endBet,bet.endBet.toInstant())
set(it.endRegistration,bet.endRegistration.toInstant())
set(it.sentenceBet,bet.sentenceBet)
set(it.theme, bet.theme)
set(it.isPrivate, bet.isPrivate)
set(it.createdBy, bet.createdBy)
}
ResponsesEntity.addResponse(bet.response,fromString(bet.id))
}
}

@ -1,12 +1,10 @@
package allin.entities
import allin.database
import allin.model.Participation
import allin.utils.Execute
import org.ktorm.dsl.*
import org.ktorm.entity.Entity
import org.ktorm.schema.*
import java.util.*
import org.ktorm.schema.Table
import org.ktorm.schema.int
import org.ktorm.schema.uuid
import org.ktorm.schema.varchar
interface ParticipationEntity : Entity<ParticipationEntity> {
val id: String
@ -23,68 +21,4 @@ object ParticipationsEntity : Table<BetEntity>("participation") {
val username = varchar("username")
val answer = varchar("answer")
val stake = int("stake")
fun createParticipationTable(){
val request="CREATE TABLE IF NOT EXISTS participation (id uuid PRIMARY KEY,bet uuid,username varchar(250),answer varchar(250),stake int);"
database.Execute(request)
}
fun addParticipationEntity(participation : Participation){
database.insert(ParticipationsEntity){
set(it.id, UUID.fromString(participation.id))
set(it.betId,UUID.fromString(participation.betId))
set(it.username,participation.username)
set(it.answer,participation.answer)
set(it.stake,participation.stake)
}
}
fun getParticipationEntityFromBetId(betid: String): MutableList<Participation> {
return database.from(ParticipationsEntity)
.select()
.where { betId eq UUID.fromString(betid) }
.map { row ->
Participation(
row[id].toString(),
row[betId].toString(),
row[username].toString(),
row[answer].toString(),
row[stake] ?: 0,
)
}.toMutableList()
}
fun getParticipationEntityFromUserId(user: String, betid: String): MutableList<Participation> {
return database.from(ParticipationsEntity)
.select()
.where { (betId eq UUID.fromString(betid)) and (username eq user) }
.map { row ->
Participation(
row[id].toString(),
row[betId].toString(),
row[username].toString(),
row[answer].toString(),
row[stake] ?: 0,
)
}.toMutableList()
}
fun getParticipationEntity(): MutableList<Participation> {
return database.from(ParticipationsEntity).select().map {
row -> Participation(
row[id].toString(),
row[betId].toString(),
row[username].toString(),
row[answer].toString(),
row[stake]?:0,
)
}.toMutableList()
}
fun deleteParticipation(participation: Participation): Boolean {
val deletedCount = database.delete(ParticipationsEntity) {
it.id eq UUID.fromString(participation.id)
}
return deletedCount > 0
}
}

@ -1,8 +1,5 @@
package allin.entities
import allin.database
import allin.utils.Execute
import org.ktorm.dsl.*
import org.ktorm.entity.Entity
import org.ktorm.schema.Table
import org.ktorm.schema.uuid
@ -10,6 +7,9 @@ import org.ktorm.schema.varchar
import java.util.*
const val YES_VALUE = "Yes"
const val NO_VALUE = "No"
interface ResponseEntity : Entity<ResponseEntity> {
val betId: UUID
val response: String
@ -18,24 +18,4 @@ interface ResponseEntity : Entity<ResponseEntity> {
object ResponsesEntity : Table<ResponseEntity>("response") {
val id = uuid("id").primaryKey()
val response = varchar("response").primaryKey()
fun createResponseTable(){
val request="CREATE TABLE IF NOT EXISTS response (id UUID,response VARCHAR(250),CONSTRAINT pk_response_id PRIMARY KEY (id,response));"
database.Execute(request)
}
fun getResponse(idBet: UUID): MutableList<String> {
return database.from(ResponsesEntity)
.select(response)
.where { id eq idBet }
.map { it[response].toString() }.toMutableList()
}
fun addResponse(responses : MutableList<String>, idBet : UUID ) {
responses.forEach {selected ->
database.insert(ResponsesEntity) {
set(it.id, idBet)
set(it.response,selected)
}
}
}
}

@ -1,16 +1,13 @@
package allin.entities
import allin.database
import allin.dto.UserDTO
import allin.model.User
import allin.utils.Execute
import allin.utils.ExecuteWithResult
import org.ktorm.database.use
import org.ktorm.dsl.*
import org.ktorm.entity.*
import org.ktorm.dsl.eq
import org.ktorm.dsl.update
import org.ktorm.entity.Entity
import org.ktorm.schema.*
import java.time.Instant.now
import java.util.UUID.fromString
import java.time.Instant
interface UserEntity : Entity<UserEntity> {
val username: String
@ -18,6 +15,7 @@ interface UserEntity : Entity<UserEntity> {
var password: String
var nbCoins: Int
}
object UsersEntity : Table<UserEntity>("utilisateur") {
val id = uuid("id").primaryKey()
val username = varchar("username")
@ -25,73 +23,11 @@ object UsersEntity : Table<UserEntity>("utilisateur") {
val nbCoins = int("coins")
val email = varchar("email")
val lastGift = timestamp("lastgift")
fun getUserToUserDTO(): MutableList<UserDTO> {
return database.from(UsersEntity).select().map {
row -> UserDTO(
row[id].toString(),
row[username].toString(),
row[email].toString(),
row[nbCoins]?:0,
null
)
}.toMutableList()
}
fun createUserTable(){
val request="CREATE TABLE IF not exists utilisateur ( id uuid PRIMARY KEY, username VARCHAR(255), password VARCHAR(255),coins numeric,email VARCHAR(255), lastgift timestamp)"
database.Execute(request)
}
fun modifyCoins(user: String, cost : Int){
val request = "UPDATE utilisateur SET coins = coins - $cost WHERE username = '$user';"
database.Execute(request)
}
fun addCoinByUsername(username: String, coins : Int){
database.update(UsersEntity){
set(nbCoins,coins+ nbCoins)
where {it.username eq username}
}
}
fun getUserByUsernameAndPassword(login: String): Pair<UserDTO?, String?> {
return database.from(UsersEntity)
.select()
.where { (username eq login) /*and (password eq passwordParam)*/ }
.map { row ->
Pair(
UserDTO(
row[id].toString(),
row[username].toString(),
row[email].toString(),
row[nbCoins] ?: 0,
null
),
row[password].toString()
)
}
.firstOrNull() ?: Pair(null, null)
}
fun addUserEntity(user : User){
database.insert(UsersEntity){
set(it.id,fromString(user.id))
set(it.nbCoins,user.nbCoins)
set(it.username,user.username)
set(it.password,user.password)
set(it.email,user.email)
set(it.lastGift,now())
}
}
fun deleteUserByUsername(username: String): Boolean {
val deletedCount = database.delete(UsersEntity) {
it.username eq username
}
return deletedCount > 0
}
/*CREATE TABLE IF not exists utilisateur ( id uuid PRIMARY KEY, username VARCHAR(255), password VARCHAR(255),coins numeric,email VARCHAR(255), lastgift timestamp)*/
/*
fun canHaveDailyGift(username: String): Boolean {
val request = "SELECT CASE WHEN NOW() - lastgift > INTERVAL '1 day' THEN true ELSE false END AS is_lastgift_greater_than_1_day FROM utilisateur WHERE username = '$username';"
val resultSet = database.ExecuteWithResult(request)
@ -101,7 +37,7 @@ object UsersEntity : Table<UserEntity>("utilisateur") {
val isDailyGift = resultSet.getBoolean("is_lastgift_greater_than_1_day")
if (isDailyGift) {
database.update(UsersEntity) {
set(lastGift, now())
set(UsersEntity.lastGift, Instant.now())
where { it.username eq username }
}
}
@ -110,8 +46,5 @@ object UsersEntity : Table<UserEntity>("utilisateur") {
}
return false
}
}
*/

@ -1,7 +1,7 @@
package allin.ext
import allin.data.UserDataSource
import allin.dto.UserDTO
import allin.entities.UsersEntity
import allin.model.ApiMessage
import io.ktor.http.*
import io.ktor.server.application.*
@ -10,15 +10,18 @@ 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<JWTPrincipal>()?.let { content(it) } ?: call.respond(HttpStatusCode.Unauthorized)
suspend fun PipelineContext<*, ApplicationCall>.verifyUserFromToken(
userDataSource: UserDataSource,
principal: JWTPrincipal,
content: suspend (user: UserDTO, password: String) -> Unit
) {
val username = principal.payload.getClaim("username").asString()
val userPassword = UsersEntity.getUserByUsernameAndPassword(username)
val userPassword = userDataSource.getUserByUsername(username)
userPassword.first?.let { content(it, userPassword.second ?: "") }
?: call.respond(HttpStatusCode.NotFound, ApiMessage.TokenUserNotFound)
}

@ -1,20 +1,21 @@
package allin.model
import allin.serializer.UUIDSerializer
import allin.model.BetStatus.IN_PROGRESS
import allin.serializer.ZonedDateTimeSerializer
import kotlinx.serialization.Serializable
import java.time.ZonedDateTime
import java.util.*
@Serializable
data class Bet(
val id: String = "",
val theme: String,
val sentenceBet: String,
val status: BetStatus = IN_PROGRESS,
val type: BetType,
@Serializable(ZonedDateTimeSerializer::class) val endRegistration: ZonedDateTime,
@Serializable(ZonedDateTimeSerializer::class) var endBet: ZonedDateTime,
var isPrivate: Boolean,
var response: MutableList<String>,
var response: List<String>,
val createdBy: String = ""
)
@ -23,5 +24,5 @@ data class UpdatedBetData(
val id: String,
@Serializable(ZonedDateTimeSerializer::class) val endBet: ZonedDateTime,
val isPrivate: Boolean,
val response: MutableList<String>
val response: List<String>
)

@ -0,0 +1,9 @@
package allin.model
enum class BetStatus {
IN_PROGRESS,
WAITING,
CLOSING,
FINISHED,
CANCELLED
}

@ -0,0 +1,7 @@
package allin.model
enum class BetType {
MATCH,
BINARY,
CUSTOM
}

@ -4,6 +4,7 @@ import io.ktor.server.application.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
fun Application.BasicRouting() {
routing {
get("/") {

@ -1,8 +1,6 @@
package allin.routing
import allin.entities.BetsEntity.getBets
import allin.entities.ParticipationsEntity.getParticipationEntityFromBetId
import allin.entities.ParticipationsEntity.getParticipationEntityFromUserId
import allin.dataSource
import allin.ext.hasToken
import allin.ext.verifyUserFromToken
import allin.model.BetDetail
@ -13,15 +11,20 @@ import io.ktor.server.auth.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
fun Application.BetDetailRouter() {
val userDataSource = this.dataSource.userDataSource
val betDataSource = this.dataSource.betDataSource
val participationDataSource = this.dataSource.participationDataSource
routing {
authenticate {
get("/betdetail/get/{id}") {
hasToken { principal ->
verifyUserFromToken(principal) { user, _ ->
verifyUserFromToken(userDataSource, principal) { user, _ ->
val id = call.parameters["id"].toString()
val participations = getParticipationEntityFromBetId(id)
val selectedBet = getBets().find { it.id == id }
val participations = participationDataSource.getParticipationFromBetId(id)
val selectedBet = betDataSource.getBetById(id)
if (selectedBet != null) {
call.respond(
HttpStatusCode.Accepted,
@ -29,7 +32,7 @@ fun Application.BetDetailRouter() {
selectedBet,
getBetAnswerDetail(selectedBet, participations),
participations.toList(),
getParticipationEntityFromUserId(user.username, id).lastOrNull()
participationDataSource.getParticipationFromUserId(user.username, id).lastOrNull()
)
)
} else {

@ -1,10 +1,6 @@
package allin.routing
import allin.entities.BetsEntity.addBetEntity
import allin.entities.BetsEntity.getBets
import allin.entities.BetsEntity.getBetsNotFinished
import allin.entities.ParticipationsEntity.getParticipationEntity
import allin.entities.ParticipationsEntity.getParticipationEntityFromUserId
import allin.dataSource
import allin.ext.hasToken
import allin.ext.verifyUserFromToken
import allin.model.ApiMessage
@ -21,7 +17,12 @@ import java.util.*
val tokenManagerBet = AppConfig.tokenManager
fun Application.BetRouter() {
val userDataSource = this.dataSource.userDataSource
val betDataSource = this.dataSource.betDataSource
val participationDataSource = this.dataSource.participationDataSource
routing {
route("/bets/add") {
authenticate {
@ -30,21 +31,11 @@ fun Application.BetRouter() {
val bet = call.receive<Bet>()
val id = UUID.randomUUID().toString()
val username = tokenManagerBet.getUsernameFromToken(principal)
val bets = getBets()
bets.find { it.id == id }?.let {
betDataSource.getBetById(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
)
addBetEntity(betWithId)
val betWithId = bet.copy(id = id, createdBy = username)
betDataSource.addBet(betWithId)
call.respond(HttpStatusCode.Created, betWithId)
}
}
@ -57,17 +48,15 @@ fun Application.BetRouter() {
route("/bets/gets") {
get {
// if(bets.size>0)
val bets= getBets()
call.respond(HttpStatusCode.Accepted, bets.toList())
call.respond(HttpStatusCode.Accepted, betDataSource.getAllBets())
// else call.respond(HttpStatusCode.NoContent)
}
}
route("/bets/get/{id}") {
get {
val bets= getBets()
val id = call.parameters["id"] ?: ""
bets.find { it.id == id }?.let { bet ->
betDataSource.getBetById(id)?.let { bet ->
call.respond(HttpStatusCode.Accepted, bet)
} ?: call.respond(HttpStatusCode.NotFound, ApiMessage.BetNotFound)
}
@ -75,35 +64,35 @@ fun Application.BetRouter() {
route("/bets/delete") {
post {
val idbet = call.receive<Map<String, String>>()["id"]
val bets= getBets()
bets.find { it.id == idbet }?.let { findbet ->
bets.remove(findbet)
call.respond(HttpStatusCode.Accepted, findbet)
} ?: call.respond(HttpStatusCode.NotFound, ApiMessage.BetNotFound)
val id = call.receive<Map<String, String>>()["id"] ?: ""
if (betDataSource.removeBet(id)) {
call.respond(HttpStatusCode.Accepted)
} else {
call.respond(HttpStatusCode.NotFound, ApiMessage.BetNotFound)
}
}
}
route("bets/update") {
post {
val updatedBetData = call.receive<UpdatedBetData>()
val bets= getBets()
bets.find { it.id == updatedBetData.id }?.let { findbet ->
findbet.endBet = updatedBetData.endBet
findbet.isPrivate = updatedBetData.isPrivate
findbet.response = updatedBetData.response
call.respond(HttpStatusCode.Accepted, findbet)
} ?: call.respond(HttpStatusCode.NotFound, ApiMessage.BetNotFound)
if (betDataSource.updateBet(updatedBetData)) {
call.respond(HttpStatusCode.Accepted)
} else {
call.respond(HttpStatusCode.NotFound, ApiMessage.BetNotFound)
}
}
}
authenticate {
get("/bets/current") {
hasToken { principal ->
verifyUserFromToken(principal) { user, _ ->
val currentBets = getBetsNotFinished()
verifyUserFromToken(userDataSource, principal) { user, _ ->
val currentBets = betDataSource.getBetsNotFinished()
.filter { bet ->
val userParticipation = getParticipationEntityFromUserId(user.username, bet.id)
userParticipation.isNotEmpty() || bet.createdBy == user.username
val userParticipation =
participationDataSource.getParticipationFromUserId(user.username, bet.id)
userParticipation.isNotEmpty()
}
call.respond(HttpStatusCode.OK, currentBets)

@ -1,9 +1,6 @@
package allin.routing
import allin.entities.ParticipationsEntity.addParticipationEntity
import allin.entities.ParticipationsEntity.deleteParticipation
import allin.entities.ParticipationsEntity.getParticipationEntity
import allin.entities.UsersEntity.modifyCoins
import allin.dataSource
import allin.ext.hasToken
import allin.ext.verifyUserFromToken
import allin.model.ApiMessage
@ -17,15 +14,20 @@ import io.ktor.server.response.*
import io.ktor.server.routing.*
import java.util.*
fun Application.ParticipationRouter() {
val userDataSource = this.dataSource.userDataSource
val participationDataSource = this.dataSource.participationDataSource
routing {
authenticate {
post("/participations/add") {
hasToken { principal ->
val participation = call.receive<ParticipationRequest>()
verifyUserFromToken(principal) { user, _ ->
verifyUserFromToken(userDataSource, principal) { user, _ ->
if (user.nbCoins >= participation.stake) {
addParticipationEntity(
participationDataSource.addParticipation(
Participation(
id = UUID.randomUUID().toString(),
betId = participation.betId,
@ -34,7 +36,9 @@ fun Application.ParticipationRouter() {
stake = participation.stake
)
)
modifyCoins(user.username,participation.stake)
userDataSource.modifyUserCoins(username = user.username, amount = participation.stake)
call.respond(HttpStatusCode.Created)
} else {
call.respond(HttpStatusCode.Forbidden, ApiMessage.NotEnoughCoins)
@ -43,14 +47,13 @@ fun Application.ParticipationRouter() {
}
}
delete("/participations/delete") {
hasToken { principal ->
hasToken {
val participationId = call.receive<String>()
getParticipationEntity().find { it.id == participationId }?.let { participation ->
verifyUserFromToken(principal) { _, _ ->
deleteParticipation(participation)
if (participationDataSource.deleteParticipation(participationId)) {
call.respond(HttpStatusCode.NoContent)
} else {
call.respond(HttpStatusCode.NotFound, ApiMessage.ParticipationNotFound)
}
} ?: call.respond(HttpStatusCode.NotFound, ApiMessage.ParticipationNotFound)
}
}
}

@ -1,11 +1,6 @@
package allin.routing
import allin.entities.UsersEntity.addCoinByUsername
import allin.entities.UsersEntity.addUserEntity
import allin.entities.UsersEntity.canHaveDailyGift
import allin.entities.UsersEntity.deleteUserByUsername
import allin.entities.UsersEntity.getUserByUsernameAndPassword
import allin.entities.UsersEntity.getUserToUserDTO
import allin.dataSource
import allin.ext.hasToken
import allin.ext.verifyUserFromToken
import allin.model.*
@ -22,8 +17,12 @@ val RegexCheckerUser = AppConfig.regexChecker
val CryptManagerUser = AppConfig.cryptManager
val tokenManagerUser = AppConfig.tokenManager
const val DEFAULT_COINS = 500
fun Application.UserRouter() {
val userDataSource = this.dataSource.userDataSource
routing {
route("/users/register") {
post {
@ -31,10 +30,10 @@ fun Application.UserRouter() {
if (RegexCheckerUser.isEmailInvalid(tempUser.email)) {
call.respond(HttpStatusCode.Forbidden, ApiMessage.InvalidMail)
}
val users = getUserToUserDTO()
users.find { it.username == tempUser.username || it.email == tempUser.email }?.let { _ ->
if (userDataSource.userExists(tempUser.username, tempUser.email)) {
call.respond(HttpStatusCode.Conflict, ApiMessage.UserAlreadyExist)
} ?: run {
}
val user = User(
id = UUID.randomUUID().toString(),
username = tempUser.username,
@ -45,16 +44,15 @@ fun Application.UserRouter() {
)
CryptManagerUser.passwordCrypt(user)
user.token = tokenManagerUser.generateOrReplaceJWTToken(user)
addUserEntity(user)
userDataSource.addUser(user)
call.respond(HttpStatusCode.Created, user)
}
}
}
route("/users/login") {
post {
val checkUser = call.receive<CheckUser>()
val user = getUserByUsernameAndPassword(checkUser.login)
val user = userDataSource.getUserByUsername(checkUser.login)
if (CryptManagerUser.passwordDecrypt(user.second ?: "", checkUser.password)) {
user.first?.let { userDtoWithToken ->
userDtoWithToken.token = tokenManagerUser.generateOrReplaceJWTToken(userDtoWithToken)
@ -69,11 +67,10 @@ fun Application.UserRouter() {
authenticate {
post("/users/delete") {
hasToken { principal ->
verifyUserFromToken(principal) { _, password ->
verifyUserFromToken(userDataSource, principal) { _, password ->
val checkUser = call.receive<CheckUser>()
if (CryptManagerUser.passwordDecrypt(password, checkUser.password)) {
if (!deleteUserByUsername(checkUser.login)) {
if (!userDataSource.deleteUser(checkUser.login)) {
call.respond(HttpStatusCode.InternalServerError, "This user can't be delete now !")
}
call.respond(HttpStatusCode.Accepted, password)
@ -87,7 +84,7 @@ fun Application.UserRouter() {
get("/users/token") {
hasToken { principal ->
verifyUserFromToken(principal) { userDto, _ ->
verifyUserFromToken(userDataSource, principal) { userDto, _ ->
call.respond(HttpStatusCode.OK, userDto)
}
}

@ -1,6 +1,5 @@
package allin.utils
import allin.database
import org.ktorm.database.Database
import java.sql.ResultSet
@ -21,8 +20,8 @@ fun Database.ExecuteWithResult(request: String): ResultSet? {
}
fun Database.Execute(request: String) {
if(!request.isNullOrEmpty())
database.useTransaction {
if (request.isNotEmpty())
this.useTransaction {
val connection = it.connection
connection.prepareStatement(request).execute()
connection.commit()

@ -0,0 +1,16 @@
package allin.utils
import kotlinx.coroutines.*
import kotlin.time.Duration
import kotlin.time.ExperimentalTime
@OptIn(DelicateCoroutinesApi::class, ExperimentalTime::class)
fun kronJob(duration: Duration, action: () -> Unit) =
GlobalScope.launch {
withContext(Dispatchers.IO) {
while (true) {
runCatching { action() }
delay(duration.toLongMilliseconds())
}
}
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save