Created
July 9, 2025 03:34
-
-
Save kevinah95/d27dcfcedb2529bdb741fde09634666e to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package io.github.laChanchita | |
import com.google.auth.oauth2.GoogleCredentials | |
import com.google.cloud.firestore.Firestore | |
import com.google.cloud.firestore.FirestoreOptions | |
import com.google.firebase.FirebaseApp | |
import com.google.firebase.FirebaseOptions | |
import com.google.firebase.cloud.FirestoreClient | |
import com.google.firebase.database.DataSnapshot | |
import com.google.firebase.database.DatabaseError | |
import com.google.firebase.database.DatabaseReference | |
import com.google.firebase.database.FirebaseDatabase | |
import com.google.firebase.database.ValueEventListener | |
import io.github.laChanchita.model.Mejenga | |
import io.github.laChanchita.model.Mejenguero | |
import io.github.laChanchita.model.Venue | |
//import io.github.laChanchita.server.borrarMejanga | |
import io.github.laChanchita.server.cargarTodasLasMejengas | |
import io.github.laChanchita.server.cargarTodosLosMejengueros | |
import io.github.laChanchita.server.loadAllVenues | |
import io.ktor.http.HttpStatusCode | |
import io.ktor.serialization.kotlinx.json.json | |
import io.ktor.server.application.Application | |
import io.ktor.server.application.install | |
import io.ktor.server.engine.embeddedServer | |
import io.ktor.server.netty.Netty | |
import io.ktor.server.plugins.contentnegotiation.ContentNegotiation | |
import io.ktor.server.request.receive | |
import io.ktor.server.response.respond | |
import io.ktor.server.response.respondText | |
import io.ktor.server.routing.delete | |
import io.ktor.server.routing.get | |
import io.ktor.server.routing.post | |
import io.ktor.server.routing.put | |
import io.ktor.server.routing.routing | |
import kotlinx.coroutines.CoroutineScope | |
import kotlinx.coroutines.launch | |
import kotlinx.coroutines.suspendCancellableCoroutine | |
import kotlinx.serialization.json.Json | |
import java.io.File | |
import java.io.FileInputStream | |
import kotlin.coroutines.resume | |
import kotlin.coroutines.resumeWithException | |
val json = Json { | |
prettyPrint = true | |
encodeDefaults = true // ✅ ¡esto es esencial! | |
} | |
fun main() { | |
embeddedServer(Netty, port = SERVER_PORT, host = "0.0.0.0", module = Application::module) | |
.start(wait = true) | |
} | |
fun Application.module() { | |
lateinit var app: FirebaseApp | |
lateinit var defaultDatabase: FirebaseDatabase | |
lateinit var rootRef: DatabaseReference | |
lateinit var venuesRef: DatabaseReference | |
val venueListener = object : ValueEventListener { | |
override fun onDataChange(snapshot: DataSnapshot) { | |
val venueList = mutableListOf<Venue>() | |
for (venueSnapshot in snapshot.children) { | |
// Each venueSnapshot represents a single venue object | |
val venue = venueSnapshot.getValue(Venue::class.java) | |
if (venue != null) { | |
// You might want to set the ID from the key if it's not part of your data | |
val venueWithId = venue.copy(id = venueSnapshot.key ?: "") | |
venueList.add(venueWithId) | |
} | |
} | |
// Now you have your list of Venue objects | |
println("Venues retrieved: $venueList") | |
// Update your UI, process data, etc. | |
} | |
override fun onCancelled(error: DatabaseError) { | |
// Handle error | |
println("Failed to read venues: ${error.message}") | |
} | |
} | |
install(ContentNegotiation){ | |
json() | |
val refreshToken = FileInputStream("key/lacanchita-fc-firebase-adminsdk-fbsvc-fca28ea9ba.json") | |
val options = FirebaseOptions.builder() | |
.setCredentials(GoogleCredentials.fromStream(refreshToken)) | |
.setDatabaseUrl("https://lacanchita-fc-default-rtdb.firebaseio.com/") | |
.build() | |
app = FirebaseApp.initializeApp(options); | |
defaultDatabase = FirebaseDatabase.getInstance(app); | |
rootRef = defaultDatabase.reference | |
val dataRef: DatabaseReference = defaultDatabase.getReference("data") | |
val specificUserRef: DatabaseReference = defaultDatabase.getReference("data").child("venues") | |
FirestoreClient.getFirestore() | |
val db = FirestoreClient.getFirestore() | |
venuesRef = defaultDatabase.getReference("data").child("venues") | |
// venuesRef.addValueEventListener(venueListener) | |
println(dataRef); | |
} | |
fun fetchVenuesOnce(scope: CoroutineScope) { | |
scope.launch { | |
// try { | |
// val snapshot: DataSnapshot = venuesRef.get().await() | |
// val venueList = mutableListOf<Venue>() | |
// for (venueSnapshot in snapshot.children) { | |
// val venue = venueSnapshot.getValue(Venue::class.java) | |
// if (venue != null) { | |
// val venueWithId = venue.copy(id = venueSnapshot.key ?: "") | |
// venueList.add(venueWithId) | |
// } | |
// } | |
// println("Venues retrieved (coroutine): $venueList") | |
// // Process your data here | |
// } catch (e: Exception) { | |
// println("Failed to read venues (coroutine): ${e.message}") | |
// } | |
} | |
} | |
routing { | |
get("/") { | |
try { | |
val venueList = venuesRef.awaitSingleValue<Venue>() | |
call.respondText("Ktor: ${Greeting().greet()} ${venueList}}") | |
} catch (e: Exception) { | |
call.respond(HttpStatusCode.InternalServerError, "Error fetching venues: ${e.message}") | |
} | |
} | |
//VENUES | |
get("/venues") { | |
val venues = loadAllVenues(); | |
println(">> VENUES: $venues") | |
call.respond(venues) | |
} | |
//CANCHAS | |
get("/canchas") { | |
val venues = loadAllVenues(); | |
val canchas = venues.flatMap { it.canchas ?: emptyList() } | |
canchas.forEach { | |
println("Cancha: ${it.name}") | |
} | |
println(">> CANCHAS: $canchas") | |
call.respond(canchas) | |
} | |
//MEJENGAS | |
post("/mejenga") { | |
val mejenga = call.receive<Mejenga>() | |
//guardarMejanga(mejenga) | |
println(">> MEJENGA RECIBIDA: $mejenga") | |
call.respondText("Mejenga recibida con éxito") | |
} | |
put("/mejenga") { | |
try { | |
val mejengaEditada = call.receive<Mejenga>() | |
val lista = cargarTodasLasMejengas().toMutableList() | |
val index = lista.indexOfFirst { it.id == mejengaEditada.id } | |
if (index != -1) { | |
// lista[index] = mejengaEditada | |
// archivo.writeText(json.encodeToString(lista)) | |
//guardarMejanga(mejengaEditada) | |
call.respond(HttpStatusCode.OK, mapOf("message" to "Mejenga actualizada")) | |
} else { | |
call.respond(HttpStatusCode.NotFound, mapOf("error" to "Mejenga no encontrada")) | |
} | |
} catch (e: Exception) { | |
println(">> ERROR editando mejenga: ${e.message}") | |
call.respond(HttpStatusCode.BadRequest, mapOf("error" to "Datos inválidos")) | |
} | |
} | |
get("/mejengas") { | |
val lista = cargarTodasLasMejengas() | |
call.respond(lista) | |
} | |
delete("/mejenga/{id}") { | |
val id = call.parameters["id"] | |
if (id != null) { | |
println("🗑️ Borrando mejenga con id: $id") | |
// Aquí podés eliminarla de una base de datos o lista temporal | |
// if (id != null && borrarMejanga(id)) { | |
// call.respond(HttpStatusCode.OK, "Mejenga $id borrada") | |
// } else { | |
// call.respond(HttpStatusCode.NotFound, "No se encontró") | |
// } | |
} else { | |
call.respond(HttpStatusCode.BadRequest, "Falta el ID") | |
} | |
} | |
//MEJENGUEROS | |
get("/mejengueros") { | |
val lista = cargarTodosLosMejengueros() | |
call.respond(lista) | |
} | |
get("/mejengueros/random/{cantidad}") { | |
val cantidad = call.parameters["cantidad"]?.toIntOrNull() ?: return@get call.respond( | |
HttpStatusCode.BadRequest, | |
mapOf("error" to "Cantidad inválida") | |
) | |
val archivoMejengueros = File("data/mejengueros.json") | |
if (!archivoMejengueros.exists()) { | |
return@get call.respond(HttpStatusCode.NotFound, mapOf("error" to "No hay mejengueros disponibles")) | |
} | |
val lista = json.decodeFromString<List<Mejenguero>>(archivoMejengueros.readText()) | |
val seleccionados = lista.shuffled().take(cantidad) | |
call.respond(seleccionados) | |
} | |
} | |
} | |
suspend inline fun <reified T : Any> DatabaseReference.awaitSingleValue(): List<T> { | |
return suspendCancellableCoroutine { continuation -> | |
val listener = object : ValueEventListener { | |
override fun onDataChange(snapshot: DataSnapshot) { | |
try { | |
val values = snapshot.children.mapNotNull { child -> | |
child.getValue(T::class.java)?.let { | |
// A bit of reflection to create the object with id. | |
try { | |
val idProperty = it::class.java.getDeclaredField("id") | |
idProperty.isAccessible = true | |
idProperty.set(it, child.key) | |
} catch (e: NoSuchFieldException) { | |
// Ignore if the class doesn't have an 'id' field. | |
} | |
it | |
} | |
} | |
continuation.resume(values) | |
} catch (e: Exception) { | |
continuation.resumeWithException(e) | |
} | |
} | |
override fun onCancelled(error: DatabaseError) { | |
continuation.resumeWithException(error.toException()) | |
} | |
} | |
addListenerForSingleValueEvent(listener) | |
continuation.invokeOnCancellation { | |
removeEventListener(listener) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment