package routes
import (
"bufio"
"encoding/json"
"fmt"
"log"
"strconv"
"time"
"github.com/aziis98/lupus-lite/events"
"github.com/aziis98/lupus-lite/lupus"
"github.com/aziis98/lupus-lite/model"
"github.com/aziis98/lupus-lite/util"
"github.com/gofiber/fiber/v2"
"github.com/valyala/fasthttp"
)
func ( s * Server ) Api ( api fiber . Router ) {
api . Get ( "/status" , func ( c * fiber . Ctx ) error {
s , err := json . MarshalIndent ( s . db , "" , " " )
if err != nil {
return err
}
log . Println ( string ( s ) )
return c . SendString ( "ok" )
} )
api . Post ( "/login" , func ( c * fiber . Ctx ) error {
var loginForm struct {
Username string ` form:"username" `
Password string ` form:"password" `
}
if err := c . BodyParser ( & loginForm ) ; err != nil {
return err
}
token , err := s . auth . Login ( loginForm . Username , loginForm . Password )
if err != nil {
return err
}
c . Cookie ( & fiber . Cookie {
Name : "sid" ,
Value : token ,
Path : "/" ,
Expires : time . Now ( ) . Add ( 3 * 24 * time . Hour ) ,
} )
return c . Redirect ( "/" )
} )
api . Post ( "/logout" , func ( c * fiber . Ctx ) error {
c . Cookie ( & fiber . Cookie {
Name : "sid" ,
Value : "" ,
Path : "/" ,
Expires : time . Now ( ) ,
} )
return c . SendString ( "ok" )
} )
api . Post ( "/register" , func ( c * fiber . Ctx ) error {
var loginForm struct {
Username string ` form:"username" `
Password string ` form:"password" `
Password2 string ` form:"password2" `
}
if err := c . BodyParser ( & loginForm ) ; err != nil {
return err
}
if err := util . ValidateUsername ( loginForm . Username ) ; err != nil {
return err
}
if err := util . ValidatePasswords ( loginForm . Password , loginForm . Password2 ) ; err != nil {
return err
}
if err := s . auth . Register ( loginForm . Username , loginForm . Password ) ; err != nil {
return err
}
return c . Redirect ( "/login" )
} )
api . Post ( "/crea-partita" , s . requireLogged , func ( c * fiber . Ctx ) error {
user := requestUser ( c )
var form struct {
NumGiocatori string ` form:"numero-giocatori" `
NumLupi string ` form:"numero-lupi" `
NumFattucchiere string ` form:"numero-fattucchiere" `
NumGuardie string ` form:"numero-guardie" `
NumCacciatori string ` form:"numero-cacciatori" `
NumMedium string ` form:"numero-medium" `
NumVeggenti string ` form:"numero-veggenti" `
}
if err := c . BodyParser ( & form ) ; err != nil {
return err
}
cfg := model . PartitaConfig {
NumeroPerRuolo : map [ string ] int { } ,
}
if err := util . AtoiInto ( form . NumGiocatori , & cfg . NumeroGiocatori ) ; err != nil {
return err
}
// Questo codice magico scorre su questa "tabella" con colonne ruolo e stringa del ruolo nel form e popola la mappa NumeroPerRuolo da uid ruolo a numero per ruolo.
for _ , r := range [ ] struct {
ruolo lupus . Ruolo
num string
} {
{ lupus . Lupo , form . NumLupi } ,
{ lupus . Fattucchiera , form . NumFattucchiere } ,
{ lupus . Guardia , form . NumGuardie } ,
{ lupus . Cacciatore , form . NumCacciatori } ,
{ lupus . Medium , form . NumMedium } ,
{ lupus . Veggente , form . NumVeggenti } ,
} {
num , err := strconv . Atoi ( r . num )
if err != nil {
return err
}
cfg . NumeroPerRuolo [ r . ruolo . Uid ] = num
}
partita , err := s . db . CreatePartita ( user . Username , cfg )
if err != nil {
return err
}
return c . JSON ( partita )
} )
api . Get ( "/user" , s . requireLogged , func ( c * fiber . Ctx ) error {
return c . JSON ( requestUser ( c ) . PublicUser ( ) )
} )
api . Get ( "/partita/:partita/joined-players" , func ( c * fiber . Ctx ) error {
partitaUid := c . Params ( "partita" )
partita , err := s . db . GetPartita ( partitaUid )
if err != nil {
return err
}
return c . JSON ( partita . Players )
} )
api . Get ( "/partita/:partita/joined-players/events" , func ( c * fiber . Ctx ) error {
partitaUid := c . Params ( "partita" )
c . Set ( "Content-Type" , "text/event-stream" )
c . Set ( "Cache-Control" , "no-cache" )
c . Set ( "Connection" , "keep-alive" )
c . Set ( "Transfer-Encoding" , "chunked" )
sse := make ( chan any , 1 )
done := make ( chan bool )
clientId := util . GenerateRandomString ( 16 )
log . Printf ( ` [%v] New SSE listener ` , clientId )
l := s . eventBus . Subscribe (
events . OnPartitaPlayerJoins ( partitaUid ) ,
func ( e interface { } ) {
sse <- e
} ,
)
go func ( ) {
<- done
close ( sse )
close ( done )
s . eventBus . Unsubscribe ( l )
} ( )
c . Context ( ) . SetBodyStreamWriter ( fasthttp . StreamWriter ( func ( w * bufio . Writer ) {
log . Printf ( "[%v] Starting communication..." , clientId )
if _ , err := fmt . Fprintf ( w , "data: initial message\n\n" ) ; err != nil {
log . Printf ( ` SSE Writer error: %v ` , err )
return
}
outer :
for {
select {
case e := <- sse :
log . Printf ( ` [%v] Event: New player joined ` , clientId )
players := e . ( [ ] string )
msg , err := json . Marshal ( players )
if err != nil {
log . Printf ( "[%v] Error %v" , clientId , err )
break outer
}
if _ , err := fmt . Fprintf ( w , "data: %s\n\n" , msg ) ; err != nil {
log . Printf ( "[%v] Error: sse writer error, %v" , clientId , err )
break outer
}
err = w . Flush ( )
log . Printf ( ` [%v] Flush result %v ` , clientId , err )
if err != nil {
log . Printf ( "[%v] Error: error while flushing, %v, Closing connection." , clientId , err )
break outer
}
case <- time . After ( 10 * time . Second ) :
break outer
}
}
log . Printf ( "[%v] Connection timeout" , clientId )
done <- true
} ) )
return nil
} )
api . Get ( "/partita/:partita/join-partita" , s . requireLogged , func ( c * fiber . Ctx ) error {
user := requestUser ( c )
partitaUid := c . Params ( "partita" )
partita , err := s . db . GetPartita ( partitaUid )
if err != nil {
return err
}
partita . Players = append ( partita . Players , user . Username )
if err := s . db . UpdatePartita ( partita ) ; err != nil {
return err
}
s . eventBus . Dispatch ( events . OnPartitaPlayerJoins ( partitaUid ) , partita . Players )
return c . SendString ( "ok" )
} )
}