Code Snippets for xCams

Performer Controller Code Samples

import { Controller, Injectable, UseGuards, Body, Post, HttpCode, HttpStatus, UsePipes, ValidationPipe, Put, Get, Param, Query, Request, UseInterceptors, Res, HttpException } from '@nestjs/common'; import { Response, Request as Req } from 'express'; import { DataResponse, PageableData, getConfig, EntityNotFoundException, QueueEventService, QueueEvent } from 'src/kernel'; import { AuthService } from 'src/modules/auth/services'; import { Roles, CurrentUser } from 'src/modules/auth/decorators'; import { UserInterceptor } from 'src/modules/auth/interceptors'; import { RoleGuard, AuthGuard } from 'src/modules/auth/guards'; import { FileUploadInterceptor, FileUploaded, FileDto } from 'src/modules/file'; import { UserDto } from 'src/modules/user/dtos'; import { FavouriteService } from 'src/modules/favourite/services'; import { SettingService } from 'src/modules/settings'; import { SETTING_KEYS } from 'src/modules/settings/constants'; import { CountryService } from 'src/modules/utils/services'; import { DELETE_FILE_TYPE, FileService, FILE_EVENT, MEDIA_FILE_CHANNEL } from 'src/modules/file/services'; import { omit } from 'lodash'; import { EXCLUDE_FIELDS } from 'src/kernel/constants'; import { AccountNotFoundxception } from 'src/modules/user/exceptions'; import { PasswordIncorrectException } from 'src/modules/auth/exceptions'; import { ApiOperation, ApiSecurity, ApiTags } from '@nestjs/swagger'; import { PerformerBroadcastSetting } from '../payloads/performer-broadcast-setting.payload'; import { PERFORMER_STATUSES } from '../constants'; import { PerformerDto, IPerformerResponse, BlockSettingDto } from '../dtos'; import { PerformerUpdatePayload, PerformerSearchPayload, PerformerStreamingStatusUpdatePayload, BlockSettingPayload, DefaultPricePayload } from '../payloads'; import { PerformerService, PerformerSearchService } from '../services'; @Injectable() @Controller('performers') @ApiTags('Performer') export class PerformerController { constructor( private readonly authService: AuthService, private readonly performerService: PerformerService, private readonly performerSearchService: PerformerSearchService, private readonly favoriteService: FavouriteService, private readonly settingService: SettingService, private readonly countryService: CountryService, private readonly fileService: FileService, private readonly queueEventService: QueueEventService ) {} @Get('/me') @HttpCode(HttpStatus.OK) // @Roles('performer') // @UseGuards(RoleGuard) async me(@Request() request: Req): Promise<DataResponse<IPerformerResponse>> { const jwtToken = request.headers.authorization; const performer = await this.authService.getSourceFromJWT(jwtToken); if (!performer || performer.status !== PERFORMER_STATUSES.ACTIVE) { throw new HttpException('Unauthorized', HttpStatus.UNAUTHORIZED); } const result = await this.performerService.getDetails( performer._id, jwtToken ); return DataResponse.ok(new PerformerDto(result).toResponse(true)); } @Get('/search') @HttpCode(HttpStatus.OK) @UseInterceptors(UserInterceptor) @UsePipes(new ValidationPipe({ transform: true })) async usearch( @Query() req: PerformerSearchPayload, @CurrentUser() user: UserDto, @Request() request: Req ): Promise<DataResponse<PageableData<IPerformerResponse>>> { const query = { ...req }; // only query activated performer, sort by online time query.status = PERFORMER_STATUSES.ACTIVE; let ipClient = request.headers['x-forwarded-for'] || request.connection.remoteAddress; ipClient = Array.isArray(ipClient) ? ipClient.toString() : ipClient; if (ipClient.substr(0, 7) === '::ffff:') { ipClient = ipClient.substr(7); } // const ipClient = '115.75.211.252'; const whiteListIps = ['127.0.0.1', '0.0.0.1']; let userCountry = null; let countryCode = null; if (whiteListIps.indexOf(ipClient) === -1) { userCountry = await this.countryService.findCountryByIP(ipClient); if ( userCountry && userCountry.status === 'success' && userCountry.countryCode ) { countryCode = userCountry.countryCode; } } const data = await this.performerSearchService.advancedSearch( query, user, countryCode ); return DataResponse.ok({ total: data.total, data: data.data }); } @Put('/') @Roles('performer') @UseGuards(RoleGuard) @ApiSecurity('authorization') @ApiOperation({ summary: 'Update Performer'}) async updatePerformer( @CurrentUser() currentPerformer: PerformerDto, @Body() payload: PerformerUpdatePayload, @Request() request: Req ): Promise<DataResponse<IPerformerResponse>> { await this.performerService.update( currentPerformer._id, omit(payload, EXCLUDE_FIELDS) ); const performer = await this.performerService.getDetails( currentPerformer._id, request.headers.authorization ); return DataResponse.ok(new PerformerDto(performer).toResponse(true)); } @Get('/:username/view') @UseInterceptors(UserInterceptor) @HttpCode(HttpStatus.OK) async getDetails( @Param('username') performerUsername: string, @Request() req: Req, @CurrentUser() user: UserDto ): Promise<DataResponse<Partial<PerformerDto>>> { let ipClient = req.headers['x-forwarded-for'] || req.connection.remoteAddress; ipClient = Array.isArray(ipClient) ? ipClient.toString() : ipClient; if (ipClient.substr(0, 7) === '::ffff:') { ipClient = ipClient.substr(7); } // const ipClient = '115.75.211.252'; const whiteListIps = ['127.0.0.1', '0.0.0.1']; let userCountry = null; let countryCode = null; if (whiteListIps.indexOf(ipClient) === -1) { userCountry = await this.countryService.findCountryByIP(ipClient); if ( userCountry && userCountry.status === 'success' && userCountry.countryCode ) { countryCode = userCountry.countryCode; } } const performer = await this.performerService.findByUsername( performerUsername, countryCode, user ); if (!performer || performer.status !== PERFORMER_STATUSES.ACTIVE) { throw new EntityNotFoundException(); } if (user) { const favorite = await this.favoriteService.findOne({ favoriteId: performer._id, ownerId: user._id }); if (favorite) performer.isFavorite = true; } const [defaultGroupChatPrice, defaultC2CPrice] = await Promise.all([ this.settingService.getKeyValue(SETTING_KEYS.GROUP_CHAT_DEFAULT_PRICE) || 0, this.settingService.getKeyValue(SETTING_KEYS.PRIVATE_C2C_PRICE) || 0 ]); performer.privateCallPrice = typeof performer.privateCallPrice !== 'undefined' ? performer.privateCallPrice : defaultC2CPrice; performer.groupCallPrice = typeof performer.groupCallPrice !== 'undefined' ? performer.groupCallPrice : defaultGroupChatPrice; return DataResponse.ok(performer.toPublicDetailsResponse()); } @Post('/documents/upload') @HttpCode(HttpStatus.OK) @Roles('performer') @UseGuards(RoleGuard) @UseInterceptors( FileUploadInterceptor('performer-document', 'file', { destination: getConfig('file').documentDir }) ) async uploadPerformerDocument( @FileUploaded() file: FileDto, @CurrentUser() performer: PerformerDto, @Request() request: Req ): Promise<any> { return DataResponse.ok({ ...file, url: `${file.getUrl()}?documentId=${file._id}&token=${ request.headers.authorization }` }); } @Post('/release-form/upload') @HttpCode(HttpStatus.OK) @Roles('performer') @UseGuards(RoleGuard) @UseInterceptors( FileUploadInterceptor('performer-release-form', 'file', { destination: getConfig('file').documentDir }) ) async uploadPerformerReleaseForm( @FileUploaded() file: FileDto, @CurrentUser() performer: PerformerDto, @Request() request: Req ): Promise<any> { return DataResponse.ok({ ...file, url: `${file.getUrl()}?documentId=${file._id}&token=${ request.headers.authorization }` }); } @Post('/avatar/upload') @HttpCode(HttpStatus.OK) @Roles('performer') @UseGuards(RoleGuard) @UseInterceptors( FileUploadInterceptor('avatar', 'avatar', { destination: getConfig('file').avatarDir, generateThumbnail: true, replaceWithThumbail: true, thumbnailSize: getConfig('image').avatar }) ) async uploadPerformerAvatar( @FileUploaded() file: FileDto, @CurrentUser() performer: PerformerDto ): Promise<any> { await this.performerService.updateAvatar(performer._id, file); await this.fileService.addRef(file._id, { itemId: performer._id, itemType: 'performer-avatar' }); await this.queueEventService.publish( new QueueEvent({ channel: MEDIA_FILE_CHANNEL, eventName: FILE_EVENT.FILE_RELATED_MODULE_UPDATED, data: { type: DELETE_FILE_TYPE.FILEID, currentFile: performer.avatarId, newFile: file._id } }) ); return DataResponse.ok({ ...file, url: file.getUrl() }); } @Post('/streaming-status/update') @HttpCode(HttpStatus.OK) @Roles('performer') @UseGuards(RoleGuard) @UsePipes(new ValidationPipe({ transform: true })) async updateStreamingStatus( @CurrentUser() currentPerformer: PerformerDto, @Body() payload: PerformerStreamingStatusUpdatePayload ): Promise<DataResponse<IPerformerResponse>> { await this.performerService.updateSteamingStatus( currentPerformer._id, payload.status || '' ); const performer = await this.performerService.findById( currentPerformer._id ); return DataResponse.ok(new PerformerDto(performer).toResponse(true)); } @Post('/default-price/update') @HttpCode(HttpStatus.OK) @Roles('performer') @UseGuards(RoleGuard) @UsePipes(new ValidationPipe({ transform: true })) async updateDefaultPrice( @CurrentUser() currentPerformer: PerformerDto, @Body() payload: DefaultPricePayload ): Promise<DataResponse<IPerformerResponse>> { await this.performerService.updateDefaultPrice( currentPerformer._id, payload ); const performer = await this.performerService.findById( currentPerformer._id ); return DataResponse.ok(new PerformerDto(performer).toResponse(true)); } @Post('/broadcast-setting/update') @HttpCode(HttpStatus.OK) @Roles('performer') @UseGuards(RoleGuard) @UsePipes(new ValidationPipe({ transform: true })) async updateBroadcastSetting( @CurrentUser() currentPerformer: PerformerDto, @Body() payload: PerformerBroadcastSetting ): Promise<DataResponse<IPerformerResponse>> { await this.performerService.updateBroadcastSetting( currentPerformer._id, payload ); const performer = await this.performerService.findById( currentPerformer._id ); return DataResponse.ok(new PerformerDto(performer).toResponse(true)); } @Get('/documents/auth/check') @HttpCode(HttpStatus.OK) async checkAuth( @Request() request: Req, @Res() response: Response ): Promise<Response> { if (!request.query.token) { return response.status(HttpStatus.UNAUTHORIZED).send(); } const user = await this.authService.getSourceFromJWT( request.query.token as string ); if (!user) { return response.status(HttpStatus.UNAUTHORIZED).send(); } const valid = await this.performerService.checkAuthDocument(request, user); return response .status(valid ? HttpStatus.OK : HttpStatus.UNAUTHORIZED) .send(); } }

Performer Details / Live Page Code Samples

/* eslint-disable camelcase */ import React, { PureComponent } from 'react'; import Head from 'next/head'; import { Row, Col, Button, message, ButtonProps } from 'antd'; import { connect } from 'react-redux'; import { IPerformer, IUser } from 'src/interfaces'; import { messageService, streamService } from 'src/services'; import { LivePublisher } from '@components/streaming/publisher'; import { SocketContext, Event } from 'src/socket'; import { getStreamConversationSuccess, loadStreamMessages, resetStreamMessage, resetAllStreamMessage, resetStreamConversation } from '@redux/stream-chat/actions'; import { updateStreamingStatus } from '@redux/performer/actions'; import { WEBRTC_ADAPTOR_INFORMATIONS } from 'src/antmedia/constants'; import ChatBox from '@components/stream-chat/chat-box'; import UpdateSatusForm from '@components/performer/streaming-status-update-form'; import Router from 'next/router'; import { getResponseError } from '@lib/utils'; import './index.less'; // eslint-disable-next-line no-shadow enum EVENT_NAME { ROOM_INFORMATIOM_CHANGED = 'public-room-changed', USER_LEFT_ROOM = 'USER_LEFT_ROOM' } interface P { resetStreamMessage: Function; resetAllStreamMessage: Function; getStreamConversationSuccess: Function; loadStreamMessages: Function; updateStreamingStatus: Function; activeConversation: any; performer: IPerformer; updating: boolean; updateSuccess: boolean; updateError: any; resetStreamConversation: Function; } interface S { loading: boolean; fetching: boolean; initialized: boolean; publish_started: boolean; total: number; members: IUser[]; } class PerformerLivePage extends PureComponent<P, S> { static authenticate = true; private publisherRef: any; private btnRef: React.RefObject<HTMLElement> = React.createRef(); private socket: SocketIOClient.Socket; constructor(props: P) { super(props); this.state = { loading: false, fetching: false, initialized: false, publish_started: false, total: 0, members: [] }; } componentDidMount() { this.socket = this.context; this.joinPublicRoom(); window.addEventListener('beforeunload', this.onbeforeunload); Router.events.on('routeChangeStart', this.onbeforeunload); } componentDidUpdate(prevProps: P) { const { updateSuccess, updateError } = this.props; if (prevProps.updateSuccess !== updateSuccess && updateSuccess) { message.success('Update Status Success.'); } if (prevProps.updateError !== updateError && updateError) { message.error(getResponseError(updateError)); } } componentWillUnmount() { window.removeEventListener('beforeunload', this.onbeforeunload); Router.events.off('routeChangeStart', this.onbeforeunload); } handler({ total, members, conversationId }) { const { activeConversation } = this.props; if (activeConversation?.data?._id === conversationId) { this.setState({ total, members }); } } handleUpdateStatusForm(data) { const { updateStreamingStatus: dispatchUpdateStreamingStatus } = this.props; dispatchUpdateStreamingStatus(data); } onbeforeunload = () => { this.leavePublicRoom(); }; start() { this.publisherRef && this.publisherRef.start(); } stop() { const { initialized, publish_started } = this.state; if (!initialized || !publish_started) { window.location.reload(); return; } if (window.confirm('Are you sure want to stop this live streaming!')) { window.location.reload(); } } async callback(info: WEBRTC_ADAPTOR_INFORMATIONS) { const { activeConversation } = this.props; if (activeConversation && activeConversation.data) { this.socket = this.context; if (info === WEBRTC_ADAPTOR_INFORMATIONS.INITIALIZED) { this.setState({ initialized: true }); try { this.setState({ loading: true }); const resp = await streamService.goLive(); this.publisherRef && this.publisherRef.publish(resp.data.sessionId); } catch (e) { const error = await Promise.resolve(e); message.error(getResponseError(error)); this.setState({ loading: false }); } } else if (info === WEBRTC_ADAPTOR_INFORMATIONS.PUBLISH_STARTED) { this.setState({ publish_started: true, loading: false }); this.socket.emit('public-stream/live', { conversationId: activeConversation.data._id }); } else if (info === WEBRTC_ADAPTOR_INFORMATIONS.PUBLISH_FINISHED) { this.setState({ loading: false, publish_started: false }); } else if (info === WEBRTC_ADAPTOR_INFORMATIONS.CLOSED) { this.setState({ loading: false, initialized: false, publish_started: false }); } } } async joinPublicRoom() { const { loadStreamMessages: dispatchLoadStreamMessages, getStreamConversationSuccess: dispatchGetStreamConversationSuccess } = this.props; try { this.setState({ fetching: true }); const resp = await streamService.goLive(); const { conversation } = resp.data; if (conversation && conversation._id) { // this.publisherRef && this.publisherRef.start(); dispatchGetStreamConversationSuccess({ data: conversation }); dispatchLoadStreamMessages({ conversationId: conversation._id, limit: 25, offset: 0, type: conversation.type }); this.socket = this.context; this.socket && this.socket.emit('public-stream/join', { conversationId: conversation._id }); } } catch (e) { const error = await Promise.resolve(e); message.error(getResponseError(error)); } finally { this.setState({ fetching: false }); } } leavePublicRoom() { const { activeConversation, resetStreamMessage: dispatchResetStreamMessage, resetStreamConversation: dispatchResetStreamConversation } = this.props; if (activeConversation && activeConversation.data) { const conversation = { ...activeConversation.data }; this.socket && this.socket.emit('public-stream/leave', { conversationId: conversation._id }); dispatchResetStreamMessage(); dispatchResetStreamConversation(); } if (this.btnRef.current) { this.btnRef.current.remove(); } } userLeftRoomHandle({ username, conversationId }) { const { activeConversation } = this.props; if (activeConversation?.data?._id === conversationId) { const { total, members } = this.state; this.setState({ total: total - 1, members: members.filter((m) => m.username !== username) }); } } async removeAllMessage() { const { activeConversation, performer, resetAllStreamMessage: dispatchResetAllMessage } = this.props; if ( !activeConversation.data || performer._id !== activeConversation.data.performerId ) { return; } try { if (!window.confirm('Are you sure you want to remove chat history?')) { return; } await messageService.deleteAllMessageInConversation( activeConversation.data._id ); dispatchResetAllMessage({ conversationId: activeConversation.data._id }); } catch (e) { const error = await Promise.resolve(e); message.error(getResponseError(error)); } } render() { const { performer, activeConversation, updating } = this.props; const { loading, initialized, publish_started, members, total, fetching } = this.state; const btnProps: ButtonProps & React.RefAttributes<HTMLElement> = { ref: this.btnRef, loading, disabled: fetching, block: true }; if (initialized && publish_started) { btnProps.type = 'text'; btnProps.style = { marginBottom: 10, background: 'black', color: 'white' }; btnProps.children = 'Stop broadcasting'; btnProps.onClick = this.stop.bind(this); } else { btnProps.type = 'primary'; btnProps.style = { marginBottom: 10 }; btnProps.children = 'Start broadcasting'; btnProps.onClick = this.start.bind(this); } return ( <> <Head> <title>Go Live</title> </Head> <Event event={EVENT_NAME.ROOM_INFORMATIOM_CHANGED} handler={this.handler.bind(this)} /> <Event event={EVENT_NAME.USER_LEFT_ROOM} handler={this.userLeftRoomHandle.bind(this)} /> <Row> <Col xs={24} sm={24} md={12}> <UpdateSatusForm status={performer.streamingTitle} updating={updating} submit={this.handleUpdateStatusForm.bind(this)} /> <Button {...btnProps} /> <LivePublisher ref={(ref) => { this.publisherRef = ref; }} onChange={this.callback.bind(this)} configs={{ debug: true, bandwidth: 900, localVideoId: 'publisher' }} /> </Col> <Col xs={24} sm={24} md={12}> <ChatBox {...this.props} members={members} totalParticipant={total} currentPerformer={performer} /> {activeConversation?.data && ( <div style={{ margin: '10px' }}> <Button type="primary" onClick={this.removeAllMessage.bind(this)} > Clear message history </Button> </div> )} </Col> </Row> </> ); } } PerformerLivePage.contextType = SocketContext; const mapStateToProps = (state) => ({ performer: state.performer.current, updating: state.performer.updating, updateSuccess: state.performer.updateSuccess, updateError: state.performer.updateError, activeConversation: state.streamMessage.activeConversation }); const mapDispatchs = { updateStreamingStatus, getStreamConversationSuccess, loadStreamMessages, resetStreamMessage, resetAllStreamMessage, resetStreamConversation }; export default connect(mapStateToProps, mapDispatchs)(PerformerLivePage);