Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions src/jukebox/juke-session/juke-session.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,12 @@ export class JukeSessionController {
@UseGuards(RolesGuard)
@Post(':id/members/join/')
@Serialize(JukeSessionMembershipDto)
@ApiOperation({ summary: 'Add Juke Session Member' })
joinJukeSession(@Param('id', new NumberPipe('id')) id: number, @CurrentUser() user: UserDto) {
@ApiOperation({ summary: '[MEMBER] Add Juke Session Member' })
joinJukeSession(
@Param('jukebox_id', new NumberPipe('jukebox_id')) jukeboxId: number,
@Param('id', new NumberPipe('id')) id: number,
@CurrentUser() user: UserDto,
) {
return this.jukeSessionService.createMembership(id, { user_id: user.id })
}

Expand Down
63 changes: 44 additions & 19 deletions src/jukebox/jukebox.gateway.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import { CLUBS_URL, NODE_ENV } from 'src/config'
import { JukeboxService } from './jukebox.service'
import { NetworkService } from 'src/network/network.service'
import { WSExceptionFilter } from 'src/utils/filters/ws-exception.filter'
import { QueuedTrackDto } from './queue/dto'

@UseFilters(new WSExceptionFilter())
@WebSocketGateway({
Expand Down Expand Up @@ -66,10 +67,10 @@ export class JukeboxGateway implements OnGatewayInit {
)
}

const jukeboxId = client.handshake.query?.jukeboxId ?? null
const clubId = client.handshake.query?.club_id ?? null
const role = client.handshake.query?.role ?? null

if (jukeboxId == null) {
if (clubId == null) {
return next(
this.handleConnectionRejection(
client,
Expand All @@ -89,9 +90,6 @@ export class JukeboxGateway implements OnGatewayInit {
)
}

const jukebox = await this.jukeboxService.findOne(parseInt(jukeboxId as string, 10))
const clubId = jukebox.club_id

const clubs = (await this.networkService.sendRequest(
`${CLUBS_URL}/api/v1/club/clubs/${role === 'admin' ? '?is_admin=true' : ''}`,
'GET',
Expand All @@ -107,24 +105,25 @@ export class JukeboxGateway implements OnGatewayInit {
)
}

if (!!clubs.data.find((m) => m.id === clubId)) {
console.log(`[WS] Client ${client.id} authorized for jukebox ${jukeboxId} as ${role}`)
if (!!clubs.data.find((m) => m.id === parseInt(clubId as string))) {
console.log(`[WS] Client ${client.id} authorized for club ${clubId} as ${role}`)
client['role'] = role
this.server.emit('hands shaked')
return next()
} else {
return next(
this.handleConnectionRejection(
client,
`Connection rejected: user not member of jukebox ${jukeboxId}`,
'Authorization failed: you are not a member of this jukebox.',
`Connection rejected: user not member of club ${clubId}`,
'Authorization failed: you are not a member of this club.',
),
)
}
} catch (e) {
return next(
this.handleConnectionRejection(
client,
'Unexpected error during handshake',
`Unexpected error during handshake: ${e}`,
'Authorization failed: unexpected error',
),
)
Expand All @@ -137,19 +136,36 @@ export class JukeboxGateway implements OnGatewayInit {
if (client['role'] !== 'member' && client['role'] !== 'admin') {
throw new WsException('You are not authorized')
}

const jukeboxId = payload.jukebox_id.toString()
console.log('Joining ', jukeboxId)
client.join(jukeboxId)
client.to(jukeboxId).emit('player-join-success', { success: true })
const playerState = await this.playerService.getPlayerState(+jukeboxId)
console.log(playerState)
this.server.to(jukeboxId).emit('player-join-success', playerState)
}

@SubscribeMessage('player-ping')
async handlePlayerPing(@ConnectedSocket() client: Socket) {
if (client['role'] !== 'admin') {
throw new WsException('You are not authorized')
}

console.log('Player Available')
}

@SubscribeMessage('player-aux-update')
async handlePlayerAuxUpdate(client: Socket, @MessageBody() payload: PlayerAuxUpdateDto) {
async handlePlayerAuxUpdate(
@ConnectedSocket() client: Socket,
@MessageBody() payload: PlayerAuxUpdateDto,
) {
console.log('Received Player Aux Update')
if (client['role'] !== 'admin') {
throw new WsException('You are not authorized')
}

console.log(payload)
const { jukebox_id, action, progress, current_track } = payload
const { jukebox_id, action, progress, spotify_track, duration_ms } = payload
const session = await this.jukeSessionService.getCurrentSession(jukebox_id)

switch (action) {
Expand All @@ -160,21 +176,28 @@ export class JukeboxGateway implements OnGatewayInit {
this.playerService.setIsPlaying(jukebox_id, false)
break
case 'changed_tracks':
if (current_track && !current_track?.spotify_id) {
if (spotify_track && !spotify_track?.spotify_id) {
throw new WsException('Track must have a spotify id')
}

// Check if next track was next in queue, if so pop it
const nextTrack = await this.queueService.getNextTrack(session.id)
let nextTrack: QueuedTrackDto | null
try {
nextTrack = await this.queueService.getNextTrack(session.id)
} catch (err) {
if (err instanceof NotFoundException) {
nextTrack = null
} else throw new err()
}

if (nextTrack.track.spotify_id === current_track?.spotify_id) {
if (nextTrack && nextTrack.track.spotify_id === spotify_track?.spotify_id) {
// Changed track was next from queue
const track = await this.queueService.popNextTrack(session.id)
await this.playerService.setCurrentQueuedTrack(jukebox_id, track)
await this.queueService.queueNextTrackToSpotify(jukebox_id, session.id)
} else if (current_track) {
} else if (spotify_track) {
// Changed track was outside of queue
const track = await this.tracksService.getTrack(current_track.spotify_id!, jukebox_id)
const track = await this.tracksService.getTrack(spotify_track.spotify_id!, jukebox_id)
await this.playerService.setCurrentSpotifyTrack(jukebox_id, track)
}
break
Expand All @@ -187,7 +210,9 @@ export class JukeboxGateway implements OnGatewayInit {
}

const playerState = await this.playerService.getPlayerState(jukebox_id)
client.to(jukebox_id.toString()).emit('player-state-update', playerState)
const result = { ...playerState, spotify_track: { ...spotify_track, duration_ms } }
console.log(result)
this.server.to(jukebox_id.toString()).emit('player-state-update', result)
}

private handleConnectionRejection(client: Socket, loggingMessage: string, errorMessage: string) {
Expand Down
9 changes: 7 additions & 2 deletions src/jukebox/player/dto/player-aux-update.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,23 @@ export class PlayerAuxUpdateDto {
progress?: number

@IsOptional()
@IsNumber()
duration_ms?: number

@IsOptional()
@Type(() => Date)
@IsDate()
timestamp?: Date

@IsOptional()
@ValidateNested()
@Type(() => TrackDto)
current_track?: TrackDto
spotify_track?: TrackDto
}

export class PlayerJoinDto extends OmitType(PlayerAuxUpdateDto, [
'action' as const,
'progress' as const,
'timestamp' as const,
'current_track' as const,
'spotify_track' as const,
]) {}
8 changes: 5 additions & 3 deletions src/utils/filters/http-exception.filter.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { ArgumentsHost, Catch, ExceptionFilter, HttpException, Logger } from '@nestjs/common'
import { HttpArgumentsHost } from '@nestjs/common/interfaces'
import { error } from 'console'
import { Request, Response } from 'express'
import { Response } from 'express'

@Catch()
export class HttpExceptionFilter implements ExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
Logger.error(exception, exception.stack)
if (host.getType() !== 'http') {
throw exception instanceof Error ? exception : new Error(String(exception))
}

const ctx = host.switchToHttp()
const response = ctx.getResponse<Response>()
let status: number
Expand Down
10 changes: 5 additions & 5 deletions src/utils/filters/ws-exception.filter.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import { ArgumentsHost, Catch } from '@nestjs/common'
import { ArgumentsHost, Catch, InternalServerErrorException } from '@nestjs/common'
import { BaseWsExceptionFilter, WsException } from '@nestjs/websockets'

@Catch(WsException)
@Catch()
export class WSExceptionFilter extends BaseWsExceptionFilter {
catch(exception: WsException, host: ArgumentsHost) {
console.log(exception)
const client = host.switchToWs().getClient()
const data = host.switchToWs().getData()
const error = exception.getError()
const details = error instanceof Object ? { ...error } : { message: error }
const error = exception.message
const jsonResponse = JSON.stringify({
event: 'WebSocket Error',
data: {
role: client['role'] ?? 'no role',
data: data,
...details,
error,
},
})
client.emit('exception', jsonResponse)
Expand Down
2 changes: 0 additions & 2 deletions src/utils/guards/roles.guard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,6 @@ export class RolesGuard implements CanActivate {
return false
}

console.log(clubs)

return !!clubs.data.find((m) => m.id == clubId)
}
}
16 changes: 8 additions & 8 deletions test/app.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,13 +70,13 @@ describe('AppController (e2e)', () => {
.mockResolvedValueOnce({
status: 200,
description: 'test',
data: [{ id: 0, name: 'Baby' }],
data: [{ id: 1, name: 'Baby' }],
})
// For admin
.mockResolvedValueOnce({
status: 200,
description: 'test',
data: [{ id: 0, name: 'Baby' }],
data: [{ id: 1, name: 'Baby' }],
})
// For not part of club
.mockResolvedValueOnce({
Expand Down Expand Up @@ -123,7 +123,7 @@ describe('AppController (e2e)', () => {
expect(jukeboxResult.status).toBe(201)

const findAllJukeboxResult = await request(app.getHttpServer())
.get('/jukebox/jukeboxes/?clubId=0')
.get('/jukebox/jukeboxes/?club_id=0')
.set('Authorization', 'Bearer MEMBER_TOKEN')
expect(findAllJukeboxResult.status).toBe(200)

Expand Down Expand Up @@ -152,7 +152,7 @@ describe('AppController (e2e)', () => {
},
query: {
role: 'member',
jukeboxId: jukebox.id,
club_id: 1,
},
})
expect(memberSocket.connected).toBe(true)
Expand All @@ -163,7 +163,7 @@ describe('AppController (e2e)', () => {
},
query: {
role: 'admin',
jukeboxId: jukebox.id,
club_id: 1,
},
})
expect(adminSocket.connected).toBe(true)
Expand All @@ -174,7 +174,7 @@ describe('AppController (e2e)', () => {
},
query: {
role: 'admin',
jukeboxId: jukebox.id,
club_id: 1,
},
})
expect(failureSocket1.connected).toBe(false)
Expand All @@ -185,7 +185,7 @@ describe('AppController (e2e)', () => {
},
query: {
role: '',
jukeboxId: jukebox.id,
club_id: 1,
},
})
expect(failureSocket2.connected).toBe(false)
Expand All @@ -196,7 +196,7 @@ describe('AppController (e2e)', () => {
},
query: {
role: 'admin',
jukeboxId: jukebox.id,
club_id: 1,
},
})
expect(wrongPermission.connected).toBe(false)
Expand Down