1
- import { createRifa , getRifasUser , getRifa , updateRifa , decrementRifaTickets } from "../models/rifa.model.js" ;
1
+ import { createRifa , getRifasUser , getRifa , updateRifa , decrementRifaTickets , rifaModel } from "../models/rifa.model.js" ;
2
+ import { ticketModel } from "../models/ticket.models.js" ;
2
3
import admin from "../firebase.js" ;
3
4
import { supabase } from "../db.js" ;
5
+ import { getUserByUid } from "../models/user.model.js" ;
4
6
5
7
6
8
export const createRifas = async ( req , res ) => {
@@ -141,4 +143,143 @@ export const decrementRifaTicket = async (req, res) => {
141
143
res . status ( 500 ) . json ( { error : "Error interno del servidor" } ) ;
142
144
}
143
145
144
- }
146
+ }
147
+
148
+ // Función para generar un número aleatorio verificable
149
+ function generateVerifiableRandom ( seed , max ) {
150
+ // Usamos un algoritmo simple pero verificable
151
+ // En producción se puede usar un algoritmo más robusto
152
+ let hash = 0 ;
153
+ for ( let i = 0 ; i < seed . length ; i ++ ) {
154
+ hash = ( ( hash << 5 ) - hash ) + seed . charCodeAt ( i ) ;
155
+ hash |= 0 ; // Convertir a entero de 32 bits
156
+ }
157
+
158
+ // Convertimos a positivo y tomamos el módulo para el rango requerido
159
+ const positiveHash = Math . abs ( hash ) ;
160
+ return positiveHash % max ;
161
+ }
162
+
163
+ // Controlador para verificar si una rifa está lista para sorteo
164
+ export const checkRifaForDraw = async ( req , res ) => {
165
+ try {
166
+ const { rifaId } = req . params ;
167
+ const { token } = req . body ;
168
+
169
+ if ( ! token ) {
170
+ return res . status ( 401 ) . json ( { error : "Autenticación requerida" } ) ;
171
+ }
172
+
173
+ // Verificar que el usuario sea el creador de la rifa
174
+ const decoded = await admin . auth ( ) . verifyIdToken ( token ) ;
175
+ const { uid } = decoded ;
176
+
177
+ const { data : rifa } = await rifaModel . getRifa ( rifaId ) ;
178
+ if ( ! rifa || rifa . userID !== uid ) {
179
+ return res . status ( 403 ) . json ( { error : "No tienes permiso para realizar el sorteo de esta rifa" } ) ;
180
+ }
181
+
182
+ const { isReady, reason } = await rifaModel . isRifaReadyForDraw ( rifaId ) ;
183
+
184
+ return res . status ( 200 ) . json ( {
185
+ isReady,
186
+ reason : ! isReady ? reason : null ,
187
+ message : isReady ? "La rifa está lista para sorteo" : "La rifa no está lista para sorteo"
188
+ } ) ;
189
+
190
+ } catch ( error ) {
191
+ console . error ( "Error al verificar rifa para sorteo:" , error ) ;
192
+ return res . status ( 500 ) . json ( { error : "Error interno al verificar la rifa" } ) ;
193
+ }
194
+ } ;
195
+
196
+ // Controlador para realizar el sorteo
197
+ export const realizarSorteo = async ( req , res ) => {
198
+ try {
199
+ const { rifaId } = req . params ;
200
+ const { token } = req . body ;
201
+
202
+ if ( ! token ) {
203
+ return res . status ( 401 ) . json ( { error : "Autenticación requerida" } ) ;
204
+ }
205
+
206
+ // Verificar que el usuario sea el creador de la rifa
207
+ const decoded = await admin . auth ( ) . verifyIdToken ( token ) ;
208
+ const { uid } = decoded ;
209
+
210
+ const { data : rifa } = await rifaModel . getRifa ( rifaId ) ;
211
+ if ( ! rifa || rifa . userID !== uid ) {
212
+ return res . status ( 403 ) . json ( { error : "No tienes permiso para realizar el sorteo de esta rifa" } ) ;
213
+ }
214
+
215
+ // Verificar si la rifa está lista para sorteo
216
+ const { isReady, reason } = await rifaModel . isRifaReadyForDraw ( rifaId ) ;
217
+ if ( ! isReady ) {
218
+ return res . status ( 400 ) . json ( { error : reason || "Esta rifa no está lista para sorteo" } ) ;
219
+ }
220
+
221
+ // Obtener todos los tickets vendidos
222
+ const tickets = await ticketModel . getTicketsByRifaId ( rifaId ) ;
223
+
224
+ if ( ! tickets || tickets . length === 0 ) {
225
+ return res . status ( 400 ) . json ( { error : "No hay tickets vendidos para esta rifa" } ) ;
226
+ }
227
+
228
+ // Generar número aleatorio usando un seed verificable
229
+ const timestamp = Date . now ( ) ;
230
+ const seed = `${ timestamp } -${ rifaId } -${ uid } ` ;
231
+
232
+ // Seleccionar un índice aleatorio entre todos los boletos vendidos
233
+ const randomIndex = generateVerifiableRandom ( seed , tickets . length ) ;
234
+
235
+ // Seleccionar ganador
236
+ const ganador = tickets [ randomIndex ] ;
237
+
238
+ // Verificamos que el ticket tenga un número de boleto válido
239
+ if ( ! ganador . numero_boleto ) {
240
+ // Si no tiene número de boleto, generamos uno con formato correcto
241
+ ganador . numero_boleto = `#${ String ( ganador . id ) . padStart ( 4 , '0' ) } ` ;
242
+
243
+ // En un entorno real, podríamos actualizar el boleto en la base de datos
244
+ // pero para esta implementación solo lo usaremos para mostrar
245
+ }
246
+
247
+ // Actualizar estado de la rifa y guardar ganador
248
+ await rifaModel . updateRifaStatus ( rifaId , 'Cerrada' ) ;
249
+ await rifaModel . setRifaWinner ( rifaId , ganador . id_user , ganador . id ) ;
250
+
251
+
252
+ const user = await getUserByUid ( ganador . id_user ) ;
253
+
254
+ if ( ! user ) {
255
+ return res . status ( 404 ) . json ( { error : "Usuario del ganador no encontrado" } ) ;
256
+ }
257
+
258
+ const ganadorInfo = {
259
+ ticketId : ganador . id ,
260
+ ticketNumber : ganador . numero_boleto ,
261
+ userId : user . data . uid ,
262
+ nombre : user . data . name ? user . data . name : 'Usuario' ,
263
+ email : user . data . email ? user . data . email : '' ,
264
+ } ;
265
+
266
+ return res . status ( 200 ) . json ( {
267
+ success : true ,
268
+ message : 'Sorteo realizado con éxito' ,
269
+ timestamp,
270
+ seed,
271
+ randomIndex,
272
+ totalTickets : tickets . length ,
273
+ ganador : ganadorInfo ,
274
+ rifa : {
275
+ id : rifa . id ,
276
+ title : rifa . title ,
277
+ prize : rifa . prize
278
+ }
279
+ } ) ;
280
+
281
+ } catch ( error ) {
282
+ console . error ( "Error al realizar sorteo:" , error ) ;
283
+ return res . status ( 500 ) . json ( { error : "Error interno al realizar el sorteo" } ) ;
284
+ }
285
+ } ;
0 commit comments