package main
import (
"net/http"
"git.phc.dm.unipi.it/aziis98/posti-dm/server/auth"
"git.phc.dm.unipi.it/aziis98/posti-dm/server/db"
"git.phc.dm.unipi.it/aziis98/posti-dm/server/httputil"
"git.phc.dm.unipi.it/aziis98/posti-dm/server/httputil/serverevents"
"github.com/go-chi/chi/v5"
)
type Server struct {
auth * auth . AuthSessionService
roomServerEventStreams map [ string ] serverevents . Handler
Database db . Database
ApiRoute chi . Router
}
func NewServer ( ) * Server {
database := db . NewInMemoryStore ( )
server := & Server {
Database : database ,
auth : auth . NewAuthSessionService (
& simpleAuthenticator {
make ( map [ string ] string ) ,
database ,
} ,
) ,
roomServerEventStreams : make ( map [ string ] serverevents . Handler ) ,
ApiRoute : chi . NewRouter ( ) ,
}
roomIDs , _ := database . GetRooms ( )
for _ , roomID := range roomIDs {
server . roomServerEventStreams [ roomID ] = serverevents . New ( & serverevents . Config { } )
}
server . setupRoutes ( )
return server
}
func ( server * Server ) setupRoutes ( ) {
api := server . ApiRoute
database := server . Database
auth := server . auth
// FIXME: in realtà tutte le routes che interagiscono con il db dovrebbero essere in transazione con
// database.BeginTransaction()
// defer database.EndTransaction()
// Authenticated Routes
api . Post ( "/login" , func ( w http . ResponseWriter , r * http . Request ) {
var credentials struct {
Username string ` json:"username" `
Password string ` json:"password" `
}
if err := httputil . ReadJSON ( r , & credentials ) ; err != nil {
http . Error ( w , err . Error ( ) , http . StatusInternalServerError )
return
}
err := auth . Login ( w , credentials . Username , credentials . Password )
if err != nil {
http . Error ( w , err . Error ( ) , http . StatusUnauthorized )
return
}
httputil . WriteJSON ( w , "ok" )
} )
api . With ( auth . LoggedMiddleware ( ) ) .
Post ( "/logout" , func ( w http . ResponseWriter , r * http . Request ) {
auth . Logout ( w )
httputil . WriteJSON ( w , "ok" )
} )
api . Get ( "/user" , func ( w http . ResponseWriter , r * http . Request ) {
userID , err := auth . RequestUser ( r )
if err != nil {
httputil . WriteJSON ( w , nil )
return
}
user , err := database . GetUser ( userID )
if err != nil {
http . Error ( w , err . Error ( ) , http . StatusNotFound )
}
httputil . WriteJSON ( w , user )
} )
api . Get ( "/room/seats" , func ( w http . ResponseWriter , r * http . Request ) {
roomID , ok := r . URL . Query ( ) [ "id" ]
if ! ok {
http . Error ( w , ` missing room id ` , http . StatusBadRequest )
}
room , err := database . GetRoom ( roomID [ 0 ] )
if err != nil {
http . Error ( w , err . Error ( ) , http . StatusNotFound )
return
}
seats := [ ] * db . Seat { }
for _ , seatID := range room . SeatIDs {
seat , err := database . GetSeat ( seatID )
if err != nil {
http . Error ( w , err . Error ( ) , http . StatusNotFound )
return
}
seats = append ( seats , seat )
}
httputil . WriteJSON ( w , seats )
} )
api . HandleFunc ( "/room_events" , func ( w http . ResponseWriter , r * http . Request ) {
roomID , ok := r . URL . Query ( ) [ "id" ]
if ! ok {
http . Error ( w , ` missing room id ` , http . StatusBadRequest )
return
}
server . roomServerEventStreams [ roomID [ 0 ] ] . ServeHTTP ( w , r )
} )
api . With ( auth . LoggedMiddleware ( ) ) .
Post ( "/seat/occupy" , func ( w http . ResponseWriter , r * http . Request ) {
database . BeginTransaction ( )
defer database . EndTransaction ( )
userID , err := auth . RequestUser ( r )
if err != nil {
http . Error ( w , err . Error ( ) , http . StatusUnauthorized )
return
}
user , err := database . GetUser ( userID )
if err != nil {
http . Error ( w , err . Error ( ) , http . StatusInternalServerError )
return
}
seatID , ok := r . URL . Query ( ) [ "id" ]
if ! ok {
http . Error ( w , ` missing seat id ` , http . StatusBadRequest )
return
}
seat , err := database . GetSeat ( seatID [ 0 ] )
if err != nil {
http . Error ( w , err . Error ( ) , http . StatusInternalServerError )
return
}
// a seat can be occupied only if empty; simple users can occupy a seat only if they have occupied no other seat; admins and moderator (for now) can occupy even more than one seat (as occupied by an anonymous?) to fix the free seat count of the room
if len ( seat . OccupiedBy ) == 0 {
userSeats , err := database . GetUserSeats ( userID )
if err != nil {
http . Error ( w , err . Error ( ) , http . StatusInternalServerError )
return
}
if ! user . Permissions . HasAny ( db . PermissionAdmin , db . PermissionModerator ) && len ( userSeats ) > 0 {
http . Error ( w , ` you can occupy only one seat at a time ` , http . StatusUnprocessableEntity )
return
}
if err := database . OccupySeat ( seatID [ 0 ] , userID ) ; err != nil {
http . Error ( w , err . Error ( ) , http . StatusInternalServerError )
return
}
server . roomServerEventStreams [ seat . RoomID ] . Broadcast ( "refresh" )
httputil . WriteJSON ( w , "ok" )
} else {
http . Error ( w , ` seat already occupied ` , http . StatusUnprocessableEntity )
}
} )
api . With ( auth . LoggedMiddleware ( ) ) .
Post ( "/seat/leave" , func ( w http . ResponseWriter , r * http . Request ) {
userID , err := auth . RequestUser ( r )
if err != nil {
http . Error ( w , err . Error ( ) , http . StatusUnauthorized )
return
}
user , err := database . GetUser ( userID )
if err != nil {
http . Error ( w , err . Error ( ) , http . StatusInternalServerError )
return
}
seatID , ok := r . URL . Query ( ) [ "id" ]
if ! ok {
http . Error ( w , ` missing seat id ` , http . StatusBadRequest )
return
}
seat , err := database . GetSeat ( seatID [ 0 ] )
if err != nil {
http . Error ( w , err . Error ( ) , http . StatusInternalServerError )
return
}
// Check permissions, if the place is occupied then only its owner, a moderator or an admin can clear it
if len ( seat . OccupiedBy ) > 0 {
if user . ID == seat . OccupiedBy [ 0 ] || user . Permissions . HasAny ( db . PermissionAdmin , db . PermissionModerator ) {
if err := database . FreeSeat ( seatID [ 0 ] ) ; err != nil {
http . Error ( w , err . Error ( ) , http . StatusInternalServerError )
return
}
server . roomServerEventStreams [ seat . RoomID ] . Broadcast ( "refresh" )
httputil . WriteJSON ( w , "ok" )
} else {
http . Error ( w , ` seat can be freed only by its owner, a moderator or an admin ` , http . StatusUnauthorized )
}
} else {
http . Error ( w , ` seat already empty ` , http . StatusUnprocessableEntity )
}
} )
}
// func (server *Server) ConnectedToRoom(roomID string, client chan string)