Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for Scalelite Tagged Servers #5790

Merged
merged 32 commits into from
Jul 24, 2024
Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
eeccdd1
init server tags prototype with config variable and corresponding dro…
Ithanil Apr 22, 2024
b0c9a36
moved ServerTag row to separate file + rubocop
Ithanil Apr 23, 2024
7676922
eslint happiness
Ithanil Apr 23, 2024
a3d50d1
add db migration for room option meta_server-tag
Ithanil Apr 23, 2024
0f3c286
split the meta_server-tag parameter into two 'virtual' room options i…
Ithanil Apr 24, 2024
8060cf6
dynamically render the server tag Dropdown from the config string + h…
Ithanil Apr 24, 2024
49a7e58
now actually reflect and mutate the serverTag setting in DB via the D…
Ithanil Apr 24, 2024
b9f9847
now use server tag room option as actual parameter on create call
Ithanil Apr 24, 2024
9db7292
rename config SERVER_TAGS_MAP->SERVER_TAG_NAMES and now handle it in …
Ithanil Apr 25, 2024
c85b00b
add config variable for tags<->roles and document the full server tag…
Ithanil Apr 25, 2024
3742138
fully implemented the tag<->role restriction as per config variable
Ithanil Apr 25, 2024
a52e5e6
add configurable default tag name for untagged servers
Ithanil Apr 25, 2024
35b8a44
added checkbox to set if server tag is required
Ithanil Apr 26, 2024
40367b3
add logic to handle serverTagRequired in meeting_starter service
Ithanil Apr 26, 2024
67b802a
rubocop + change tagRequired toggle to react-bootstrap Radio buttons …
Ithanil Apr 26, 2024
36f303e
eslint + add description above server tag row
Ithanil Apr 26, 2024
8672270
make tags Dropdown drop up + cleanpu
Ithanil Apr 26, 2024
4ef1673
finalized DB migration for the server tags options
Ithanil Apr 29, 2024
94db53c
final polish (including translated strings)
Ithanil Apr 29, 2024
b5c85e2
add rake task server_tags_sync to clear invalid/disallowed server tag…
Ithanil May 6, 2024
f3037a4
adds translated string for default tag name / DEFAULT_TAG_NAME env n…
Ithanil Jun 24, 2024
c4175d6
show a specific error message when a required server type is unavaila…
Ithanil Jun 25, 2024
75ff62a
Merge branch 'master' into tagged_servers
farhatahmad Jul 12, 2024
60c359d
move tag fetching logic from CurrentUserSerializer to new ServerTagsC…
Ithanil Jul 15, 2024
0cb9ac6
refactor ServerTagRow and remove tags-related env variables completel…
Ithanil Jul 15, 2024
c870c53
remove DEFAULT_TAG_NAME from sample.env
Ithanil Jul 16, 2024
2928003
now silently fall back to untagged if an invalid/forbidden tag is use…
Ithanil Jul 16, 2024
ffcd972
check room owner's role_id instead of current user's for role restric…
Ithanil Jul 18, 2024
b3c2b47
improve guarding for the cases of disabled tags or invalid room_ids i…
Ithanil Jul 19, 2024
8923859
avoid checking serverTags.isLoading
Ithanil Jul 22, 2024
b85299d
streamline server tags migration
Ithanil Jul 23, 2024
50ff44d
massively increase performance of server tags migration if database i…
Ithanil Jul 24, 2024
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
5 changes: 5 additions & 0 deletions app/assets/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,10 @@
"wrong_access_code": "Wrong Access Code",
"generate_viewers_access_code": "Generate access code for viewers",
"generate_mods_access_code": "Generate access code for moderators",
"server_tag": "Select a server type for this room",
"default_tag_name": "Default",
"server_tag_desired": "Desired",
"server_tag_required": "Required",
"are_you_sure_delete_room": "Are you sure you want to delete this room?"
}
},
Expand Down Expand Up @@ -437,6 +441,7 @@
},
"error": {
"problem_completing_action": "The action can't be completed. \n Please try again.",
"server_type_unavailable": "The required server type is unavailable. Please select a different type in the room settings.",
"file_type_not_supported": "The file type is not supported.",
"file_size_too_large": "The file size is too large.",
"file_upload_error": "The file can't be uploaded.",
Expand Down
2 changes: 1 addition & 1 deletion app/controllers/api/v1/meetings_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def start
begin
MeetingStarter.new(room: @room, base_url: request.base_url, current_user:, provider: current_provider).call
rescue BigBlueButton::BigBlueButtonException => e
return render_error status: :bad_request unless e.key == 'idNotUnique'
return render_error status: :bad_request, errors: e.key unless e.key == 'idNotUnique'
end

render_data data: BigBlueButtonApi.new(provider: current_provider).join_meeting(
Expand Down
34 changes: 34 additions & 0 deletions app/controllers/api/v1/server_tags_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
#
# Copyright (c) 2022 BigBlueButton Inc. and by respective authors (see below).
#
# This program is free software; you can redistribute it and/or modify it under the
# terms of the GNU Lesser General Public License as published by the Free Software
# Foundation; either version 3.0 of the License, or (at your option) any later
# version.
#
# Greenlight is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with Greenlight; if not, see <http://www.gnu.org/licenses/>.

# frozen_string_literal: true

module Api
module V1
class ServerTagsController < ApiController
# GET /api/v1/server_tags/:friendly_id
# Returns a list of all allowed tags&names for the room's owner
def show
room = Room.find_by!(friendly_id: params[:friendly_id])
Ithanil marked this conversation as resolved.
Show resolved Hide resolved
tag_names = Rails.configuration.server_tag_names
tag_roles = Rails.configuration.server_tag_roles
Ithanil marked this conversation as resolved.
Show resolved Hide resolved
allowed_tag_names = tag_names.reject { |tag, _| tag_roles.key?(tag) && tag_roles[tag].exclude?(room.user.role_id) }

render_data data: allowed_tag_names, status: :ok
end
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ import { useAuth } from '../../../../contexts/auth/AuthProvider';
import UpdateRoomNameForm from './forms/UpdateRoomNameForm';
import useRoom from '../../../../hooks/queries/rooms/useRoom';
import UnshareRoom from './UnshareRoom';
import useServerTags from '../../../../hooks/queries/rooms/useServerTags';
import ServerTagRow from './ServerTagRow';

export default function RoomSettings() {
const { t } = useTranslation();
Expand All @@ -41,6 +43,7 @@ export default function RoomSettings() {
const roomSetting = useRoomSettings(friendlyId);
const { data: roomConfigs } = useRoomConfigs();
const { data: room } = useRoom(friendlyId);
const serverTags = useServerTags(friendlyId);

const updateMutationWrapper = () => useUpdateRoomSetting(friendlyId);
const deleteMutationWrapper = (args) => useDeleteRoom({ friendlyId, ...args });
Expand All @@ -66,6 +69,15 @@ export default function RoomSettings() {
config={roomConfigs?.glModeratorAccessCode}
description={t('room.settings.generate_mods_access_code')}
/>
{(!serverTags.isLoading && Object.keys(serverTags?.data).length !== 0) && (
Ithanil marked this conversation as resolved.
Show resolved Hide resolved
<ServerTagRow
updateMutation={updateMutationWrapper}
currentTag={roomSetting?.data?.serverTag}
tagRequired={roomSetting?.data?.serverTagRequired === 'true'}
serverTags={serverTags?.data}
description={t('room.settings.server_tag')}
/>
)}
</Col>
<Col className="ps-4">
<Row> <h6 className="text-brand">{ t('room.settings.user_settings') }</h6> </Row>
Expand Down
118 changes: 118 additions & 0 deletions app/javascript/components/rooms/room/room_settings/ServerTagRow.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
//
// Copyright (c) 2022 BigBlueButton Inc. and by respective authors (see below).
//
// This program is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License as published by the Free Software
// Foundation; either version 3.0 of the License, or (at your option) any later
// version.
//
// Greenlight is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License along
// with Greenlight; if not, see <http://www.gnu.org/licenses/>.

import React from 'react';
import { useTranslation } from 'react-i18next';
import PropTypes from 'prop-types';
import {
Row, Col, Dropdown, ButtonGroup, ToggleButton,
} from 'react-bootstrap';
import SimpleSelect from '../../../shared_components/utilities/SimpleSelect';

export default function ServerTagRow({
updateMutation: useUpdateAPI, currentTag, tagRequired, serverTags, description,
}) {
const updateAPI = useUpdateAPI();
const { t } = useTranslation();

function getDefaultTagName() {
return t('room.settings.default_tag_name');
}

function getTagName(tag) {
if (tag in serverTags) {
return serverTags[tag];
}
return getDefaultTagName();
}

const dropdownTags = Object.entries(serverTags).map(([tagString, tagName]) => (
(
<Dropdown.Item
key={tagString}
value={tagName}
onClick={() => updateAPI.mutate({ settingName: 'serverTag', settingValue: tagString })}
>
{tagName}
</Dropdown.Item>
)
));

return (
<Row>
<h6 className="text-brand">{description}</h6>
<Col>
<SimpleSelect defaultValue={getTagName(currentTag)} dropUp>
{[
<Dropdown.Item
key=""
value={getDefaultTagName()}
disabled={updateAPI.isLoading}
onClick={() => updateAPI.mutate({ settingName: 'serverTag', settingValue: '' })}
>
{getDefaultTagName()}
</Dropdown.Item>,
].concat(dropdownTags)}
</SimpleSelect>
</Col>
<Col>
<ButtonGroup>
<ToggleButton
key="desired"
id="desired"
type="radio"
variant="outline-success"
name="radio"
checked={tagRequired === false}
disabled={updateAPI.isLoading}
onChange={() => {
updateAPI.mutate({ settingName: 'serverTagRequired', settingValue: false });
}}
>
{t('room.settings.server_tag_desired')}
</ToggleButton>
<ToggleButton
key="required"
id="required"
type="radio"
variant="outline-danger"
name="radio"
checked={tagRequired === true}
disabled={updateAPI.isLoading}
onChange={() => {
updateAPI.mutate({ settingName: 'serverTagRequired', settingValue: true });
}}
>
{t('room.settings.server_tag_required')}
</ToggleButton>
</ButtonGroup>
</Col>
</Row>
);
}

ServerTagRow.defaultProps = {
currentTag: '',
tagRequired: false,
};

ServerTagRow.propTypes = {
updateMutation: PropTypes.func.isRequired,
currentTag: PropTypes.string,
tagRequired: PropTypes.bool,
serverTags: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
description: PropTypes.string.isRequired,
};
8 changes: 6 additions & 2 deletions app/javascript/hooks/mutations/rooms/useStartMeeting.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,12 @@ export default function useStartMeeting(friendlyId) {
onSuccess: (joinUrl) => {
window.location.href = joinUrl;
},
onError: () => {
toast.error(t('toast.error.problem_completing_action'));
onError: (error) => {
if (error.response.data.errors !== 'serverTagUnavailable') {
toast.error(t('toast.error.problem_completing_action'));
} else {
toast.error(t('toast.error.server_type_unavailable'));
}
},
},
);
Expand Down
25 changes: 25 additions & 0 deletions app/javascript/hooks/queries/rooms/useServerTags.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
//
// Copyright (c) 2022 BigBlueButton Inc. and by respective authors (see below).
//
// This program is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License as published by the Free Software
// Foundation; either version 3.0 of the License, or (at your option) any later
// version.
//
// Greenlight is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License along
// with Greenlight; if not, see <http://www.gnu.org/licenses/>.

import { useQuery } from 'react-query';
import axios from '../../../helpers/Axios';

export default function useServerTags(friendlyId) {
return useQuery(
['getServerTags', friendlyId],
() => axios.get(`/server_tags/${friendlyId}.json`).then((resp) => resp.data.data),
);
}
19 changes: 19 additions & 0 deletions app/services/meeting_starter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ def call
settings: 'glViewerAccessCode'
).call

handle_server_tag(meeting_options: options)

options.merge!(computed_options(access_code: viewer_code['glViewerAccessCode']))

retries = 0
Expand Down Expand Up @@ -73,6 +75,23 @@ def computed_options(access_code:)
}
end

def handle_server_tag(meeting_options:)
if meeting_options['serverTag'].present?
tag_names = Rails.configuration.server_tag_names
tag_roles = Rails.configuration.server_tag_roles
tag = meeting_options.delete('serverTag')
tag_required = meeting_options.delete('serverTagRequired')

if tag_names.key?(tag) && !(tag_roles.key?(tag) && tag_roles[tag].exclude?(@room.user.role_id))
tag_param = tag_required == 'true' ? "#{tag} !" : tag
meeting_options.store('meta_server-tag', tag_param)
end
else
meeting_options.delete('serverTag')
meeting_options.delete('serverTagRequired')
end
end

def presentation_url
return unless @room.presentation.attached?

Expand Down
5 changes: 5 additions & 0 deletions config/application.rb
Original file line number Diff line number Diff line change
Expand Up @@ -86,5 +86,10 @@ class Application < Rails::Application
I18n.load_path += Dir[Rails.root.join('config/locales/*.{rb,yml}').to_s]
config.i18n.fallbacks = %i[en]
config.i18n.enforce_available_locales = false

# Handle server tag config
config.server_tag_names = ENV.fetch('SERVER_TAG_NAMES', '').split(',').to_h { |pair| pair.split(':') }
config.server_tag_roles = ENV.fetch('SERVER_TAG_ROLES', '').split(',').to_h { |pair| pair.split(':') }
config.server_tag_roles = config.server_tag_roles.transform_values! { |v| v.split('/') }
end
end
1 change: 1 addition & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@
resources :site_settings, only: :index
resources :rooms_configurations, only: %i[index show], param: :name
resources :locales, only: %i[index show], param: :name
resources :server_tags, only: :show, param: :friendly_id

namespace :admin do
resources :users, only: %i[update] do
Expand Down
61 changes: 61 additions & 0 deletions db/data/20240423162700_create_server_tags_option.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
#
# Copyright (c) 2022 BigBlueButton Inc. and by respective authors (see below).
#
# This program is free software; you can redistribute it and/or modify it under the
# terms of the GNU Lesser General Public License as published by the Free Software
# Foundation; either version 3.0 of the License, or (at your option) any later
# version.
#
# Greenlight is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with Greenlight; if not, see <http://www.gnu.org/licenses/>.

# frozen_string_literal: true

class CreateServerTagsOption < ActiveRecord::Migration[7.0]
def up
MeetingOption.create!(name: 'serverTag', default_value: '') unless MeetingOption.exists?(name: 'serverTag')
tag_option = MeetingOption.find_by!(name: 'serverTag')
unless RoomsConfiguration.exists?(meeting_option: tag_option, provider: 'greenlight')
RoomsConfiguration.create!(meeting_option: tag_option, value: 'optional', provider: 'greenlight')
end
Tenant.all.each do |tenant|
unless RoomsConfiguration.exists?(meeting_option: tag_option, provider: tenant.name)
RoomsConfiguration.create!(meeting_option: tag_option, value: 'optional', provider: tenant.name)
end
end
Room.find_each { |room| RoomMeetingOption.find_or_create_by!(room:, meeting_option: tag_option) }

MeetingOption.create!(name: 'serverTagRequired', default_value: 'false') unless MeetingOption.exists?(name: 'serverTagRequired')
tag_required_option = MeetingOption.find_by!(name: 'serverTagRequired')
unless RoomsConfiguration.exists?(meeting_option: tag_required_option, provider: 'greenlight')
RoomsConfiguration.create!(meeting_option: tag_required_option, value: 'optional', provider: 'greenlight')
end
Tenant.all.each do |tenant|
unless RoomsConfiguration.exists?(meeting_option: tag_required_option, provider: tenant.name)
RoomsConfiguration.create!(meeting_option: tag_required_option, value: 'optional', provider: tenant.name)
end
end
Room.find_each do |room|
unless RoomMeetingOption.exists?(room:, meeting_option: tag_required_option)
RoomMeetingOption.create!(room:, meeting_option: tag_required_option, value: 'false')
end
end
Ithanil marked this conversation as resolved.
Show resolved Hide resolved
end

def down
tag_option = MeetingOption.find_by!(name: 'serverTag')
RoomMeetingOption.destroy_by(meeting_option: tag_option)
RoomsConfiguration.destroy_by(meeting_option: tag_option)
tag_option.destroy

tag_required_option = MeetingOption.find_by!(name: 'serverTagRequired')
RoomMeetingOption.destroy_by(meeting_option: tag_required_option)
RoomsConfiguration.destroy_by(meeting_option: tag_required_option)
tag_required_option.destroy
end
end
2 changes: 1 addition & 1 deletion db/data_schema.rb
Original file line number Diff line number Diff line change
@@ -1 +1 @@
DataMigrate::Data.define(version: 20240209155229)
DataMigrate::Data.define(version: 20240423162700)
2 changes: 1 addition & 1 deletion esbuild.dev.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,4 @@ esbuild.context({
}).catch((e) => {
console.error('build failed:', e);
process.exit(1)
})
})
Loading
Loading