@xrengine/server-core
Version:
Shared components for XREngine server
411 lines (373 loc) • 13.1 kB
text/typescript
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 })
}
}