UNPKG

@xrengine/server-core

Version:

Shared components for XREngine server

411 lines (373 loc) 13.1 kB
import { Paginated, Params } from '@feathersjs/feathers' import { SequelizeServiceOptions, Service } from 'feathers-sequelize' import Sequelize, { Op } from 'sequelize' import slugify from 'slugify' import { Location as LocationType } from '@xrengine/common/src/interfaces/Location' import { UserInterface } from '@xrengine/common/src/interfaces/User' import { Application } from '../../../declarations' import logger from '../../ServerLogger' import { UserParams } from '../../user/user/user.class' export type LocationDataType = LocationType export class Location<T = LocationDataType> extends Service<T> { app: Application docs: any constructor(options: Partial<SequelizeServiceOptions>, app: Application) { super(options) this.app = app } // async create (data: any, params: Params): Promise<any> { // console.log(data); // const { id } = data; // // if (id) { // await this.app.service('location').get(id).then((existingLocation: any) => { // new Promise((resolve) => // setTimeout(() => resolve(super.update(id, data, params)), 1000) // ).then((updatedLocation: any) => { // this.createInstances({ id: updatedLocation.id, instance: data.instance }).then(() => {}, () => {}); // }, (reason: any) => { // console.error(reason); // }); // }, (newLoc: any) => { // this.createNewLocation({ data, params }).then(() => {}, () => {}); // }); // } else { // this.createNewLocation({ data, params }).then(() => {}, () => {}); // } // // return 'success'; // } /** * A method which is used to create new location * * @param param0 data of new location */ async createNewLocation({ data, params }: { data: any; params: Params }): Promise<any> { await new Promise((resolve) => setTimeout(() => resolve(super.create(data, params)), 1000)).then( (updatedLocation: any) => { this.createInstances({ id: updatedLocation.id, instance: data.instance }).then( () => {}, () => {} ) }, (reason: any) => { logger.error(reason) } ) } /** * A function which is used to create new instance * * @param param0 data of instance */ async createInstances({ id, instance }: { id: any; instance: any }): Promise<void> { if (instance) { await instance.forEach((element: any) => { if (element.id) { this.app.services.instance.get(element.id).then( (existingInstance: any) => { element.locationId = id new Promise((resolve) => setTimeout(() => resolve(this.app.services.instance.update(existingInstance.id, element)), 1000) ).then( (value: any) => { logger.info(value) }, (reason: any) => { logger.error(reason) } ) }, (newIns: any) => { element.locationId = id new Promise((resolve) => setTimeout(() => resolve(this.app.services.instance.create(element)), 1000) ).then( (value: any) => { logger.info(value) }, (reasone: any) => { logger.error(reasone) } ) } ) } else { element.locationId = id new Promise((resolve) => setTimeout(() => resolve(this.app.services.instance.create(element)), 1000)).then( (value: any) => { logger.info(value) }, (reason: any) => { logger.error(reason) } ) } }) } } /** * A function which help to find and display all locations * * @param params of query with limit number and skip number * @returns {@Array} of all locations */ async find(params?: UserParams): Promise<T[] | Paginated<T>> { let { $skip, $limit, $sort, joinableLocations, adminnedLocations, search, ...strippedQuery } = params?.query ?? {} if ($skip == null) $skip = 0 if ($limit == null) $limit = 10 const order: any[] = [] if ($sort != null) Object.keys($sort).forEach((name, val) => { if (name === 'type') { order.push(['location_setting', 'locationType', $sort[name] === 0 ? 'DESC' : 'ASC']) } else if (name === 'audioEnabled') { order.push(['location_setting', 'audioEnabled', $sort[name] === 0 ? 'DESC' : 'ASC']) } else if (name === 'videoEnabled') { order.push(['location_setting', 'videoEnabled', $sort[name] === 0 ? 'DESC' : 'ASC']) } else { order.push([name, $sort[name] === 0 ? 'DESC' : 'ASC']) } }) if (joinableLocations) { const locationResult = await this.app.service('location').Model.findAndCountAll({ offset: $skip, limit: $limit, where: strippedQuery, order: order, include: [ { model: this.app.service('instance').Model, required: false, where: { currentUsers: { [Op.lt]: Sequelize.col('location.maxUsersPerInstance') }, ended: false } }, { model: this.app.service('location-settings').Model, required: false }, { model: this.app.service('location-ban').Model, required: false }, { model: this.app.service('location-authorized-user').Model, required: false } ] }) return { skip: $skip, limit: $limit, total: locationResult.count, data: locationResult.rows } } else if (adminnedLocations) { const loggedInUser = params!.user as UserInterface const include = [ { model: this.app.service('location-settings').Model, required: false }, { model: this.app.service('location-ban').Model, required: false }, { model: this.app.service('location-authorized-user').Model, require: false } ] if (!loggedInUser.scopes || !loggedInUser.scopes.find((scope) => scope.type === 'admin:admin')) { ;(include as any).push({ model: this.app.service('location-admin').Model, where: { userId: loggedInUser.id } }) } let q = {} if (search) { q = { [Op.or]: [ Sequelize.where(Sequelize.fn('lower', Sequelize.col('name')), { [Op.like]: '%' + search.toLowerCase() + '%' }), Sequelize.where(Sequelize.fn('lower', Sequelize.col('sceneId')), { [Op.like]: '%' + search.toLowerCase() + '%' }) ] } } const locationResult = await this.app.service('location').Model.findAndCountAll({ offset: $skip, limit: $limit, where: { ...strippedQuery, ...q }, order: order, include: include }) return { skip: $skip, limit: $limit, total: locationResult.count, data: locationResult.rows } } else { return super.find(params) } } /** * A function which is used to create new location * * @param data of location * @param params * @returns new location object */ async create(data: any, params?: UserParams): Promise<T> { const t = await this.app.get('sequelizeClient').transaction() try { // @ts-ignore let { location_settings, ...locationData } = data const loggedInUser = params!.user as UserInterface locationData.slugifiedName = slugify(locationData.name, { lower: true }) if (locationData.isLobby) await this.makeLobby(t, params) const location = await this.Model.create(locationData, { transaction: t }) await this.app.service('location-settings').Model.create( { videoEnabled: !!location_settings.videoEnabled, audioEnabled: !!location_settings.audioEnabled, faceStreamingEnabled: !!location_settings.faceStreamingEnabled, screenSharingEnabled: !!location_settings.screenSharingEnabled, maxUsersPerInstance: locationData.maxUsersPerInstance || 10, locationType: location_settings.locationType || 'private', locationId: location.id }, { transaction: t } ) if (loggedInUser) { await Promise.all([ this.app.service('location-admin').Model.create( { locationId: location.id, userId: loggedInUser.id }, { transaction: t } ), this.app.service('location-authorized-user').Model.create( { locationId: location.id, userId: loggedInUser.id }, { transaction: t } ) ]) } await t.commit() return location as T } catch (err) { logger.error(err) await t.rollback() if (err.errors[0].message === 'slugifiedName must be unique') { throw new Error('Name is in use.') } throw err } } /** * A function which is used to update location * * @param id of location to update * @param data of location going to be updated * @returns updated location */ async patch(id: string, data: any, params?: Params): Promise<T> { const t = await this.app.get('sequelizeClient').transaction() try { // @ts-ignore let { location_settings, ...locationData } = data location_settings ??= data['location_setting'] const old = await this.Model.findOne({ where: { id }, include: [this.app.service('location-settings').Model] }) const oldSettings = old.location_setting ?? old.location_settings if (locationData.name) locationData.slugifiedName = slugify(locationData.name, { lower: true }) if (!old.isLobby && locationData.isLobby) await this.makeLobby(t, params) await this.Model.update(locationData, { where: { id }, transaction: t }) // super.patch(id, locationData, params); await this.app.service('location-settings').Model.update( { videoEnabled: !!location_settings.videoEnabled, audioEnabled: !!location_settings.audioEnabled, faceStreamingEnabled: !!location_settings.faceStreamingEnabled, screenSharingEnabled: !!location_settings.screenSharingEnabled, maxUsersPerInstance: locationData.maxUsersPerInstance || 10, locationType: location_settings.locationType || 'private' }, { where: { id: oldSettings.id }, transaction: t } ) await t.commit() const location = await this.Model.findOne({ where: { id }, include: [this.app.service('location-settings').Model] }) return location as T } catch (err) { logger.error(err) await t.rollback() if (err.errors[0].message === 'slugifiedName must be unique') { throw new Error('That name is already in use') } throw err } } /** * A function which is used to remove location * * @param id of location which is going to be removed * @param params which contain user information * @returns {@function} of remove data */ async remove(id: string, params?: UserParams): Promise<T> { const location = await this.app.service('location').Model.findOne({ where: { isLobby: true, id: id }, attributes: ['id', 'isLobby'] }) if (location) { throw new Error("Lobby can't be deleted") } if (id != null) { const selfUser = params!.user as UserInterface const location = await this.app.service('location').get(id) if (location.locationSettingsId != null) await this.app.service('location-settings').remove(location.locationSettingsId) try { const locationAdminItems = await (this.app.service('location-admin') as any).Model.findAll({ where: { locationId: id, userId: selfUser.id ?? null } }) locationAdminItems.length && locationAdminItems.forEach(async (route) => { await this.app.service('location-admin').remove(route.dataValues.id) }) } catch (err) { logger.error(err, `Could not remove location-admin: ${err.message}`) } } return (await super.remove(id)) as T } async makeLobby(t, params?: UserParams): Promise<void> { const selfUser = params!.user as UserInterface if (!selfUser || !selfUser.scopes || !selfUser.scopes.find((scope) => scope.type === 'admin:admin')) throw new Error('Only Admin can set Lobby') await this.Model.update({ isLobby: false }, { where: { isLobby: true }, transaction: t }) } }