Compare commits

..

8 Commits

Author SHA1 Message Date
0e061c9eca ripristino plugin CallLogging
permette di stampare nei log la risposta HTTP inviata da Ktor
2025-03-27 17:35:19 +01:00
7b8eaa6261 rimozione plugin CallLogging
non utilizzato (usiamo logback)
2025-03-27 17:04:17 +01:00
7f6e1fc6cb gestione rolling logs
di default ogni file di log è giornaliero e può essere al max 100MB. Il totale dei files di log può essere di 5GB e i files verranno cancellati ogni 15 giorni
2025-03-27 16:08:25 +01:00
8a934dca5b modifica path autenticazione
pulizia codice
2025-03-20 13:00:16 +01:00
23733caca9 modifica nome folder properties
usato nome generico backend_api
2025-03-19 09:17:31 +01:00
b49577804e porta server su file esterno 2025-03-18 15:48:08 +01:00
8242c78b35 file config esterno
sul file di configurazione esterno vengono mappate le proprietà legate alla connessione al database
2025-03-05 11:57:27 +01:00
74570dc330 configurazione build
task buildFatJar Ktor
aggiunta file per il logging dell'applicazione (file logFile.log creato nella stessa folder di esecuzione dell'applicazione)
2025-03-05 09:43:57 +01:00
17 changed files with 72 additions and 279 deletions

1
.gitignore vendored
View File

@ -41,3 +41,4 @@ bin/
### Mac OS ###
.DS_Store
.idea/.name
*.log

View File

@ -9,6 +9,10 @@ plugins {
kotlin("plugin.serialization") version "1.9.23" // Aggiungi il plugin di Serialization
}
application {
mainClass.set("eu.maiora.ApplicationKt")
}
group = "org.maiora"
version = "1.0-SNAPSHOT"

View File

@ -1,5 +1,5 @@
plugins {
id("org.gradle.toolchains.foojay-resolver-convention") version "0.5.0"
}
rootProject.name = "Backend_API_DSU"
rootProject.name = "Backend_API"

View File

@ -9,21 +9,26 @@ import io.ktor.server.engine.*
import io.ktor.server.netty.*
import io.ktor.server.plugins.callloging.*
import io.ktor.server.plugins.cors.routing.*
import java.io.FileInputStream
import java.util.*
fun main() {
embeddedServer(Netty, port = 8098, host = "0.0.0.0", module = Application::module)
.start(wait = true)
val properties = loadConfig()
val port = properties.getProperty("server.port").toInt()
embeddedServer(Netty, port = port, host = "0.0.0.0") {
module(properties)
}.start(wait = true)
}
fun Application.module() {
fun Application.module(configFile: Properties) {
val config = ApplicationConfig("application.conf")
val dbUrl = config.property("ktor.database.url").getString()
val username = config.property("ktor.database.username").getString()
val password = config.property("ktor.database.password").getString()
val dbUrl = configFile.getProperty("ktor.database.url")
val username = configFile.getProperty("ktor.database.username")
val password = configFile.getProperty("ktor.database.password")
val secret = config.property("ktor.jwt.secret").getString()
configureDatabases(dbUrl, username, password)
configureSecurity(secret)
configureRouting(dbUrl, username, password)
configureRouting()
configureSerialization()
install(CallLogging)
@ -39,3 +44,10 @@ fun Application.module() {
allowMethod(HttpMethod.Delete)
}
}
fun loadConfig(): Properties {
val properties = Properties()
val inputStream = FileInputStream("/home/backend_api/config.properties")
properties.load(inputStream)
return properties
}

View File

@ -1,22 +1,15 @@
package eu.maiora.db
import com.fasterxml.jackson.databind.deser.impl.CreatorCandidate.Param
import eu.maiora.model.Accounts
import eu.maiora.model.Movimenti
import eu.maiora.model.Parametri
import eu.maiora.model.Tessere
import kotlinx.coroutines.Dispatchers
import org.jetbrains.exposed.dao.IntEntity
import org.jetbrains.exposed.dao.IntEntityClass
import org.jetbrains.exposed.dao.LongEntity
import org.jetbrains.exposed.dao.LongEntityClass
import org.jetbrains.exposed.dao.id.EntityID
import org.jetbrains.exposed.dao.id.IdTable
import org.jetbrains.exposed.sql.Transaction
import org.jetbrains.exposed.sql.javatime.datetime
import org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransaction
import java.time.format.DateTimeFormatter
object AccountsTable : IdTable<Int>("accounts"){
override val id = integer("id").entityId()
@ -34,34 +27,6 @@ object ParametriTable : IdTable<Int>("parametri"){
override val primaryKey = PrimaryKey(id)
}
object TessereTable : IdTable<Long>("view_tessere_api"){
override val id = long("id").entityId()
val idUtente = long("id_utente")
val codiceFiscale = varchar("codice_fiscale", 255)
val numero = varchar("numero", 255)
val saldo = double("saldo")
val punti = integer("punti")
override val primaryKey = PrimaryKey(id)
}
object MovimentiTable : IdTable<Long>("view_movimenti_api"){
override val id = long("id").entityId()
val idTessera = long("id_tessera")
val numero = varchar("numero_tessera", 255)
val operazione = varchar("operazione", 255)
val dataMovimento = datetime("data_movimento")
val transazione = varchar("transazione", 255)
val dispositivo = varchar("dispositivo", 255)
val riferimento= varchar("riferimento", 255)
val saldoPre = double("saldo_pre")
val importo = double("importo")
val saldoPost = double("saldo_post")
val puntiPre = integer("punti_pre")
val punti = integer("punti")
val puntiPost = integer("punti_post")
}
class AccountsDAO(id: EntityID<Int>) :IntEntity(id) {
companion object : IntEntityClass<AccountsDAO>(AccountsTable)
@ -76,34 +41,6 @@ class ParametriDAO(id: EntityID<Int>) :IntEntity(id) {
var valore by ParametriTable.valore
}
class TessereDao(id: EntityID<Long>) :LongEntity(id) {
companion object : LongEntityClass<TessereDao>(TessereTable)
var idUtente by TessereTable.idUtente
var codiceFiscale by TessereTable.codiceFiscale
var numero by TessereTable.numero
var saldo by TessereTable.saldo
var punti by TessereTable.punti
}
class MovimentiDao(id: EntityID<Long>) :LongEntity(id) {
companion object : LongEntityClass<MovimentiDao>(MovimentiTable)
var idTessera by MovimentiTable.idTessera
var numero by MovimentiTable.numero
var operazione by MovimentiTable.operazione
var dataMovimento by MovimentiTable.dataMovimento
var transazione by MovimentiTable.transazione
var dispositivo by MovimentiTable.dispositivo
var riferimento by MovimentiTable.riferimento
var saldoPre by MovimentiTable.saldoPre
var importo by MovimentiTable.importo
var saldoPost by MovimentiTable.saldoPost
var puntiPre by MovimentiTable.puntiPre
var punti by MovimentiTable.punti
var puntiPost by MovimentiTable.puntiPost
}
fun accountsDaoToModel(dao: AccountsDAO) = Accounts(
dao.id.value,
@ -117,40 +54,6 @@ fun parametriDaoToModel(dao: ParametriDAO) = Parametri(
dao.valore
)
fun tessereDaoToModel(dao: TessereDao) = Tessere(
dao.id.value,
dao.idUtente,
dao.codiceFiscale,
dao.numero,
dao.saldo,
dao.punti
)
fun movimentiDaoToModel(dao: MovimentiDao) :Movimenti{
val formatter = DateTimeFormatter.ofPattern("ddMMyyyy HH:mm")
val formattedDate = dao.dataMovimento.format(formatter)
return Movimenti(
dao.id.value,
dao.idTessera,
dao.numero,
dao.operazione,
formattedDate,
dao.transazione,
dao.dispositivo,
dao.riferimento,
dao.saldoPre,
dao.importo,
dao.saldoPost,
dao.puntiPre,
dao.punti,
dao.puntiPost
)
}
suspend fun <T> suspendTransaction(block: Transaction.() -> T): T =
newSuspendedTransaction(Dispatchers.IO, statement = block)

View File

@ -1,22 +0,0 @@
package eu.maiora.model
import kotlinx.serialization.Serializable
@Serializable
data class Movimenti(
val id: Long,
val idTessera: Long,
val numero: String,
val operazione: String,
val dataMovimento: String,
val transazione: String,
val dispositivo: String,
val riferimento: String,
val saldoPre: Double,
val importo: Double,
val saldoPost: Double,
val puntiPre: Int,
val punti: Int,
val puntiPost: Int
)

View File

@ -1,5 +0,0 @@
package eu.maiora.model
interface MovimentiRepository {
suspend fun movimentiByIdTessera(idTessera : Long): List<Movimenti>?
}

View File

@ -1,15 +0,0 @@
package eu.maiora.model
import eu.maiora.db.*
import org.jetbrains.exposed.sql.SortOrder
class MovimentiRepositoryImpl : MovimentiRepository {
override suspend fun movimentiByIdTessera(idTessera : Long): List<Movimenti> = suspendTransaction {
// Cerca la lista di movimenti
MovimentiDao.find { MovimentiTable.idTessera eq idTessera }
.orderBy(MovimentiTable.dataMovimento to SortOrder.DESC)
.toList() // Restituisce la lista dei movimenti
.map { movimentiDaoToModel(it) } // Converte il DAO in un oggetto Movimenti
}
}

View File

@ -1,13 +0,0 @@
package eu.maiora.model
import kotlinx.serialization.Serializable
@Serializable
data class Tessere(
val id: Long,
val idUtente : Long,
val codiceFiscale : String,
val numero : String,
val saldo : Double,
val punti : Int
)

View File

@ -1,5 +0,0 @@
package eu.maiora.model
interface TessereRepository {
suspend fun tesseraByCodiceFiscale(cf : String): Tessere?
}

View File

@ -1,13 +0,0 @@
package eu.maiora.model
import eu.maiora.db.*
class TessereRepositoryImpl : TessereRepository {
override suspend fun tesseraByCodiceFiscale(cf: String): Tessere? = suspendTransaction {
// Cerca tessere in base al codice fiscale
TessereDao.find { TessereTable.codiceFiscale eq cf }
.singleOrNull() // Restituisce un singolo risultato o null se non trovato
?.let { tessereDaoToModel(it) } // Converte il DAO in un oggetto Tessere
}
}

View File

@ -1,29 +1,17 @@
package eu.maiora.plugins
//import eu.maiora.model.LogScriptRepositoryImpl
//import eu.maiora.routes.analizzaURLRoute
//import eu.maiora.routes.eseguiScriptSQLRoute
//import eu.maiora.routes.logScriptRouting
import eu.maiora.model.AccountsRepositoryImpl
import eu.maiora.model.MovimentiRepositoryImpl
import eu.maiora.model.ParametriRepositoryImpl
import eu.maiora.model.TessereRepositoryImpl
import eu.maiora.routes.auth
import eu.maiora.routes.movimenti
import eu.maiora.routes.tessere
import io.ktor.server.application.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
//fun Application.configureRouting(dbUrl : String, username : String, password : String, repository : LogScriptRepositoryImpl ) {
fun Application.configureRouting(dbUrl : String, username : String, password : String) {
fun Application.configureRouting() {
routing {
get("/") {
call.respondText("Hello World!")
}
auth(AccountsRepositoryImpl())
tessere(TessereRepositoryImpl())
movimenti(MovimentiRepositoryImpl())
}
}

View File

@ -16,7 +16,7 @@ import java.util.*
fun Route.auth(accountsRepository: AccountsRepositoryImpl) {
route("/auth") {
route("/api/auth") {
post() {
// Riceve il body della richiesta e lo deserializza in ReceivedResponse
val receivedResponse = try {
@ -31,7 +31,7 @@ fun Route.auth(accountsRepository: AccountsRepositoryImpl) {
logger.info(
"param: " +
receivedResponse.param
);
)
// Decodifica la stringa da Base64 a oggetto Credentials
val decodedBytes = Base64.getDecoder().decode(receivedResponse.param)

View File

@ -1,36 +0,0 @@
package eu.maiora.routes
import eu.maiora.model.MovimentiRepositoryImpl
import io.ktor.http.*
import io.ktor.server.application.*
import io.ktor.server.auth.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
fun Route.movimenti(movimentiRepository: MovimentiRepositoryImpl){
route("/api/movimenti"){
authenticate("auth-jwt") {
get("{idTessera}"){
// Ottieni l'id della tessera dal percorso
val idTessera = call.parameters["idTessera"]
if (idTessera == null) {
call.respondText("ID tessera non valido", status = HttpStatusCode.BadRequest)
return@get
}
// Cerca la tessera per codice fiscale
val listaMovimenti = movimentiRepository.movimentiByIdTessera(idTessera.toLong())
if (listaMovimenti != null) {
call.respond(listaMovimenti)
} else {
call.respondText("Movimenti non trovati", status = HttpStatusCode.NotFound)
}
}
}
}
}

View File

@ -1,35 +0,0 @@
package eu.maiora.routes
import eu.maiora.model.TessereRepositoryImpl
import io.ktor.http.*
import io.ktor.server.application.*
import io.ktor.server.auth.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
fun Route.tessere(tessereRepository: TessereRepositoryImpl){
route("/api/tessere"){
authenticate("auth-jwt") {
get("{cf}"){
// Ottieni il codice fiscale dal percorso
val cf = call.parameters["cf"]
if (cf == null) {
call.respondText("Codice fiscale non valido", status = HttpStatusCode.BadRequest)
return@get
}
// Cerca la tessera per codice fiscale
val tessera = tessereRepository.tesseraByCodiceFiscale(cf)
if (tessera != null) {
call.respond(tessera)
} else {
call.respondText("Tessera non trovata", status = HttpStatusCode.NotFound)
}
}
}
}
}

View File

@ -1,18 +1,4 @@
ktor {
database {
; url = "jdbc:postgresql://192.168.20.49:5432/caritas"
; username = "caritas"
; password = "caritas"
; driver = "org.postgresql.Driver"
driver = "com.microsoft.sqlserver.jdbc.SQLServerDriver"
url = "jdbc:sqlserver://192.168.20.45;databaseName=EP_FAER;integratedSecurity=false;encrypt=true;trustServerCertificate=true;"
username = "SA"
password = "I5fz9l1a"
;driver = "oracle.jdbc.OracleDriver"
;url = "jdbc:oracle:thin:@//192.168.20.101:1521/SIR"
;username = "EP_DONORIONE"
;password = "ep_donorione"
}
jwt {
# secret per JWT generato partendo dalla stringa '?Backend_API*06022025!' codificato in Base64
secret = "P0JhY2tlbmRfQVBJKjA2MDIyMDI1IQ=="

View File

@ -0,0 +1,43 @@
<configuration>
<!-- Appender per la console -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!-- Appender per il file di log con rotazione basata su tempo e dimensione -->
<appender name="ROLLING_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>./log/logFile.log</file> <!-- File di log principale -->
<append>true</append>
<!-- RollingPolicy per dimensione e tempo -->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- Pattern per il nome dei file ruotati: include la data -->
<FileNamePattern>./log/logFile.%d{yyyy-MM-dd}.%i.log</FileNamePattern> <!-- %i è il numero di file generato -->
<!-- Limita la dimensione del file a 100MB -->
<maxFileSize>100MB</maxFileSize> <!-- Ruota il file quando raggiunge 100MB -->
<!-- Conserva i log per due settimane -->
<maxHistory>15</maxHistory> <!-- Limita a 15 giorni i log archiviati -->
<!-- Limita la dimensione totale dei file di log a 5GB -->
<totalSizeCap>5GB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>%d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!-- Configurazione del livello di log -->
<root level="DEBUG">
<appender-ref ref="ROLLING_FILE"/>
<appender-ref ref="STDOUT"/>
</root>
<logger name="io.netty" level="INFO"/>
</configuration>