diff --git a/certs/edumeet-demo-cert.pem b/certs/edumeet-demo-cert.pem new file mode 100644 index 0000000..b850c91 --- /dev/null +++ b/certs/edumeet-demo-cert.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDWjCCAkKgAwIBAgIUGbtKs0LwPm1IGy6bD+WJIocLL7QwDQYJKoZIhvcNAQEL +BQAwZjELMAkGA1UEBhMCRVUxEDAOBgNVBAgMB2VkdW1lZXQxDTALBgNVBAcMBGRl +bW8xEDAOBgNVBAoMB2VkdW1lZXQxEDAOBgNVBAsMB2VkdW1lZXQxEjAQBgNVBAMM +CWxvY2FsaG9zdDAgFw0yMjEyMjExMTA5MjVaGA8yMjk2MTAwNDExMDkyNVowZjEL +MAkGA1UEBhMCRVUxEDAOBgNVBAgMB2VkdW1lZXQxDTALBgNVBAcMBGRlbW8xEDAO +BgNVBAoMB2VkdW1lZXQxEDAOBgNVBAsMB2VkdW1lZXQxEjAQBgNVBAMMCWxvY2Fs +aG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALHtbL07xpr0pCJR +eXZZ/Br/Zw2KoiyclqQ7LDBv6LSYmSf8phAKQhTb8LT1+D+rQdELtZB25JTZNpZ8 +zsDL4yycAx2WpkLeonAcE8zxv95ou5qnYupzXbKSpXJVt9NaNlhEovoiXMECFP1V +wGweAsfy/8v6peC/8LiqUN4GmIKqtSA+mhr1Gulk6zLgKInY6RUc5dQ3A88eBVbJ +ERHbOBzmKbcbgTVnk+pClYM8BDbuJKX2DnNrcWsHDNFHbi2oWmCSKED6wKndXGmg +mD+lsS+Xrj4b0KVNYa/Nky/MYwlTMRxE7O6+B31c1iOldqJgVCIU1psRZr2o1m6R +mxMnwKkCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAjMdc9HZh30jI2IFMEL+dwFSM +0FCuDwQi7fa5XW1h/6r3s5aMEBzOU1aN/+eJBLiea1Jjh+Yvjlwsfd11mS6ZQFvn +lDM9WBD6q8wVduqZv+TlYJM2JWC8ryn8fpYx1msIoNAQbPFVlyRZkh0YTtu0QUUk +MgIZSe0SfshhlYjBZ78Fug9G8me3a7u/5N6hGQA20oMW4K1XBEWpD6BpgULoVNXB ++D4E+SiKHwFoVvksyHovv1a/kVbJiywyjF/yXQ/ZhlKlblN92RpZVbuOAsTWqh0F +7Y5mEm+T8UxXzRPP3U1qgLlMeRfZgtnEmD+XbkwHBZWg/BFrWv9QTzqfXyedKg== +-----END CERTIFICATE----- diff --git a/certs/edumeet-demo-key.pem b/certs/edumeet-demo-key.pem new file mode 100644 index 0000000..d77dc22 --- /dev/null +++ b/certs/edumeet-demo-key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCx7Wy9O8aa9KQi +UXl2Wfwa/2cNiqIsnJakOywwb+i0mJkn/KYQCkIU2/C09fg/q0HRC7WQduSU2TaW +fM7Ay+MsnAMdlqZC3qJwHBPM8b/eaLuap2Lqc12ykqVyVbfTWjZYRKL6IlzBAhT9 +VcBsHgLH8v/L+qXgv/C4qlDeBpiCqrUgPpoa9RrpZOsy4CiJ2OkVHOXUNwPPHgVW +yRER2zgc5im3G4E1Z5PqQpWDPAQ27iSl9g5za3FrBwzRR24tqFpgkihA+sCp3Vxp +oJg/pbEvl64+G9ClTWGvzZMvzGMJUzEcROzuvgd9XNYjpXaiYFQiFNabEWa9qNZu +kZsTJ8CpAgMBAAECggEBALBqTKrEHgN+FU/BoMDsL79FBRS0JGA/Uk28LfBIxV5q +/1BafPziburnuB5005OtZjCzf26hs7qyNCG51fXrMnAEsEIiIO+kd0LZU/icqjUH +exu4Y6c6g+iO5mQbAY0ZoVCbnUyNzZHBUrZs08FfcDp4gY/qEZZqOW+fz1aVC5ph +pxbLRF8hWHxYkGh+OajQYdasC9eYu3ZUa6zGfQhO1sF4arT0mfpz7s6BmfyGnXNO +ekpV6CcmbeMUh+BVTReS1JV3XlT1RV3Ftq6Q+U9zNTihXMKYM+9HzJXcIH4J7Eqn +IQAJDJeh3SxMUfz0qkyuuyuBnbjjYrUHKxhy3v4RaZECgYEA6UaC9vgnyKoI90id +3HwtZ/T6S/rWKSbT226j/f23EPCDOnkK8M2yQxjM+GbqAhtwFuycjx1RDikWe5jH +kFRgppVl7Stdhbsh90Fb332/8EAC6j3g4jxzHBXePW5+RqrXL0m9ugTA3OHAq0s3 +dxjvQjD5C7wF+bHaPBW1qJS9aXcCgYEAw0KhGldNoU1dUiHUWx9b4Yp7sNOj0QhS +2scXHvcpV2DFiTcaIkFsQOBlpfFgItCbfAwlHxgE97LTUr/04pKqagape1DKJiqW +UCplsHuAWAPePjGpU6U9sar7piW91K41vY8atqt26jmd2DOpfX2mbfrD+uzobXA+ +3gxHqP3Wrt8CgYEA04fhElqMmOW3I6VxoJHqCq6WmXdn5AEVc5Gsm5EKBA5UsR05 +0opz72v50fZglHCU//SnMw9+hLy1anZr4HDjA6vNtXbxY2cBsdhOmybIee9RtV9P +IVd3eixKTqJ3V1Vrs7HGnXvZ93Gu6KVYMmMl8nu+Genx8S5ZmsqE482cxJkCgYBO +Hdrr+d9x18ys7iHR7sLIgamofZM9LnJPWnUSi1k7vthoY+YNrFTNw7iG4gFPDFwY +DZfQT8C1BBLtOSSUfI626kFlt1WYXVNTcRLLvy0CiVGhWibe8u2ypbAVsWW3r5aK +ZrUOPZzOagyPEWQOc3Vg7ID5Y0w9dDmChjEpF5yKywKBgAc2gm5PLn8RH32kNfko +J+jYKkPEH1MXAVSpCDU8RF983qFJJy7MuuctEMOAOcyIeGWth7JJtqWLp5DFO5xi +wwqpQCO0kV4+BxOUO4osZFiuICeWo9nlXqHdxDiloInv5lhc9EarCdGD6w7I2F3r +zBCyXQk0+UBCzit6t1JKKOge +-----END PRIVATE KEY----- diff --git a/migrations/20230118072156_initial.ts b/migrations/20230118072156_initial.ts index 16fc616..9b51603 100644 --- a/migrations/20230118072156_initial.ts +++ b/migrations/20230118072156_initial.ts @@ -2,6 +2,8 @@ import { Knex } from 'knex'; import bcrypt from 'bcryptjs'; export async function up(knex: Knex): Promise { + + await knex.schema.createTable('tenants', (table) => { table.increments('id'); table.string('name'); @@ -111,15 +113,14 @@ export async function up(knex: Knex): Promise { table.unique([ 'roleId', 'permissionId' ], { useConstraint: true }); }); - await knex.schema.createTable('rooms', (table) => { + await knex.schema.createTable('roomSettings', (table) => { table.increments('id'); table.string('name'); table.string('description'); table.bigint('createdAt'); table.bigint('updatedAt'); - table.bigint('creatorId').references('id').inTable('users'); - table.bigint('tenantId').references('id').inTable('tenants').onDelete('CASCADE'); - table.bigint('defaultRoleId').references('id').inTable('roles'); + table.integer('tenantId').references('id').inTable('tenants').onDelete('CASCADE'); + table.integer('ownerId').references('id').inTable('users').onDelete('CASCADE'); table.string('logo'); table.string('background'); @@ -154,6 +155,19 @@ export async function up(knex: Knex): Promise { table.boolean('screenSharingSimulcast'); table.string('screenSharingResolution'); table.integer('screenSharingFramerate'); + }); + + await knex.schema.createTable('rooms', (table) => { + table.increments('id'); + table.string('name'); + table.string('description'); + table.bigint('createdAt'); + table.bigint('updatedAt'); + table.bigint('creatorId').references('id').inTable('users'); + table.bigint('tenantId').references('id').inTable('tenants').onDelete('CASCADE'); + table.bigint('defaultRoleId').references('id').inTable('roles'); + table.bigint('roomSettingsId').references('id').inTable('roomSettings'); + table.unique([ 'tenantId', 'name' ], { useConstraint: true }); }); @@ -210,8 +224,7 @@ export async function up(knex: Knex): Promise { await knex.schema.createTable('mediaNodes', (table) => { table.increments('id'); - table.bigint('createdAt'); - table.bigint('updatedAt'); + table.string('description'); table.string('hostname'); table.integer('port'); table.string('secret'); @@ -246,4 +259,4 @@ export async function down(knex: Knex): Promise { await knex.schema.dropTable('trackers'); await knex.schema.dropTable('locations'); await knex.schema.dropTable('mediaNodes'); -} \ No newline at end of file +} diff --git a/migrations/202310138072158_migrate.ts b/migrations/202310138072158_migrate.ts deleted file mode 100644 index 703456f..0000000 --- a/migrations/202310138072158_migrate.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { Knex } from 'knex'; -export async function up(knex: Knex): Promise { - /* - EXAMPLE UPDATE/migration file - await knex.schema.alterTable('rooms', (table) => { - table.bigint('defaultRoleId').references('id').inTable('roles'); - table.string('videoCodec'); - table.boolean('simulcast'); - table.string('videoResolution'); - table.integer('videoFramerate'); - table.string('audioCodec'); - table.boolean('autoGainControl'); - table.boolean('echoCancellation'); - table.boolean('noiseSuppression'); - table.integer('sampleRate'); - table.integer('channelCount'); - table.integer('sampleSize'); - table.boolean('opusStereo'); - table.boolean('opusDtx'); - table.boolean('opusFec'); - table.integer('opusPtime'); - table.integer('opusMaxPlaybackRate'); - table.string('screenSharingCodec'); - table.boolean('screenSharingSimulcast'); - table.string('screenSharingResolution'); - table.integer('screenSharingFramerate'); - }); */ -} -export async function down(knex: Knex): Promise { - /* await knex.schema.dropTable('users'); - await knex.schema.dropTable('groups'); - await knex.schema.dropTable('groupUsers'); - await knex.schema.dropTable('tenants'); - await knex.schema.dropTable('tenantOwners'); - await knex.schema.dropTable('tenantAdmins'); - await knex.schema.dropTable('tenantFQDNs'); - await knex.schema.dropTable('tenantOAuths'); - await knex.schema.dropTable('roles'); - await knex.schema.dropTable('permissions'); - await knex.schema.dropTable('rolePermissions'); - await knex.schema.dropTable('rooms'); - await knex.schema.dropTable('roomOwners'); - await knex.schema.dropTable('roomGroupRoles'); - await knex.schema.dropTable('roomUserRoles'); - await knex.schema.dropTable('recorders'); - await knex.schema.dropTable('trackers'); - await knex.schema.dropTable('locations'); - await knex.schema.dropTable('mediaNodes'); */ -} \ No newline at end of file diff --git a/migrations/20240220213036_media-nodes.ts b/migrations/20240220213036_media-nodes.ts new file mode 100644 index 0000000..4107e99 --- /dev/null +++ b/migrations/20240220213036_media-nodes.ts @@ -0,0 +1,14 @@ +// For more information about this file see https://dove.feathersjs.com/guides/cli/knexfile.html +import type { Knex } from 'knex' + +export async function up(knex: Knex): Promise { + await knex.schema.createTable('mediaNodes', (table) => { + table.increments('id') + + table.string('text') + }) +} + +export async function down(knex: Knex): Promise { + await knex.schema.dropTable('mediaNodes') +} diff --git a/migrations/20240229193330_room-settings.ts b/migrations/20240229193330_room-settings.ts new file mode 100644 index 0000000..4ac49bb --- /dev/null +++ b/migrations/20240229193330_room-settings.ts @@ -0,0 +1,14 @@ +// For more information about this file see https://dove.feathersjs.com/guides/cli/knexfile.html +import type { Knex } from 'knex' + +export async function up(knex: Knex): Promise { + await knex.schema.createTable('roomSettings', (table) => { + table.increments('id') + + table.string('text') + }) +} + +export async function down(knex: Knex): Promise { + await knex.schema.dropTable('roomSettings') +} diff --git a/src/client.ts b/src/client.ts index 9765ee5..f66d8f1 100644 --- a/src/client.ts +++ b/src/client.ts @@ -1,103 +1,127 @@ // For more information about this file see https://dove.feathersjs.com/guides/cli/client.html -import { feathers } from '@feathersjs/feathers'; -import type { TransportConnection, Application } from '@feathersjs/feathers'; -import authenticationClient from '@feathersjs/authentication-client'; -import type { AuthenticationClientOptions } from '@feathersjs/authentication-client'; +import { feathers } from '@feathersjs/feathers' +import type { TransportConnection, Application } from '@feathersjs/feathers' +import authenticationClient from '@feathersjs/authentication-client' +import type { AuthenticationClientOptions } from '@feathersjs/authentication-client' -import { tenantClient } from './services/tenants/tenants.shared'; -export type { Tenant, TenantData, TenantQuery, TenantPatch } from './services/tenants/tenants.shared'; +import { roomSettingsClient } from './services/roomSettings/roomSettings.shared' +export type { + RoomSettings, + RoomSettingsData, + RoomSettingsQuery, + RoomSettingsPatch +} from './services/roomSettings/roomSettings.shared' -import { tenantOwnerClient } from './services/tenantOwners/tenantOwners.shared'; +import { mediaNodesClient } from './services/mediaNodes/mediaNodes.shared' export type { - TenantOwner, - TenantOwnerData, - TenantOwnerQuery, - TenantOwnerPatch -} from './services/tenantOwners/tenantOwners.shared'; + MediaNodes, + MediaNodesData, + MediaNodesQuery, + MediaNodesPatch +} from './services/mediaNodes/mediaNodes.shared' -import { tenantOAuthClient } from './services/tenantOAuths/tenantOAuths.shared'; +import { mediaNodesClient } from './services/mediaNodes/mediaNodes.shared' export type { - TenantOAuth, - TenantOAuthData, - TenantOAuthQuery, - TenantOAuthPatch -} from './services/tenantOAuths/tenantOAuths.shared'; + MediaNodes, + MediaNodesData, + MediaNodesQuery, + MediaNodesPatch +} from './services/mediaNodes/mediaNodes.shared' + +import { tenantClient } from './services/tenants/tenants.shared' +export type { Tenant, TenantData, TenantQuery, TenantPatch } from './services/tenants/tenants.shared' -import { tenantFqdnClient } from './services/tenantFQDNs/tenantFQDNs.shared'; +import { tenantOwnerClient } from './services/tenantOwners/tenantOwners.shared' export type { - TenantFqdn, - TenantFqdnData, - TenantFqdnQuery, - TenantFqdnPatch -} from './services/tenantFQDNs/tenantFQDNs.shared'; + TenantOwner, + TenantOwnerData, + TenantOwnerQuery, + TenantOwnerPatch +} from './services/tenantOwners/tenantOwners.shared' -import { tenantAdminClient } from './services/tenantAdmins/tenantAdmins.shared'; +import { tenantOAuthClient } from './services/tenantOAuths/tenantOAuths.shared' export type { - TenantAdmin, - TenantAdminData, - TenantAdminQuery, - TenantAdminPatch -} from './services/tenantAdmins/tenantAdmins.shared'; + TenantOAuth, + TenantOAuthData, + TenantOAuthQuery, + TenantOAuthPatch +} from './services/tenantOAuths/tenantOAuths.shared' -import { roomUserRoleClient } from './services/roomUserRoles/roomUserRoles.shared'; +import { tenantFqdnClient } from './services/tenantFQDNs/tenantFQDNs.shared' export type { - RoomUserRole, - RoomUserRoleData, - RoomUserRoleQuery, - RoomUserRolePatch -} from './services/roomUserRoles/roomUserRoles.shared'; + TenantFqdn, + TenantFqdnData, + TenantFqdnQuery, + TenantFqdnPatch +} from './services/tenantFQDNs/tenantFQDNs.shared' -import { roomClient } from './services/rooms/rooms.shared'; -export type { Room, RoomData, RoomQuery, RoomPatch } from './services/rooms/rooms.shared'; +import { tenantAdminClient } from './services/tenantAdmins/tenantAdmins.shared' +export type { + TenantAdmin, + TenantAdminData, + TenantAdminQuery, + TenantAdminPatch +} from './services/tenantAdmins/tenantAdmins.shared' -import { roomOwnerClient } from './services/roomOwners/roomOwners.shared'; +import { roomUserRoleClient } from './services/roomUserRoles/roomUserRoles.shared' export type { - RoomOwner, - RoomOwnerData, - RoomOwnerQuery, - RoomOwnerPatch -} from './services/roomOwners/roomOwners.shared'; + RoomUserRole, + RoomUserRoleData, + RoomUserRoleQuery, + RoomUserRolePatch +} from './services/roomUserRoles/roomUserRoles.shared' + +import { roomClient } from './services/rooms/rooms.shared' +export type { Room, RoomData, RoomQuery, RoomPatch } from './services/rooms/rooms.shared' -import { roomGroupRoleClient } from './services/roomGroupRoles/roomGroupRoles.shared'; +import { roomOwnerClient } from './services/roomOwners/roomOwners.shared' export type { - RoomGroupRole, - RoomGroupRoleData, - RoomGroupRoleQuery, - RoomGroupRolePatch -} from './services/roomGroupRoles/roomGroupRoles.shared'; + RoomOwner, + RoomOwnerData, + RoomOwnerQuery, + RoomOwnerPatch +} from './services/roomOwners/roomOwners.shared' -import { roleClient } from './services/roles/roles.shared'; -export type { Role, RoleData, RoleQuery, RolePatch } from './services/roles/roles.shared'; +import { roomGroupRoleClient } from './services/roomGroupRoles/roomGroupRoles.shared' +export type { + RoomGroupRole, + RoomGroupRoleData, + RoomGroupRoleQuery, + RoomGroupRolePatch +} from './services/roomGroupRoles/roomGroupRoles.shared' + +import { roleClient } from './services/roles/roles.shared' +export type { Role, RoleData, RoleQuery, RolePatch } from './services/roles/roles.shared' -import { rolePermissionClient } from './services/rolePermissions/rolePermissions.shared'; +import { rolePermissionClient } from './services/rolePermissions/rolePermissions.shared' export type { - RolePermission, - RolePermissionData, - RolePermissionQuery, - RolePermissionPatch -} from './services/rolePermissions/rolePermissions.shared'; + RolePermission, + RolePermissionData, + RolePermissionQuery, + RolePermissionPatch +} from './services/rolePermissions/rolePermissions.shared' -import { permissionClient } from './services/permissions/permissions.shared'; +import { permissionClient } from './services/permissions/permissions.shared' export type { - Permission, - PermissionData, - PermissionQuery, - PermissionPatch -} from './services/permissions/permissions.shared'; + Permission, + PermissionData, + PermissionQuery, + PermissionPatch +} from './services/permissions/permissions.shared' -import { groupUserClient } from './services/groupUsers/groupUsers.shared'; +import { groupUserClient } from './services/groupUsers/groupUsers.shared' export type { - GroupUser, - GroupUserData, - GroupUserQuery, - GroupUserPatch -} from './services/groupUsers/groupUsers.shared'; + GroupUser, + GroupUserData, + GroupUserQuery, + GroupUserPatch +} from './services/groupUsers/groupUsers.shared' -import { groupClient } from './services/groups/groups.shared'; -export type { Group, GroupData, GroupQuery, GroupPatch } from './services/groups/groups.shared'; +import { groupClient } from './services/groups/groups.shared' +export type { Group, GroupData, GroupQuery, GroupPatch } from './services/groups/groups.shared' -import { userClient } from './services/users/users.shared'; -export type { User, UserData, UserQuery, UserPatch } from './services/users/users.shared'; +import { userClient } from './services/users/users.shared' +export type { User, UserData, UserQuery, UserPatch } from './services/users/users.shared' export interface Configuration { connection: TransportConnection @@ -117,31 +141,34 @@ export type ClientApplication = Application * @returns The Feathers client application */ // eslint-disable-next-line no-unused-vars, no-shadow -export const createClient = ( - connection: TransportConnection, - authenticationOptions: Partial = {} +export const createClient = ( + connection: TransportConnection, + authenticationOptions: Partial = {} ) => { - const client: ClientApplication = feathers(); - - client.configure(connection); - client.configure(authenticationClient(authenticationOptions)); - client.set('connection', connection); - - client.configure(userClient); - client.configure(groupClient); - client.configure(groupUserClient); - client.configure(permissionClient); - client.configure(rolePermissionClient); - client.configure(roleClient); - client.configure(roomGroupRoleClient); - client.configure(roomOwnerClient); - client.configure(roomClient); - client.configure(roomUserRoleClient); - client.configure(tenantAdminClient); - client.configure(tenantFqdnClient); - client.configure(tenantOAuthClient); - client.configure(tenantOwnerClient); - client.configure(tenantClient); - - return client; -}; + const client: ClientApplication = feathers() + + client.configure(connection) + client.configure(authenticationClient(authenticationOptions)) + client.set('connection', connection) + + client.configure(userClient) + client.configure(groupClient) + client.configure(groupUserClient) + client.configure(permissionClient) + client.configure(rolePermissionClient) + client.configure(roleClient) + client.configure(roomGroupRoleClient) + client.configure(roomOwnerClient) + client.configure(roomClient) + client.configure(roomUserRoleClient) + client.configure(tenantAdminClient) + client.configure(tenantFqdnClient) + client.configure(tenantOAuthClient) + client.configure(tenantOwnerClient) + client.configure(tenantClient) + + client.configure(mediaNodesClient) + client.configure(mediaNodesClient) + client.configure(roomSettingsClient) + return client +} diff --git a/src/services/index.ts b/src/services/index.ts index 9197fe1..1b2c9df 100644 --- a/src/services/index.ts +++ b/src/services/index.ts @@ -1,36 +1,41 @@ -import { tenant } from './tenants/tenants'; -import { tenantOwner } from './tenantOwners/tenantOwners'; -import { tenantOAuth } from './tenantOAuths/tenantOAuths'; -import { tenantFqdn } from './tenantFQDNs/tenantFQDNs'; -import { tenantAdmin } from './tenantAdmins/tenantAdmins'; -import { roomUserRole } from './roomUserRoles/roomUserRoles'; -import { room } from './rooms/rooms'; -import { roomOwner } from './roomOwners/roomOwners'; -import { roomGroupRole } from './roomGroupRoles/roomGroupRoles'; -import { role } from './roles/roles'; -import { rolePermission } from './rolePermissions/rolePermissions'; -import { permission } from './permissions/permissions'; -import { groupUser } from './groupUsers/groupUsers'; -import { group } from './groups/groups'; -import { user } from './users/users'; +import { roomSettings } from './roomSettings/roomSettings' +import { mediaNodes } from './mediaNodes/mediaNodes' +import { tenant } from './tenants/tenants' +import { tenantOwner } from './tenantOwners/tenantOwners' +import { tenantOAuth } from './tenantOAuths/tenantOAuths' +import { tenantFqdn } from './tenantFQDNs/tenantFQDNs' +import { tenantAdmin } from './tenantAdmins/tenantAdmins' +import { roomUserRole } from './roomUserRoles/roomUserRoles' +import { room } from './rooms/rooms' +import { roomOwner } from './roomOwners/roomOwners' +import { roomGroupRole } from './roomGroupRoles/roomGroupRoles' +import { role } from './roles/roles' +import { rolePermission } from './rolePermissions/rolePermissions' +import { permission } from './permissions/permissions' +import { groupUser } from './groupUsers/groupUsers' +import { group } from './groups/groups' +import { user } from './users/users' // For more information about this file see https://dove.feathersjs.com/guides/cli/application.html#configure-functions -import type { Application } from '../declarations'; +import type { Application } from '../declarations' export const services = (app: Application) => { - app.configure(tenant); - app.configure(tenantOwner); - app.configure(tenantOAuth); - app.configure(tenantFqdn); - app.configure(tenantAdmin); - app.configure(roomUserRole); - app.configure(room); - app.configure(roomOwner); - app.configure(roomGroupRole); - app.configure(role); - app.configure(rolePermission); - app.configure(permission); - app.configure(groupUser); - app.configure(group); - app.configure(user); - // All services will be registered here -}; + app.configure(roomSettings) + app.configure(mediaNodes) + app.configure(mediaNodes) + app.configure(tenant) + app.configure(tenantOwner) + app.configure(tenantOAuth) + app.configure(tenantFqdn) + app.configure(tenantAdmin) + app.configure(roomUserRole) + app.configure(room) + app.configure(roomOwner) + app.configure(roomGroupRole) + app.configure(role) + app.configure(rolePermission) + app.configure(permission) + app.configure(groupUser) + app.configure(group) + app.configure(user) + // All services will be registered here +} diff --git a/src/services/mediaNodes/mediaNodes.class.ts b/src/services/mediaNodes/mediaNodes.class.ts new file mode 100644 index 0000000..6587e4e --- /dev/null +++ b/src/services/mediaNodes/mediaNodes.class.ts @@ -0,0 +1,22 @@ +// For more information about this file see https://dove.feathersjs.com/guides/cli/service.class.html#database-services +import type { Params } from '@feathersjs/feathers'; +import { KnexService } from '@feathersjs/knex'; +import type { KnexAdapterParams, KnexAdapterOptions } from '@feathersjs/knex'; + +import type { Application } from '../../declarations'; +import type { MediaNodes, MediaNodesData, MediaNodesPatch, MediaNodesQuery } from './mediaNodes.schema'; + +export type { MediaNodes, MediaNodesData, MediaNodesPatch, MediaNodesQuery }; + +export type MediaNodesParams = KnexAdapterParams + +// By default calls the standard Knex adapter service methods but can be customized with your own functionality. +export class MediaNodesService extends KnexService { } + +export const getOptions = (app: Application): KnexAdapterOptions => { + return { + paginate: app.get('paginate'), + Model: app.get('postgresqlClient'), + name: 'mediaNodes' + }; +}; diff --git a/src/services/mediaNodes/mediaNodes.schema.ts b/src/services/mediaNodes/mediaNodes.schema.ts new file mode 100644 index 0000000..55732d3 --- /dev/null +++ b/src/services/mediaNodes/mediaNodes.schema.ts @@ -0,0 +1,107 @@ +// // For more information about this file see https://dove.feathersjs.com/guides/cli/service.schemas.html +import { resolve } from '@feathersjs/schema'; +import { Type, getValidator, querySyntax } from '@feathersjs/typebox'; +import type { Static } from '@feathersjs/typebox'; + +import type { HookContext } from '../../declarations'; +import { dataValidator, queryValidator } from '../../validators'; +import type { MediaNodesService } from './mediaNodes.class'; + +// Main data model schema +export const mediaNodesSchema = Type.Object( + { + id: Type.Number(), + description: Type.Optional(Type.String()), + hostname: Type.String(), + port: Type.Number(), + latitude: Type.Number(), + longitude: Type.Number(), + tenantId: Type.Optional(Type.Number()), + shared: Type.Boolean(), + turnHostname: Type.Optional(Type.String()), + turnTlsPort: Type.Optional(Type.Number()), + turnUdpPort: Type.Optional(Type.Number()), + }, + { $id: 'MediaNodes', additionalProperties: false } +); +export type MediaNodes = Static +export const mediaNodesValidator = getValidator(mediaNodesSchema, dataValidator); +export const mediaNodesResolver = resolve>({}); + +export const mediaNodesExternalResolver = resolve>({}); + +// Schema for creating new entries +export const mediaNodesDataSchema = Type.Intersect([ + Type.Pick(mediaNodesSchema, [ + 'hostname', + 'port', + 'latitude', + 'longitude', + 'shared', + 'turnHostname', + 'turnTlsPort', + 'turnUdpPort', + ], { + $id: 'MediaNodesData' + }), + Type.Optional(Type.Pick(mediaNodesSchema, [ + 'description', + 'tenantId', + ], { + $id: 'MediaNodesDataOptional' + })) +]); +export type MediaNodesData = Static +export const mediaNodesDataValidator = getValidator(mediaNodesDataSchema, dataValidator); +export const mediaNodesDataResolver = resolve>({}); + +// Schema for updating existing entries +export const mediaNodesPatchSchema = Type.Partial(mediaNodesSchema, { + $id: 'MediaNodesPatch' +}); +export type MediaNodesPatch = Static +export const mediaNodesPatchValidator = getValidator(mediaNodesPatchSchema, dataValidator); +export const mediaNodesPatchResolver = resolve>({}); + +// Schema for allowed query properties +export const mediaNodesQueryProperties = Type.Pick(mediaNodesSchema, [ + 'id', + 'hostname', + 'port', + 'latitude', + 'longitude', + 'tenantId', + 'shared', + 'turnHostname', + 'turnTlsPort', + 'turnUdpPort', +]); +export const mediaNodesQuerySchema = Type.Intersect( + [ + querySyntax(mediaNodesQueryProperties), + // Add additional query properties here + Type.Object({}, { additionalProperties: false }) + ], + { additionalProperties: false } +); +export type MediaNodesQuery = Static +export const mediaNodesQueryValidator = getValidator(mediaNodesQuerySchema, queryValidator); +export const mediaNodesQueryResolver = resolve>({ + hostname: async (value, query, context) => { + if (typeof value === 'string' && context.params.user) { + const existingMediaNode = await context.app.service('mediaNodes').get(value); + + if (!existingMediaNode || existingMediaNode.tenantId === context.params.user.tenantId) + throw new Error('Media node not found'); + } + + return value; + }, + tenantId: async (value, query, context) => { + if (context.params.user?.tenantId) { + return context.params.user.tenantId; + } + + return value; + } +}); diff --git a/src/services/mediaNodes/mediaNodes.shared.ts b/src/services/mediaNodes/mediaNodes.shared.ts new file mode 100644 index 0000000..695c5a9 --- /dev/null +++ b/src/services/mediaNodes/mediaNodes.shared.ts @@ -0,0 +1,33 @@ +// For more information about this file see https://dove.feathersjs.com/guides/cli/service.shared.html +import type { Params } from '@feathersjs/feathers'; +import type { ClientApplication } from '../../client'; +import type { + MediaNodes, + MediaNodesData, + MediaNodesPatch, + MediaNodesQuery, + MediaNodesService +} from './mediaNodes.class'; + +export type { MediaNodes, MediaNodesData, MediaNodesPatch, MediaNodesQuery }; + +export type MediaNodesClientService = Pick>, (typeof mediaNodesMethods)[number]> + +export const mediaNodesPath = 'mediaNodes'; + +export const mediaNodesMethods = [ 'find', 'get', 'create', 'patch', 'remove' ] as const; + +export const mediaNodesClient = (client: ClientApplication) => { + const connection = client.get('connection'); + + client.use(mediaNodesPath, connection.service(mediaNodesPath), { + methods: mediaNodesMethods + }); +}; + +// Add this service to the client service type index +declare module '../../client' { + interface ServiceTypes { + [mediaNodesPath]: MediaNodesClientService + } +} diff --git a/src/services/mediaNodes/mediaNodes.ts b/src/services/mediaNodes/mediaNodes.ts new file mode 100644 index 0000000..b626cc1 --- /dev/null +++ b/src/services/mediaNodes/mediaNodes.ts @@ -0,0 +1,73 @@ +// For more information about this file see https://dove.feathersjs.com/guides/cli/service.html +import { authenticate } from '@feathersjs/authentication'; + +import { hooks as schemaHooks } from '@feathersjs/schema'; + +import { + mediaNodesDataValidator, + mediaNodesPatchValidator, + mediaNodesQueryValidator, + mediaNodesResolver, + mediaNodesExternalResolver, + mediaNodesDataResolver, + mediaNodesPatchResolver, + mediaNodesQueryResolver +} from './mediaNodes.schema'; + +import type { Application } from '../../declarations'; +import { MediaNodesService, getOptions } from './mediaNodes.class'; +import { mediaNodesPath, mediaNodesMethods } from './mediaNodes.shared'; + +export * from './mediaNodes.class'; +export * from './mediaNodes.schema'; + +// A configure function that registers the service and its hooks via `app.configure` +export const mediaNodes = (app: Application) => { + // Register our service on the Feathers application + app.use(mediaNodesPath, new MediaNodesService(getOptions(app)), { + // A list of all methods this service exposes externally + methods: mediaNodesMethods, + // You can add additional custom events to be sent to clients here + events: [] + }); + // Initialize hooks + app.service(mediaNodesPath).hooks({ + around: { + all: [ + authenticate('jwt'), + schemaHooks.resolveExternal(mediaNodesExternalResolver), + schemaHooks.resolveResult(mediaNodesResolver) + ] + }, + before: { + all: [ + schemaHooks.validateQuery(mediaNodesQueryValidator), + schemaHooks.resolveQuery(mediaNodesQueryResolver) + ], + find: [], + get: [], + create: [ + schemaHooks.validateData(mediaNodesDataValidator), + schemaHooks.resolveData(mediaNodesDataResolver) + ], + patch: [ + schemaHooks.validateData(mediaNodesPatchValidator), + schemaHooks.resolveData(mediaNodesPatchResolver) + ], + remove: [] + }, + after: { + all: [] + }, + error: { + all: [] + } + }); +}; + +// Add this service to the service type index +declare module '../../declarations' { + interface ServiceTypes { + [mediaNodesPath]: MediaNodesService + } +} diff --git a/src/services/roles/roles.ts b/src/services/roles/roles.ts index 48d3688..92b0c1a 100644 --- a/src/services/roles/roles.ts +++ b/src/services/roles/roles.ts @@ -46,12 +46,12 @@ export const role = (app: Application) => { all: [ schemaHooks.validateQuery(roleQueryValidator), iff(notSuperAdmin(), schemaHooks.resolveQuery(roleQueryResolver)) ], find: [], get: [], - create: [ + create: [ iff(notSuperAdmin(), notInSameTenant), schemaHooks.validateData(roleDataValidator), schemaHooks.resolveData(roleDataResolver) ], - patch: [ + patch: [ iff(notSuperAdmin(), notInSameTenant), schemaHooks.validateData(rolePatchValidator), schemaHooks.resolveData(rolePatchResolver) diff --git a/src/services/roomSettings/roomSettings.class.ts b/src/services/roomSettings/roomSettings.class.ts new file mode 100644 index 0000000..230ba95 --- /dev/null +++ b/src/services/roomSettings/roomSettings.class.ts @@ -0,0 +1,32 @@ +// For more information about this file see https://dove.feathersjs.com/guides/cli/service.class.html#database-services +import type { Params } from '@feathersjs/feathers'; +import { KnexService } from '@feathersjs/knex'; +import type { KnexAdapterParams, KnexAdapterOptions } from '@feathersjs/knex'; + +import type { Application } from '../../declarations'; +import type { + RoomSettings, + RoomSettingsData, + RoomSettingsPatch, + RoomSettingsQuery +} from './roomSettings.schema'; + +export type { RoomSettings, RoomSettingsData, RoomSettingsPatch, RoomSettingsQuery }; + +export type RoomSettingsParams = KnexAdapterParams + +// By default calls the standard Knex adapter service methods but can be customized with your own functionality. +export class RoomSettingsService extends KnexService< + RoomSettings, + RoomSettingsData, + RoomSettingsParams, + RoomSettingsPatch +> {} + +export const getOptions = (app: Application): KnexAdapterOptions => { + return { + paginate: app.get('paginate'), + Model: app.get('postgresqlClient'), + name: 'roomSettings' + }; +}; diff --git a/src/services/roomSettings/roomSettings.schema.ts b/src/services/roomSettings/roomSettings.schema.ts new file mode 100644 index 0000000..c781b7a --- /dev/null +++ b/src/services/roomSettings/roomSettings.schema.ts @@ -0,0 +1,166 @@ +// // For more information about this file see https://dove.feathersjs.com/guides/cli/service.schemas.html +import { resolve, virtual } from '@feathersjs/schema'; +import { StringEnum, Type, getValidator, querySyntax } from '@feathersjs/typebox'; +import type { Static } from '@feathersjs/typebox'; + +import type { HookContext } from '../../declarations'; +import { dataValidator, queryValidator } from '../../validators'; +import type { RoomSettingsService } from './roomSettings.class'; +import { userSchema } from '../users/users.schema'; + +export const VideoCodec = StringEnum([ 'vp8', 'vp9', 'h264', 'h265', 'av1' ]); +export const VideoResolution = StringEnum([ 'low', 'medium', 'high', 'veryhigh', 'ultra' ]); + +// Main data model schema +export const roomSettingsSchema = Type.Object( + { + id: Type.Number(), + name: Type.Optional(Type.String()), + description: Type.Optional(Type.String()), + createdAt: Type.Number(), + updatedAt: Type.Number(), + tenantId: Type.Optional(Type.Number()), + owner: Type.Optional(Type.Ref(userSchema)), // User owner + ownerId: Type.Optional(Type.Number()), // User ID of the owner + + // Look and feel + logo: Type.Optional(Type.String()), + background: Type.Optional(Type.String()), + + // Features of the room + maxActiveVideos: Type.Number(), + locked: Type.Boolean(), + breakoutsEnabled: Type.Boolean(), + chatEnabled: Type.Boolean(), + raiseHandEnabled: Type.Boolean(), + filesharingEnabled: Type.Boolean(), + localRecordingEnabled: Type.Boolean(), + + // Video settings + videoCodec: Type.Optional(Type.String()), // vp8, vp9, h264, h265, av1 + simulcast: Type.Optional(Type.Boolean()), + videoResolution: Type.Optional(VideoResolution), // low, medium, high, veryhigh, ultra + videoFramerate: Type.Optional(Type.Number()), + + // Audio settings + audioCodec: Type.Optional(Type.String()), // opus, g722, pcmu, pcma, isac, ilbc, g729, speex + autoGainControl: Type.Optional(Type.Boolean()), + echoCancellation: Type.Optional(Type.Boolean()), + noiseSuppression: Type.Optional(Type.Boolean()), + sampleRate: Type.Optional(Type.Number()), + channelCount: Type.Optional(Type.Number()), + sampleSize: Type.Optional(Type.Number()), + opusStereo: Type.Optional(Type.Boolean()), + opusDtx: Type.Optional(Type.Boolean()), + opusFec: Type.Optional(Type.Boolean()), + opusPtime: Type.Optional(Type.Number()), + opusMaxPlaybackRate: Type.Optional(Type.Number()), + + // Screen sharing settings + screenSharingCodec: Type.Optional(Type.String()), + screenSharingSimulcast: Type.Optional(Type.Boolean()), + screenSharingResolution: Type.Optional(VideoResolution), + screenSharingFramerate: Type.Optional(Type.Number()), + }, + { $id: 'RoomSettings', additionalProperties: false } +); +export type RoomSettings = Static +export const roomSettingsValidator = getValidator(roomSettingsSchema, dataValidator); +export const roomSettingsResolver = resolve>({ + owner: virtual(async (roomSettings, context) => { + if (roomSettings.ownerId) + return await context.app.service('users').get(roomSettings.ownerId); + }) +}); + +export const roomSettingsExternalResolver = resolve>({ + logo: async (value) => value ?? undefined, + background: async (value) => value ?? undefined, + videoCodec: async (value) => value ?? undefined, + simulcast: async (value) => value ?? undefined, + videoResolution: async (value) => value ?? undefined, + videoFramerate: async (value) => value ?? undefined, + audioCodec: async (value) => value ?? undefined, + autoGainControl: async (value) => value ?? undefined, + echoCancellation: async (value) => value ?? undefined, + noiseSuppression: async (value) => value ?? undefined, + sampleRate: async (value) => value ?? undefined, + channelCount: async (value) => value ?? undefined, + sampleSize: async (value) => value ?? undefined, + opusStereo: async (value) => value ?? undefined, + opusDtx: async (value) => value ?? undefined, + opusFec: async (value) => value ?? undefined, + opusPtime: async (value) => value ?? undefined, + opusMaxPlaybackRate: async (value) => value ?? undefined, + screenSharingCodec: async (value) => value ?? undefined, + screenSharingSimulcast: async (value) => value ?? undefined, + screenSharingResolution: async (value) => value ?? undefined, + screenSharingFramerate: async (value) => value ?? undefined, +}); + +// Schema for creating new entries +export const roomSettingsDataSchema = Type.Partial(roomSettingsSchema, { + $id: 'RoomSettingsData' +}); +export type RoomSettingsData = Static +export const roomSettingsDataValidator = getValidator(roomSettingsDataSchema, dataValidator); +export const roomSettingsDataResolver = resolve>({ + createdAt: async () => Date.now(), + updatedAt: async () => Date.now(), + ownerId: async (_value, _room, context) => context.params.user?.id, + tenantId: async (_value, _room, context) => context.params.user?.tenantId, + + maxActiveVideos: async (value = 12) => value, + locked: async (value = true) => value, + breakoutsEnabled: async (value = true) => value, + chatEnabled: async (value = true) => value, + raiseHandEnabled: async (value = true) => value, + filesharingEnabled: async (value = true) => value, + localRecordingEnabled: async (value = true) => value, +}); + +// Schema for updating existing entries +export const roomSettingsPatchSchema = Type.Partial(Type.Omit( + roomSettingsSchema, + [ + 'id', + 'createdAt', + 'updatedAt', + 'tenantId', + 'owner', + 'ownerId', + ]), { + $id: 'RoomSettingsPatch' +}); +export type RoomSettingsPatch = Static +export const roomSettingsPatchValidator = getValidator(roomSettingsPatchSchema, dataValidator); +export const roomSettingsPatchResolver = resolve>({ + updatedAt: async () => Date.now() +}); + +// Schema for allowed query properties +export const roomSettingsQueryProperties = Type.Pick(roomSettingsSchema, [ 'id', 'tenantId', 'ownerId' ]); +export const roomSettingsQuerySchema = Type.Intersect( + [ + querySyntax(roomSettingsQueryProperties), + // Add additional query properties here + Type.Object({}, { additionalProperties: false }) + ], + { additionalProperties: false } +); +export type RoomSettingsQuery = Static +export const roomSettingsQueryValidator = getValidator(roomSettingsQuerySchema, queryValidator); +export const roomSettingsQueryResolver = resolve>({ + tenantId: async (value, _query, context) => { + if (context.params.user?.tenantId) + return context.params.user.tenantId; + + return value; + }, + ownerId: async (value, _query, context) => { + if (context.params.user?.id && !context.params.user?.tenantAdmin) + return context.params.user.id; + + return value; + } +}); diff --git a/src/services/roomSettings/roomSettings.shared.ts b/src/services/roomSettings/roomSettings.shared.ts new file mode 100644 index 0000000..dc7da6f --- /dev/null +++ b/src/services/roomSettings/roomSettings.shared.ts @@ -0,0 +1,27 @@ +// For more information about this file see https://dove.feathersjs.com/guides/cli/service.shared.html +import type { Params } from '@feathersjs/feathers'; +import type { ClientApplication } from '../../client'; +import type { RoomSettings, RoomSettingsData, RoomSettingsPatch, RoomSettingsQuery, RoomSettingsService } from './roomSettings.class'; + +export type { RoomSettings, RoomSettingsData, RoomSettingsPatch, RoomSettingsQuery }; + +export type RoomSettingsClientService = Pick>, (typeof roomSettingsMethods)[number]> + +export const roomSettingsPath = 'roomSettings'; + +export const roomSettingsMethods = [ 'find', 'get', 'create', 'patch', 'remove' ] as const; + +export const roomSettingsClient = (client: ClientApplication) => { + const connection = client.get('connection'); + + client.use(roomSettingsPath, connection.service(roomSettingsPath), { + methods: roomSettingsMethods + }); +}; + +// Add this service to the client service type index +declare module '../../client' { + interface ServiceTypes { + [roomSettingsPath]: RoomSettingsClientService + } +} diff --git a/src/services/roomSettings/roomSettings.ts b/src/services/roomSettings/roomSettings.ts new file mode 100644 index 0000000..76463ca --- /dev/null +++ b/src/services/roomSettings/roomSettings.ts @@ -0,0 +1,75 @@ +// For more information about this file see https://dove.feathersjs.com/guides/cli/service.html +import { authenticate } from '@feathersjs/authentication'; + +import { hooks as schemaHooks } from '@feathersjs/schema'; + +import { + roomSettingsDataValidator, + roomSettingsPatchValidator, + roomSettingsQueryValidator, + roomSettingsResolver, + roomSettingsExternalResolver, + roomSettingsDataResolver, + roomSettingsPatchResolver, + roomSettingsQueryResolver +} from './roomSettings.schema'; + +import type { Application } from '../../declarations'; +import { RoomSettingsService, getOptions } from './roomSettings.class'; +import { roomSettingsPath, roomSettingsMethods } from './roomSettings.shared'; +import { iff } from 'feathers-hooks-common'; +import { notSuperAdmin } from '../../hooks/notSuperAdmin'; + +export * from './roomSettings.class'; +export * from './roomSettings.schema'; + +// A configure function that registers the service and its hooks via `app.configure` +export const roomSettings = (app: Application) => { + // Register our service on the Feathers application + app.use(roomSettingsPath, new RoomSettingsService(getOptions(app)), { + // A list of all methods this service exposes externally + methods: roomSettingsMethods, + // You can add additional custom events to be sent to clients here + events: [] + }); + // Initialize hooks + app.service(roomSettingsPath).hooks({ + around: { + all: [ + authenticate('jwt'), + schemaHooks.resolveExternal(roomSettingsExternalResolver), + schemaHooks.resolveResult(roomSettingsResolver) + ] + }, + before: { + all: [ + schemaHooks.validateQuery(roomSettingsQueryValidator), + iff(notSuperAdmin(), schemaHooks.resolveQuery(roomSettingsQueryResolver)) + ], + find: [], + get: [], + create: [ + schemaHooks.validateData(roomSettingsDataValidator), + schemaHooks.resolveData(roomSettingsDataResolver) + ], + patch: [ + schemaHooks.validateData(roomSettingsPatchValidator), + schemaHooks.resolveData(roomSettingsPatchResolver) + ], + remove: [ ] + }, + after: { + all: [] + }, + error: { + all: [] + } + }); +}; + +// Add this service to the service type index +declare module '../../declarations' { + interface ServiceTypes { + [roomSettingsPath]: RoomSettingsService + } +} diff --git a/src/services/rooms/rooms.schema.ts b/src/services/rooms/rooms.schema.ts index a921714..317c168 100644 --- a/src/services/rooms/rooms.schema.ts +++ b/src/services/rooms/rooms.schema.ts @@ -1,6 +1,6 @@ // // For more information about this file see https://dove.feathersjs.com/guides/cli/service.schemas.html import { resolve, virtual } from '@feathersjs/schema'; -import { StringEnum, Type, getValidator, querySyntax } from '@feathersjs/typebox'; +import { Type, getValidator, querySyntax } from '@feathersjs/typebox'; import type { Static } from '@feathersjs/typebox'; import type { HookContext } from '../../declarations'; @@ -9,9 +9,7 @@ import { roomGroupRoleSchema } from '../roomGroupRoles/roomGroupRoles.schema'; import { roomUserRoleSchema } from '../roomUserRoles/roomUserRoles.schema'; import { roomOwnerSchema } from '../roomOwners/roomOwners.schema'; import { roleSchema } from '../roles/roles.schema'; - -export const VideoCodec = StringEnum([ 'vp8', 'vp9', 'h264', 'h265', 'av1' ]); -export const VideoResolution = StringEnum([ 'low', 'medium', 'high', 'veryhigh', 'ultra' ]); +import { roomSettingsSchema } from '../roomSettings/roomSettings.schema'; // Main data model schema export const roomSchema = Type.Object( @@ -31,44 +29,8 @@ export const roomSchema = Type.Object( defaultRole: Type.Optional(Type.Ref(roleSchema)), defaultRoleId: Type.Optional(Type.Number()), // Default role for users without a role in this room - // Look and feel - logo: Type.Optional(Type.String()), - background: Type.Optional(Type.String()), - - // Features of the room - maxActiveVideos: Type.Number(), - locked: Type.Boolean(), - breakoutsEnabled: Type.Boolean(), - chatEnabled: Type.Boolean(), - raiseHandEnabled: Type.Boolean(), - filesharingEnabled: Type.Boolean(), - localRecordingEnabled: Type.Boolean(), - - // Video settings - videoCodec: Type.Optional(Type.String()), // vp8, vp9, h264, h265, av1 - simulcast: Type.Optional(Type.Boolean()), - videoResolution: Type.Optional(VideoResolution), // low, medium, high, veryhigh, ultra - videoFramerate: Type.Optional(Type.Number()), - - // Audio settings - audioCodec: Type.Optional(Type.String()), // opus, g722, pcmu, pcma, isac, ilbc, g729, speex - autoGainControl: Type.Optional(Type.Boolean()), - echoCancellation: Type.Optional(Type.Boolean()), - noiseSuppression: Type.Optional(Type.Boolean()), - sampleRate: Type.Optional(Type.Number()), - channelCount: Type.Optional(Type.Number()), - sampleSize: Type.Optional(Type.Number()), - opusStereo: Type.Optional(Type.Boolean()), - opusDtx: Type.Optional(Type.Boolean()), - opusFec: Type.Optional(Type.Boolean()), - opusPtime: Type.Optional(Type.Number()), - opusMaxPlaybackRate: Type.Optional(Type.Number()), - - // Screen sharing settings - screenSharingCodec: Type.Optional(Type.String()), - screenSharingSimulcast: Type.Optional(Type.Boolean()), - screenSharingResolution: Type.Optional(VideoResolution), - screenSharingFramerate: Type.Optional(Type.Number()), + roomSettings: Type.Optional(Type.Ref(roomSettingsSchema)), // Room settings ID + roomSettingsId: Type.Optional(Type.Number()), // Room settings ID }, { $id: 'Room', additionalProperties: false } ); @@ -108,32 +70,19 @@ export const roomResolver = resolve({ if (room.defaultRoleId) return context.app.service('roles').get(room.defaultRoleId); }), + roomSettings: virtual(async (room, context) => { + if (room.roomSettingsId) + return context.app.service('roomSettings').get(room.roomSettingsId); + else if (room.tenantId) { + const tenant = await context.app.service('tenants').get(room.tenantId); + + if (tenant.defaultRoomSettingsId) + return context.app.service('roomSettings').get(tenant.defaultRoomSettingsId); + } + }), }); -export const roomExternalResolver = resolve({ - logo: async (value) => value ?? undefined, - background: async (value) => value ?? undefined, - videoCodec: async (value) => value ?? undefined, - simulcast: async (value) => value ?? undefined, - videoResolution: async (value) => value ?? undefined, - videoFramerate: async (value) => value ?? undefined, - audioCodec: async (value) => value ?? undefined, - autoGainControl: async (value) => value ?? undefined, - echoCancellation: async (value) => value ?? undefined, - noiseSuppression: async (value) => value ?? undefined, - sampleRate: async (value) => value ?? undefined, - channelCount: async (value) => value ?? undefined, - sampleSize: async (value) => value ?? undefined, - opusStereo: async (value) => value ?? undefined, - opusDtx: async (value) => value ?? undefined, - opusFec: async (value) => value ?? undefined, - opusPtime: async (value) => value ?? undefined, - opusMaxPlaybackRate: async (value) => value ?? undefined, - screenSharingCodec: async (value) => value ?? undefined, - screenSharingSimulcast: async (value) => value ?? undefined, - screenSharingResolution: async (value) => value ?? undefined, - screenSharingFramerate: async (value) => value ?? undefined, -}); +export const roomExternalResolver = resolve({}); // Schema for creating new entries export const roomDataSchema = Type.Intersect([ @@ -149,6 +98,7 @@ export const roomDataSchema = Type.Intersect([ 'updatedAt', 'creatorId', 'tenantId', + 'roomSettings', ], { additionalProperties: false })) ], { $id: 'RoomData', additionalProperties: false }); export type RoomData = Static @@ -160,19 +110,13 @@ export const roomDataResolver = resolve({ updatedAt: async () => Date.now(), creatorId: async (value, room, context) => context.params.user?.id, tenantId: async (value, room, context) => context.params.user?.tenantId, - maxActiveVideos: async (value = 12) => value, - locked: async (value = true) => value, - breakoutsEnabled: async (value = true) => value, - chatEnabled: async (value = true) => value, - raiseHandEnabled: async (value = true) => value, - filesharingEnabled: async (value = true) => value, - localRecordingEnabled: async (value = true) => value, }); // Schema for updating existing entries export const roomPatchSchema = Type.Partial(Type.Omit( roomSchema, [ + 'id', 'name', 'owners', 'groupRoles', @@ -182,6 +126,7 @@ export const roomPatchSchema = Type.Partial(Type.Omit( 'updatedAt', 'creatorId', 'tenantId', + 'roomSettings', ]), { $id: 'RoomPatch' }); diff --git a/src/services/tenants/tenants.schema.ts b/src/services/tenants/tenants.schema.ts index 4efec5d..c4a6687 100644 --- a/src/services/tenants/tenants.schema.ts +++ b/src/services/tenants/tenants.schema.ts @@ -1,10 +1,11 @@ // // For more information about this file see https://dove.feathersjs.com/guides/cli/service.schemas.html -import { resolve } from '@feathersjs/schema'; +import { resolve, virtual } from '@feathersjs/schema'; import { Type, getValidator, querySyntax } from '@feathersjs/typebox'; import type { Static } from '@feathersjs/typebox'; import type { HookContext } from '../../declarations'; import { dataValidator, queryValidator } from '../../validators'; +import { roomSettingsSchema } from '../roomSettings/roomSettings.schema'; // Main data model schema export const tenantSchema = Type.Object( @@ -12,11 +13,18 @@ export const tenantSchema = Type.Object( id: Type.Number(), name: Type.String(), description: Type.Optional(Type.String()), + defaultRoomSettingsId: Type.Optional(Type.Number()), + defaultRoomSettings: Type.Optional(Type.Ref(roomSettingsSchema)), }, { $id: 'Tenant', additionalProperties: false } ); export type Tenant = Static -export const tenantResolver = resolve({}); +export const tenantResolver = resolve({ + defaultRoomSettings: virtual(async (tenant, context) => { + if (tenant.defaultRoomSettingsId) + return context.app.service('roomSettings').get(tenant.defaultRoomSettingsId); + }), +}); export const tenantExternalResolver = resolve({}); diff --git a/test/services/mediaNodes/mediaNodes.test.ts b/test/services/mediaNodes/mediaNodes.test.ts new file mode 100644 index 0000000..0435681 --- /dev/null +++ b/test/services/mediaNodes/mediaNodes.test.ts @@ -0,0 +1,11 @@ +// For more information about this file see https://dove.feathersjs.com/guides/cli/service.test.html +import assert from 'assert' +import { app } from '../../../src/app' + +describe('mediaNodes service', () => { + it('registered the service', () => { + const service = app.service('mediaNodes') + + assert.ok(service, 'Registered the service') + }) +}) diff --git a/test/services/roomSettings/roomSettings.test.ts b/test/services/roomSettings/roomSettings.test.ts new file mode 100644 index 0000000..af9c136 --- /dev/null +++ b/test/services/roomSettings/roomSettings.test.ts @@ -0,0 +1,11 @@ +// For more information about this file see https://dove.feathersjs.com/guides/cli/service.test.html +import assert from 'assert' +import { app } from '../../../src/app' + +describe('roomSettings service', () => { + it('registered the service', () => { + const service = app.service('roomSettings') + + assert.ok(service, 'Registered the service') + }) +})