diff --git a/app/__init__.py b/app/__init__.py index 9953a1cf..eb4f7bd6 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -132,9 +132,6 @@ def create_app(config: Config = Config) -> Flask: # region SocketIO Namespaces from .namespaces.cqi_over_sio import CQiOverSocketIONamespace socketio.on_namespace(CQiOverSocketIONamespace('/cqi_over_sio')) - - from .namespaces.users import UsersNamespace - socketio.on_namespace(UsersNamespace('/users')) # endregion SocketIO Namespaces # region Database event Listeners diff --git a/app/blueprints/users/__init__.py b/app/blueprints/users/__init__.py index 21e9c382..d305e242 100644 --- a/app/blueprints/users/__init__.py +++ b/app/blueprints/users/__init__.py @@ -15,4 +15,4 @@ def before_request(): pass -from . import cli, json_routes, routes, settings +from . import cli, events, json_routes, routes, settings diff --git a/app/blueprints/users/events.py b/app/blueprints/users/events.py new file mode 100644 index 00000000..254a5ca6 --- /dev/null +++ b/app/blueprints/users/events.py @@ -0,0 +1,82 @@ +from flask_login import current_user +from flask_socketio import join_room, leave_room +from app import hashids, socketio +from app.decorators import socketio_login_required +from app.models import User + + +@socketio.on('users.get_user') +@socketio_login_required +def get_user(user_hashid: str) -> dict: + user_id = hashids.decode(user_hashid) + + if not isinstance(user_id, int): + return {'status': 400, 'statusText': 'Bad Request'} + + user = User.query.get(user_id) + + if user is None: + return {'status': 404, 'statusText': 'Not found'} + + if not ( + user == current_user + or current_user.is_administrator + ): + return {'status': 403, 'statusText': 'Forbidden'} + + return { + 'body': user.to_json_serializeable( + backrefs=True, + relationships=True + ), + 'status': 200, + 'statusText': 'OK' + } + + +@socketio.on('users.subscribe_user') +@socketio_login_required +def subscribe_user(user_hashid: str) -> dict: + user_id = hashids.decode(user_hashid) + + if not isinstance(user_id, int): + return {'status': 400, 'statusText': 'Bad Request'} + + user = User.query.get(user_id) + + if user is None: + return {'status': 404, 'statusText': 'Not found'} + + if not ( + user == current_user + or current_user.is_administrator + ): + return {'status': 403, 'statusText': 'Forbidden'} + + join_room(f'/users/{user.hashid}') + + return {'status': 200, 'statusText': 'OK'} + + +@socketio.on('users.unsubscribe_user') +@socketio_login_required +def on_unsubscribe_user(user_hashid: str) -> dict: + user_id = hashids.decode(user_hashid) + + if not isinstance(user_id, int): + return {'status': 400, 'statusText': 'Bad Request'} + + user = User.query.get(user_id) + + if user is None: + return {'status': 404, 'statusText': 'Not found'} + + if not ( + user == current_user + or current_user.is_administrator + ): + return {'status': 403, 'statusText': 'Forbidden'} + + leave_room(f'/users/{user.hashid}') + + return {'status': 200, 'statusText': 'OK'} diff --git a/app/namespaces/users/__init__.py b/app/namespaces/users/__init__.py deleted file mode 100644 index 42e2f9e3..00000000 --- a/app/namespaces/users/__init__.py +++ /dev/null @@ -1,78 +0,0 @@ -from flask_login import current_user -from flask_socketio import join_room, leave_room, Namespace -from app import hashids -from app.decorators import socketio_login_required -from app.models import User - - -class UsersNamespace(Namespace): - @socketio_login_required - def on_get_user(self, user_hashid: str) -> dict: - user_id = hashids.decode(user_hashid) - - if not isinstance(user_id, int): - return {'status': 400, 'statusText': 'Bad Request'} - - user = User.query.get(user_id) - - if user is None: - return {'status': 404, 'statusText': 'Not found'} - - if not ( - user == current_user - or current_user.is_administrator - ): - return {'status': 403, 'statusText': 'Forbidden'} - - return { - 'body': user.to_json_serializeable( - backrefs=True, - relationships=True - ), - 'status': 200, - 'statusText': 'OK' - } - - @socketio_login_required - def on_subscribe_user(self, user_hashid: str) -> dict: - user_id = hashids.decode(user_hashid) - - if not isinstance(user_id, int): - return {'status': 400, 'statusText': 'Bad Request'} - - user = User.query.get(user_id) - - if user is None: - return {'status': 404, 'statusText': 'Not found'} - - if not ( - user == current_user - or current_user.is_administrator - ): - return {'status': 403, 'statusText': 'Forbidden'} - - join_room(f'/users/{user.hashid}') - - return {'status': 200, 'statusText': 'OK'} - - @socketio_login_required - def on_unsubscribe_user(self, user_hashid: str) -> dict: - user_id = hashids.decode(user_hashid) - - if not isinstance(user_id, int): - return {'status': 400, 'statusText': 'Bad Request'} - - user = User.query.get(user_id) - - if user is None: - return {'status': 404, 'statusText': 'Not found'} - - if not ( - user == current_user - or current_user.is_administrator - ): - return {'status': 403, 'statusText': 'Forbidden'} - - leave_room(f'/users/{user.hashid}') - - return {'status': 200, 'statusText': 'OK'} diff --git a/app/static/js/app.js b/app/static/js/app.js index 8ad12449..281452f7 100644 --- a/app/static/js/app.js +++ b/app/static/js/app.js @@ -11,11 +11,9 @@ nopaque.App = class App { subscribeUser: {} }; - this.sockets = { - users: io('/users', {transports: ['websocket'], upgrade: false}) - }; + this.socket = io({transports: ['websocket'], upgrade: false}); - this.sockets.users.on('patch_user', (patch) => {this.onPatch(patch);}); + this.socket.on('patch_user', (patch) => {this.onPatch(patch);}); } getUser(userId) { @@ -23,10 +21,8 @@ nopaque.App = class App { return this.#promises.getUser[userId]; } - let socket = this.sockets.users; - this.#promises.getUser[userId] = new Promise((resolve, reject) => { - socket.emit('get_user', userId, (response) => { + this.socket.emit('users.get_user', userId, (response) => { if (response.status === 200) { this.data.users[userId] = response.body; resolve(this.data.users[userId]); @@ -44,10 +40,8 @@ nopaque.App = class App { return this.#promises.subscribeUser[userId]; } - let socket = this.sockets.users; - this.#promises.subscribeUser[userId] = new Promise((resolve, reject) => { - socket.emit('subscribe_user', userId, (response) => { + this.socket.emit('users.subscribe_user', userId, (response) => { if (response.status === 200) { resolve(response); } else { diff --git a/app/static/js/resource-displays/resource-display.js b/app/static/js/resource-displays/resource-display.js index 2cd45d67..9d327131 100644 --- a/app/static/js/resource-displays/resource-display.js +++ b/app/static/js/resource-displays/resource-display.js @@ -8,7 +8,7 @@ nopaque.resource_displays.ResourceDisplay = class ResourceDisplay { if (this.userId) { app.subscribeUser(this.userId) .then((response) => { - app.sockets.users.on('patch_user', (patch) => { + app.socket.on('patch_user', (patch) => { if (this.isInitialized) {this.onPatch(patch);} }); }); diff --git a/app/static/js/resource-lists/corpus-file-list.js b/app/static/js/resource-lists/corpus-file-list.js index e794444f..fd3acea1 100644 --- a/app/static/js/resource-lists/corpus-file-list.js +++ b/app/static/js/resource-lists/corpus-file-list.js @@ -15,7 +15,7 @@ nopaque.resource_lists.CorpusFileList = class CorpusFileList extends nopaque.res this.hasPermissionManageFiles = listContainerElement.dataset?.hasPermissionManageFiles == 'true' || false; if (this.userId === undefined || this.corpusId === undefined) {return;} app.subscribeUser(this.userId).then((response) => { - app.sockets.users.on('patch_user', (patch) => { + app.socket.on('patch_user', (patch) => { if (this.isInitialized) {this.onPatch(patch);} }); }); diff --git a/app/static/js/resource-lists/corpus-follower-list.js b/app/static/js/resource-lists/corpus-follower-list.js index 3419f56c..0a0679d0 100644 --- a/app/static/js/resource-lists/corpus-follower-list.js +++ b/app/static/js/resource-lists/corpus-follower-list.js @@ -13,7 +13,7 @@ nopaque.resource_lists.CorpusFollowerList = class CorpusFollowerList extends nop this.corpusId = listContainerElement.dataset.corpusId; if (this.userId === undefined || this.corpusId === undefined) {return;} app.subscribeUser(this.userId).then((response) => { - app.sockets.users.on('patch_user', (patch) => { + app.socket.on('patch_user', (patch) => { if (this.isInitialized) {this.onPatch(patch);} }); }); diff --git a/app/static/js/resource-lists/corpus-list.js b/app/static/js/resource-lists/corpus-list.js index f3ff7832..96e10e88 100644 --- a/app/static/js/resource-lists/corpus-list.js +++ b/app/static/js/resource-lists/corpus-list.js @@ -12,7 +12,7 @@ nopaque.resource_lists.CorpusList = class CorpusList extends nopaque.resource_li this.userId = listContainerElement.dataset.userId; if (this.userId === undefined) {return;} app.subscribeUser(this.userId).then((response) => { - app.sockets.users.on('patch_user', (patch) => { + app.socket.on('patch_user', (patch) => { if (this.isInitialized) {this.onPatch(patch);} }); }); diff --git a/app/static/js/resource-lists/job-list.js b/app/static/js/resource-lists/job-list.js index 3c2fc4ea..abc1145f 100644 --- a/app/static/js/resource-lists/job-list.js +++ b/app/static/js/resource-lists/job-list.js @@ -13,7 +13,7 @@ nopaque.resource_lists.JobList = class JobList extends nopaque.resource_lists.Re this.userId = listContainerElement.dataset.userId; if (this.userId === undefined) {return;} app.subscribeUser(this.userId).then((response) => { - app.sockets.users.on('patch_user', (patch) => { + app.socket.on('patch_user', (patch) => { if (this.isInitialized) {this.onPatch(patch);} }); }); diff --git a/app/static/js/resource-lists/job-result-list.js b/app/static/js/resource-lists/job-result-list.js index fbea8161..f361306c 100644 --- a/app/static/js/resource-lists/job-result-list.js +++ b/app/static/js/resource-lists/job-result-list.js @@ -9,7 +9,7 @@ nopaque.resource_lists.JobResultList = class JobResultList extends nopaque.resou this.jobId = listContainerElement.dataset.jobId; if (this.userId === undefined || this.jobId === undefined) {return;} app.subscribeUser(this.userId).then((response) => { - app.sockets.users.on('patch_user', (patch) => { + app.socket.on('patch_user', (patch) => { if (this.isInitialized) {this.onPatch(patch);} }); }); diff --git a/app/static/js/resource-lists/spacy-nlp-pipeline-model-list.js b/app/static/js/resource-lists/spacy-nlp-pipeline-model-list.js index 828537bc..eb340a78 100644 --- a/app/static/js/resource-lists/spacy-nlp-pipeline-model-list.js +++ b/app/static/js/resource-lists/spacy-nlp-pipeline-model-list.js @@ -9,7 +9,7 @@ nopaque.resource_lists.SpaCyNLPPipelineModelList = class SpaCyNLPPipelineModelLi this.userId = listContainerElement.dataset.userId; if (this.userId === undefined) {return;} app.subscribeUser(this.userId).then((response) => { - app.sockets.users.on('patch_user', (patch) => { + app.socket.on('patch_user', (patch) => { if (this.isInitialized) {this.onPatch(patch);} }); }); diff --git a/app/static/js/resource-lists/tesseract-ocr-pipeline-model-list.js b/app/static/js/resource-lists/tesseract-ocr-pipeline-model-list.js index 7bcc984f..86d673d9 100644 --- a/app/static/js/resource-lists/tesseract-ocr-pipeline-model-list.js +++ b/app/static/js/resource-lists/tesseract-ocr-pipeline-model-list.js @@ -9,7 +9,7 @@ nopaque.resource_lists.TesseractOCRPipelineModelList = class TesseractOCRPipelin this.userId = listContainerElement.dataset.userId; if (this.userId === undefined) {return;} app.subscribeUser(this.userId).then((response) => { - app.sockets.users.on('patch_user', (patch) => { + app.socket.on('patch_user', (patch) => { if (this.isInitialized) {this.onPatch(patch);} }); });