diff --git a/502.html b/502.html
index 53732a6..ccedbbe 100644
--- a/502.html
+++ b/502.html
@@ -129,7 +129,7 @@
The application is currently launching. The page will automatically refresh once site is
available.
diff --git a/README.md b/README.md
index c2fb511..4b3e25d 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
-# Adminpro
+# society-community-portal
## This project was generated by [Flatlogic Platform](https://flatlogic.com).
diff --git a/app-shell/src/_schema.json b/app-shell/src/_schema.json
index 4009a31..c1141d7 100644
--- a/app-shell/src/_schema.json
+++ b/app-shell/src/_schema.json
@@ -1,4 +1,5 @@
{
"Initial version": "{\"iv\":\"DFwAjJtPtbc8CvjV\",\"encryptedData\":\"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\"}",
- "Society admin portal": "{\"iv\":\"3H2yb9Cm1+0ds1Ow\",\"encryptedData\":\"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\"}"
+ "Society admin portal": "{\"iv\":\"3H2yb9Cm1+0ds1Ow\",\"encryptedData\":\"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\"}",
+ "Society Community portal": "{\"iv\":\"+rK8HORjiw5Wcjed\",\"encryptedData\":\"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\"}"
}
\ No newline at end of file
diff --git a/backend/README.md b/backend/README.md
index a4ebd6c..835df29 100644
--- a/backend/README.md
+++ b/backend/README.md
@@ -1,4 +1,4 @@
-#Adminpro - template backend,
+#society-community-portal - template backend,
#### Run App on local machine:
@@ -38,10 +38,10 @@
- Type this command to creating a new database.
- - `postgres=> CREATE DATABASE db_adminpro;`
+ - `postgres=> CREATE DATABASE db_society_community_portal;`
- Then give that new user privileges to the new database then quit the `psql`.
- - `postgres=> GRANT ALL PRIVILEGES ON DATABASE db_adminpro TO admin;`
+ - `postgres=> GRANT ALL PRIVILEGES ON DATABASE db_society_community_portal TO admin;`
- `postgres=> \q`
---
diff --git a/backend/package.json b/backend/package.json
index 285f97c..4eef1f9 100644
--- a/backend/package.json
+++ b/backend/package.json
@@ -1,6 +1,6 @@
{
- "name": "adminpro",
- "description": "Adminpro - template backend",
+ "name": "societycommunityportal",
+ "description": "society-community-portal - template backend",
"scripts": {
"start": "npm run db:migrate && npm run db:seed && npm run watch",
"db:migrate": "sequelize-cli db:migrate",
diff --git a/backend/src/config.js b/backend/src/config.js
index e08b7dd..3266419 100644
--- a/backend/src/config.js
+++ b/backend/src/config.js
@@ -3,7 +3,7 @@ const os = require('os');
const config = {
gcloud: {
bucket: 'fldemo-files',
- hash: '9c474fb5916d5795b943fa0f92fc45ed',
+ hash: 'afeefb9d49f5b7977577876b99532ac7',
},
bcrypt: {
saltRounds: 12,
@@ -36,7 +36,7 @@ const config = {
},
uploadDir: os.tmpdir(),
email: {
- from: 'Adminpro
',
+ from: 'society-community-portal ',
host: 'email-smtp.us-east-1.amazonaws.com',
port: 587,
auth: {
diff --git a/backend/src/db/api/chatmessages.js b/backend/src/db/api/chatmessages.js
new file mode 100644
index 0000000..9c9533a
--- /dev/null
+++ b/backend/src/db/api/chatmessages.js
@@ -0,0 +1,370 @@
+const db = require('../models');
+const FileDBApi = require('./file');
+const crypto = require('crypto');
+const Utils = require('../utils');
+
+const Sequelize = db.Sequelize;
+const Op = Sequelize.Op;
+
+module.exports = class ChatmessagesDBApi {
+ static async create(data, options) {
+ const currentUser = (options && options.currentUser) || { id: null };
+ const transaction = (options && options.transaction) || undefined;
+
+ const chatmessages = await db.chatmessages.create(
+ {
+ id: data.id || undefined,
+
+ content: data.content || null,
+ created_date: data.created_date || null,
+ importHash: data.importHash || null,
+ createdById: currentUser.id,
+ updatedById: currentUser.id,
+ },
+ { transaction },
+ );
+
+ await chatmessages.setChatroom(data.chatroom || null, {
+ transaction,
+ });
+
+ await chatmessages.setSender(data.sender || null, {
+ transaction,
+ });
+
+ return chatmessages;
+ }
+
+ static async bulkImport(data, options) {
+ const currentUser = (options && options.currentUser) || { id: null };
+ const transaction = (options && options.transaction) || undefined;
+
+ // Prepare data - wrapping individual data transformations in a map() method
+ const chatmessagesData = data.map((item, index) => ({
+ id: item.id || undefined,
+
+ content: item.content || null,
+ created_date: item.created_date || null,
+ importHash: item.importHash || null,
+ createdById: currentUser.id,
+ updatedById: currentUser.id,
+ createdAt: new Date(Date.now() + index * 1000),
+ }));
+
+ // Bulk create items
+ const chatmessages = await db.chatmessages.bulkCreate(chatmessagesData, {
+ transaction,
+ });
+
+ // For each item created, replace relation files
+
+ return chatmessages;
+ }
+
+ static async update(id, data, options) {
+ const currentUser = (options && options.currentUser) || { id: null };
+ const transaction = (options && options.transaction) || undefined;
+
+ const chatmessages = await db.chatmessages.findByPk(
+ id,
+ {},
+ { transaction },
+ );
+
+ const updatePayload = {};
+
+ if (data.content !== undefined) updatePayload.content = data.content;
+
+ if (data.created_date !== undefined)
+ updatePayload.created_date = data.created_date;
+
+ updatePayload.updatedById = currentUser.id;
+
+ await chatmessages.update(updatePayload, { transaction });
+
+ if (data.chatroom !== undefined) {
+ await chatmessages.setChatroom(
+ data.chatroom,
+
+ { transaction },
+ );
+ }
+
+ if (data.sender !== undefined) {
+ await chatmessages.setSender(
+ data.sender,
+
+ { transaction },
+ );
+ }
+
+ return chatmessages;
+ }
+
+ static async deleteByIds(ids, options) {
+ const currentUser = (options && options.currentUser) || { id: null };
+ const transaction = (options && options.transaction) || undefined;
+
+ const chatmessages = await db.chatmessages.findAll({
+ where: {
+ id: {
+ [Op.in]: ids,
+ },
+ },
+ transaction,
+ });
+
+ await db.sequelize.transaction(async (transaction) => {
+ for (const record of chatmessages) {
+ await record.update({ deletedBy: currentUser.id }, { transaction });
+ }
+ for (const record of chatmessages) {
+ await record.destroy({ transaction });
+ }
+ });
+
+ return chatmessages;
+ }
+
+ static async remove(id, options) {
+ const currentUser = (options && options.currentUser) || { id: null };
+ const transaction = (options && options.transaction) || undefined;
+
+ const chatmessages = await db.chatmessages.findByPk(id, options);
+
+ await chatmessages.update(
+ {
+ deletedBy: currentUser.id,
+ },
+ {
+ transaction,
+ },
+ );
+
+ await chatmessages.destroy({
+ transaction,
+ });
+
+ return chatmessages;
+ }
+
+ static async findBy(where, options) {
+ const transaction = (options && options.transaction) || undefined;
+
+ const chatmessages = await db.chatmessages.findOne(
+ { where },
+ { transaction },
+ );
+
+ if (!chatmessages) {
+ return chatmessages;
+ }
+
+ const output = chatmessages.get({ plain: true });
+
+ output.chatroom = await chatmessages.getChatroom({
+ transaction,
+ });
+
+ output.sender = await chatmessages.getSender({
+ transaction,
+ });
+
+ return output;
+ }
+
+ static async findAll(filter, options) {
+ const limit = filter.limit || 0;
+ let offset = 0;
+ let where = {};
+ const currentPage = +filter.page;
+
+ offset = currentPage * limit;
+
+ const orderBy = null;
+
+ const transaction = (options && options.transaction) || undefined;
+
+ let include = [
+ {
+ model: db.chatrooms,
+ as: 'chatroom',
+
+ where: filter.chatroom
+ ? {
+ [Op.or]: [
+ {
+ id: {
+ [Op.in]: filter.chatroom
+ .split('|')
+ .map((term) => Utils.uuid(term)),
+ },
+ },
+ {
+ name: {
+ [Op.or]: filter.chatroom
+ .split('|')
+ .map((term) => ({ [Op.iLike]: `%${term}%` })),
+ },
+ },
+ ],
+ }
+ : {},
+ },
+
+ {
+ model: db.users,
+ as: 'sender',
+
+ where: filter.sender
+ ? {
+ [Op.or]: [
+ {
+ id: {
+ [Op.in]: filter.sender
+ .split('|')
+ .map((term) => Utils.uuid(term)),
+ },
+ },
+ {
+ firstName: {
+ [Op.or]: filter.sender
+ .split('|')
+ .map((term) => ({ [Op.iLike]: `%${term}%` })),
+ },
+ },
+ ],
+ }
+ : {},
+ },
+ ];
+
+ if (filter) {
+ if (filter.id) {
+ where = {
+ ...where,
+ ['id']: Utils.uuid(filter.id),
+ };
+ }
+
+ if (filter.content) {
+ where = {
+ ...where,
+ [Op.and]: Utils.ilike('chatmessages', 'content', filter.content),
+ };
+ }
+
+ if (filter.created_dateRange) {
+ const [start, end] = filter.created_dateRange;
+
+ if (start !== undefined && start !== null && start !== '') {
+ where = {
+ ...where,
+ created_date: {
+ ...where.created_date,
+ [Op.gte]: start,
+ },
+ };
+ }
+
+ if (end !== undefined && end !== null && end !== '') {
+ where = {
+ ...where,
+ created_date: {
+ ...where.created_date,
+ [Op.lte]: end,
+ },
+ };
+ }
+ }
+
+ if (filter.active !== undefined) {
+ where = {
+ ...where,
+ active: filter.active === true || filter.active === 'true',
+ };
+ }
+
+ if (filter.createdAtRange) {
+ const [start, end] = filter.createdAtRange;
+
+ if (start !== undefined && start !== null && start !== '') {
+ where = {
+ ...where,
+ ['createdAt']: {
+ ...where.createdAt,
+ [Op.gte]: start,
+ },
+ };
+ }
+
+ if (end !== undefined && end !== null && end !== '') {
+ where = {
+ ...where,
+ ['createdAt']: {
+ ...where.createdAt,
+ [Op.lte]: end,
+ },
+ };
+ }
+ }
+ }
+
+ const queryOptions = {
+ where,
+ include,
+ distinct: true,
+ order:
+ filter.field && filter.sort
+ ? [[filter.field, filter.sort]]
+ : [['createdAt', 'desc']],
+ transaction: options?.transaction,
+ logging: console.log,
+ };
+
+ if (!options?.countOnly) {
+ queryOptions.limit = limit ? Number(limit) : undefined;
+ queryOptions.offset = offset ? Number(offset) : undefined;
+ }
+
+ try {
+ const { rows, count } = await db.chatmessages.findAndCountAll(
+ queryOptions,
+ );
+
+ return {
+ rows: options?.countOnly ? [] : rows,
+ count: count,
+ };
+ } catch (error) {
+ console.error('Error executing query:', error);
+ throw error;
+ }
+ }
+
+ static async findAllAutocomplete(query, limit, offset) {
+ let where = {};
+
+ if (query) {
+ where = {
+ [Op.or]: [
+ { ['id']: Utils.uuid(query) },
+ Utils.ilike('chatmessages', 'id', query),
+ ],
+ };
+ }
+
+ const records = await db.chatmessages.findAll({
+ attributes: ['id', 'id'],
+ where,
+ limit: limit ? Number(limit) : undefined,
+ offset: offset ? Number(offset) : undefined,
+ orderBy: [['id', 'ASC']],
+ });
+
+ return records.map((record) => ({
+ id: record.id,
+ label: record.id,
+ }));
+ }
+};
diff --git a/backend/src/db/api/chatroomparticipants.js b/backend/src/db/api/chatroomparticipants.js
new file mode 100644
index 0000000..2bf5208
--- /dev/null
+++ b/backend/src/db/api/chatroomparticipants.js
@@ -0,0 +1,334 @@
+const db = require('../models');
+const FileDBApi = require('./file');
+const crypto = require('crypto');
+const Utils = require('../utils');
+
+const Sequelize = db.Sequelize;
+const Op = Sequelize.Op;
+
+module.exports = class ChatroomparticipantsDBApi {
+ static async create(data, options) {
+ const currentUser = (options && options.currentUser) || { id: null };
+ const transaction = (options && options.transaction) || undefined;
+
+ const chatroomparticipants = await db.chatroomparticipants.create(
+ {
+ id: data.id || undefined,
+
+ importHash: data.importHash || null,
+ createdById: currentUser.id,
+ updatedById: currentUser.id,
+ },
+ { transaction },
+ );
+
+ await chatroomparticipants.setChatroom(data.chatroom || null, {
+ transaction,
+ });
+
+ await chatroomparticipants.setUser(data.user || null, {
+ transaction,
+ });
+
+ return chatroomparticipants;
+ }
+
+ static async bulkImport(data, options) {
+ const currentUser = (options && options.currentUser) || { id: null };
+ const transaction = (options && options.transaction) || undefined;
+
+ // Prepare data - wrapping individual data transformations in a map() method
+ const chatroomparticipantsData = data.map((item, index) => ({
+ id: item.id || undefined,
+
+ importHash: item.importHash || null,
+ createdById: currentUser.id,
+ updatedById: currentUser.id,
+ createdAt: new Date(Date.now() + index * 1000),
+ }));
+
+ // Bulk create items
+ const chatroomparticipants = await db.chatroomparticipants.bulkCreate(
+ chatroomparticipantsData,
+ { transaction },
+ );
+
+ // For each item created, replace relation files
+
+ return chatroomparticipants;
+ }
+
+ static async update(id, data, options) {
+ const currentUser = (options && options.currentUser) || { id: null };
+ const transaction = (options && options.transaction) || undefined;
+
+ const chatroomparticipants = await db.chatroomparticipants.findByPk(
+ id,
+ {},
+ { transaction },
+ );
+
+ const updatePayload = {};
+
+ updatePayload.updatedById = currentUser.id;
+
+ await chatroomparticipants.update(updatePayload, { transaction });
+
+ if (data.chatroom !== undefined) {
+ await chatroomparticipants.setChatroom(
+ data.chatroom,
+
+ { transaction },
+ );
+ }
+
+ if (data.user !== undefined) {
+ await chatroomparticipants.setUser(
+ data.user,
+
+ { transaction },
+ );
+ }
+
+ return chatroomparticipants;
+ }
+
+ static async deleteByIds(ids, options) {
+ const currentUser = (options && options.currentUser) || { id: null };
+ const transaction = (options && options.transaction) || undefined;
+
+ const chatroomparticipants = await db.chatroomparticipants.findAll({
+ where: {
+ id: {
+ [Op.in]: ids,
+ },
+ },
+ transaction,
+ });
+
+ await db.sequelize.transaction(async (transaction) => {
+ for (const record of chatroomparticipants) {
+ await record.update({ deletedBy: currentUser.id }, { transaction });
+ }
+ for (const record of chatroomparticipants) {
+ await record.destroy({ transaction });
+ }
+ });
+
+ return chatroomparticipants;
+ }
+
+ static async remove(id, options) {
+ const currentUser = (options && options.currentUser) || { id: null };
+ const transaction = (options && options.transaction) || undefined;
+
+ const chatroomparticipants = await db.chatroomparticipants.findByPk(
+ id,
+ options,
+ );
+
+ await chatroomparticipants.update(
+ {
+ deletedBy: currentUser.id,
+ },
+ {
+ transaction,
+ },
+ );
+
+ await chatroomparticipants.destroy({
+ transaction,
+ });
+
+ return chatroomparticipants;
+ }
+
+ static async findBy(where, options) {
+ const transaction = (options && options.transaction) || undefined;
+
+ const chatroomparticipants = await db.chatroomparticipants.findOne(
+ { where },
+ { transaction },
+ );
+
+ if (!chatroomparticipants) {
+ return chatroomparticipants;
+ }
+
+ const output = chatroomparticipants.get({ plain: true });
+
+ output.chatroom = await chatroomparticipants.getChatroom({
+ transaction,
+ });
+
+ output.user = await chatroomparticipants.getUser({
+ transaction,
+ });
+
+ return output;
+ }
+
+ static async findAll(filter, options) {
+ const limit = filter.limit || 0;
+ let offset = 0;
+ let where = {};
+ const currentPage = +filter.page;
+
+ offset = currentPage * limit;
+
+ const orderBy = null;
+
+ const transaction = (options && options.transaction) || undefined;
+
+ let include = [
+ {
+ model: db.chatrooms,
+ as: 'chatroom',
+
+ where: filter.chatroom
+ ? {
+ [Op.or]: [
+ {
+ id: {
+ [Op.in]: filter.chatroom
+ .split('|')
+ .map((term) => Utils.uuid(term)),
+ },
+ },
+ {
+ name: {
+ [Op.or]: filter.chatroom
+ .split('|')
+ .map((term) => ({ [Op.iLike]: `%${term}%` })),
+ },
+ },
+ ],
+ }
+ : {},
+ },
+
+ {
+ model: db.users,
+ as: 'user',
+
+ where: filter.user
+ ? {
+ [Op.or]: [
+ {
+ id: {
+ [Op.in]: filter.user
+ .split('|')
+ .map((term) => Utils.uuid(term)),
+ },
+ },
+ {
+ firstName: {
+ [Op.or]: filter.user
+ .split('|')
+ .map((term) => ({ [Op.iLike]: `%${term}%` })),
+ },
+ },
+ ],
+ }
+ : {},
+ },
+ ];
+
+ if (filter) {
+ if (filter.id) {
+ where = {
+ ...where,
+ ['id']: Utils.uuid(filter.id),
+ };
+ }
+
+ if (filter.active !== undefined) {
+ where = {
+ ...where,
+ active: filter.active === true || filter.active === 'true',
+ };
+ }
+
+ if (filter.createdAtRange) {
+ const [start, end] = filter.createdAtRange;
+
+ if (start !== undefined && start !== null && start !== '') {
+ where = {
+ ...where,
+ ['createdAt']: {
+ ...where.createdAt,
+ [Op.gte]: start,
+ },
+ };
+ }
+
+ if (end !== undefined && end !== null && end !== '') {
+ where = {
+ ...where,
+ ['createdAt']: {
+ ...where.createdAt,
+ [Op.lte]: end,
+ },
+ };
+ }
+ }
+ }
+
+ const queryOptions = {
+ where,
+ include,
+ distinct: true,
+ order:
+ filter.field && filter.sort
+ ? [[filter.field, filter.sort]]
+ : [['createdAt', 'desc']],
+ transaction: options?.transaction,
+ logging: console.log,
+ };
+
+ if (!options?.countOnly) {
+ queryOptions.limit = limit ? Number(limit) : undefined;
+ queryOptions.offset = offset ? Number(offset) : undefined;
+ }
+
+ try {
+ const { rows, count } = await db.chatroomparticipants.findAndCountAll(
+ queryOptions,
+ );
+
+ return {
+ rows: options?.countOnly ? [] : rows,
+ count: count,
+ };
+ } catch (error) {
+ console.error('Error executing query:', error);
+ throw error;
+ }
+ }
+
+ static async findAllAutocomplete(query, limit, offset) {
+ let where = {};
+
+ if (query) {
+ where = {
+ [Op.or]: [
+ { ['id']: Utils.uuid(query) },
+ Utils.ilike('chatroomparticipants', 'id', query),
+ ],
+ };
+ }
+
+ const records = await db.chatroomparticipants.findAll({
+ attributes: ['id', 'id'],
+ where,
+ limit: limit ? Number(limit) : undefined,
+ offset: offset ? Number(offset) : undefined,
+ orderBy: [['id', 'ASC']],
+ });
+
+ return records.map((record) => ({
+ id: record.id,
+ label: record.id,
+ }));
+ }
+};
diff --git a/backend/src/db/api/chatrooms.js b/backend/src/db/api/chatrooms.js
new file mode 100644
index 0000000..3ab189f
--- /dev/null
+++ b/backend/src/db/api/chatrooms.js
@@ -0,0 +1,297 @@
+const db = require('../models');
+const FileDBApi = require('./file');
+const crypto = require('crypto');
+const Utils = require('../utils');
+
+const Sequelize = db.Sequelize;
+const Op = Sequelize.Op;
+
+module.exports = class ChatroomsDBApi {
+ static async create(data, options) {
+ const currentUser = (options && options.currentUser) || { id: null };
+ const transaction = (options && options.transaction) || undefined;
+
+ const chatrooms = await db.chatrooms.create(
+ {
+ id: data.id || undefined,
+
+ name: data.name || null,
+ type: data.type || null,
+ created_date: data.created_date || null,
+ importHash: data.importHash || null,
+ createdById: currentUser.id,
+ updatedById: currentUser.id,
+ },
+ { transaction },
+ );
+
+ return chatrooms;
+ }
+
+ static async bulkImport(data, options) {
+ const currentUser = (options && options.currentUser) || { id: null };
+ const transaction = (options && options.transaction) || undefined;
+
+ // Prepare data - wrapping individual data transformations in a map() method
+ const chatroomsData = data.map((item, index) => ({
+ id: item.id || undefined,
+
+ name: item.name || null,
+ type: item.type || null,
+ created_date: item.created_date || null,
+ importHash: item.importHash || null,
+ createdById: currentUser.id,
+ updatedById: currentUser.id,
+ createdAt: new Date(Date.now() + index * 1000),
+ }));
+
+ // Bulk create items
+ const chatrooms = await db.chatrooms.bulkCreate(chatroomsData, {
+ transaction,
+ });
+
+ // For each item created, replace relation files
+
+ return chatrooms;
+ }
+
+ static async update(id, data, options) {
+ const currentUser = (options && options.currentUser) || { id: null };
+ const transaction = (options && options.transaction) || undefined;
+
+ const chatrooms = await db.chatrooms.findByPk(id, {}, { transaction });
+
+ const updatePayload = {};
+
+ if (data.name !== undefined) updatePayload.name = data.name;
+
+ if (data.type !== undefined) updatePayload.type = data.type;
+
+ if (data.created_date !== undefined)
+ updatePayload.created_date = data.created_date;
+
+ updatePayload.updatedById = currentUser.id;
+
+ await chatrooms.update(updatePayload, { transaction });
+
+ return chatrooms;
+ }
+
+ static async deleteByIds(ids, options) {
+ const currentUser = (options && options.currentUser) || { id: null };
+ const transaction = (options && options.transaction) || undefined;
+
+ const chatrooms = await db.chatrooms.findAll({
+ where: {
+ id: {
+ [Op.in]: ids,
+ },
+ },
+ transaction,
+ });
+
+ await db.sequelize.transaction(async (transaction) => {
+ for (const record of chatrooms) {
+ await record.update({ deletedBy: currentUser.id }, { transaction });
+ }
+ for (const record of chatrooms) {
+ await record.destroy({ transaction });
+ }
+ });
+
+ return chatrooms;
+ }
+
+ static async remove(id, options) {
+ const currentUser = (options && options.currentUser) || { id: null };
+ const transaction = (options && options.transaction) || undefined;
+
+ const chatrooms = await db.chatrooms.findByPk(id, options);
+
+ await chatrooms.update(
+ {
+ deletedBy: currentUser.id,
+ },
+ {
+ transaction,
+ },
+ );
+
+ await chatrooms.destroy({
+ transaction,
+ });
+
+ return chatrooms;
+ }
+
+ static async findBy(where, options) {
+ const transaction = (options && options.transaction) || undefined;
+
+ const chatrooms = await db.chatrooms.findOne({ where }, { transaction });
+
+ if (!chatrooms) {
+ return chatrooms;
+ }
+
+ const output = chatrooms.get({ plain: true });
+
+ output.chatmessages_chatroom = await chatrooms.getChatmessages_chatroom({
+ transaction,
+ });
+
+ output.chatroomparticipants_chatroom =
+ await chatrooms.getChatroomparticipants_chatroom({
+ transaction,
+ });
+
+ return output;
+ }
+
+ static async findAll(filter, options) {
+ const limit = filter.limit || 0;
+ let offset = 0;
+ let where = {};
+ const currentPage = +filter.page;
+
+ offset = currentPage * limit;
+
+ const orderBy = null;
+
+ const transaction = (options && options.transaction) || undefined;
+
+ let include = [];
+
+ if (filter) {
+ if (filter.id) {
+ where = {
+ ...where,
+ ['id']: Utils.uuid(filter.id),
+ };
+ }
+
+ if (filter.name) {
+ where = {
+ ...where,
+ [Op.and]: Utils.ilike('chatrooms', 'name', filter.name),
+ };
+ }
+
+ if (filter.created_dateRange) {
+ const [start, end] = filter.created_dateRange;
+
+ if (start !== undefined && start !== null && start !== '') {
+ where = {
+ ...where,
+ created_date: {
+ ...where.created_date,
+ [Op.gte]: start,
+ },
+ };
+ }
+
+ if (end !== undefined && end !== null && end !== '') {
+ where = {
+ ...where,
+ created_date: {
+ ...where.created_date,
+ [Op.lte]: end,
+ },
+ };
+ }
+ }
+
+ if (filter.active !== undefined) {
+ where = {
+ ...where,
+ active: filter.active === true || filter.active === 'true',
+ };
+ }
+
+ if (filter.type) {
+ where = {
+ ...where,
+ type: filter.type,
+ };
+ }
+
+ if (filter.createdAtRange) {
+ const [start, end] = filter.createdAtRange;
+
+ if (start !== undefined && start !== null && start !== '') {
+ where = {
+ ...where,
+ ['createdAt']: {
+ ...where.createdAt,
+ [Op.gte]: start,
+ },
+ };
+ }
+
+ if (end !== undefined && end !== null && end !== '') {
+ where = {
+ ...where,
+ ['createdAt']: {
+ ...where.createdAt,
+ [Op.lte]: end,
+ },
+ };
+ }
+ }
+ }
+
+ const queryOptions = {
+ where,
+ include,
+ distinct: true,
+ order:
+ filter.field && filter.sort
+ ? [[filter.field, filter.sort]]
+ : [['createdAt', 'desc']],
+ transaction: options?.transaction,
+ logging: console.log,
+ };
+
+ if (!options?.countOnly) {
+ queryOptions.limit = limit ? Number(limit) : undefined;
+ queryOptions.offset = offset ? Number(offset) : undefined;
+ }
+
+ try {
+ const { rows, count } = await db.chatrooms.findAndCountAll(queryOptions);
+
+ return {
+ rows: options?.countOnly ? [] : rows,
+ count: count,
+ };
+ } catch (error) {
+ console.error('Error executing query:', error);
+ throw error;
+ }
+ }
+
+ static async findAllAutocomplete(query, limit, offset) {
+ let where = {};
+
+ if (query) {
+ where = {
+ [Op.or]: [
+ { ['id']: Utils.uuid(query) },
+ Utils.ilike('chatrooms', 'name', query),
+ ],
+ };
+ }
+
+ const records = await db.chatrooms.findAll({
+ attributes: ['id', 'name'],
+ where,
+ limit: limit ? Number(limit) : undefined,
+ offset: offset ? Number(offset) : undefined,
+ orderBy: [['name', 'ASC']],
+ });
+
+ return records.map((record) => ({
+ id: record.id,
+ label: record.name,
+ }));
+ }
+};
diff --git a/backend/src/db/api/users.js b/backend/src/db/api/users.js
index 3a11e69..daac9c1 100644
--- a/backend/src/db/api/users.js
+++ b/backend/src/db/api/users.js
@@ -267,6 +267,16 @@ module.exports = class UsersDBApi {
const output = users.get({ plain: true });
+ output.chatmessages_sender = await users.getChatmessages_sender({
+ transaction,
+ });
+
+ output.chatroomparticipants_user = await users.getChatroomparticipants_user(
+ {
+ transaction,
+ },
+ );
+
output.avatar = await users.getAvatar({
transaction,
});
diff --git a/backend/src/db/db.config.js b/backend/src/db/db.config.js
index b6a6f18..aae76fb 100644
--- a/backend/src/db/db.config.js
+++ b/backend/src/db/db.config.js
@@ -13,7 +13,7 @@ module.exports = {
username: 'postgres',
dialect: 'postgres',
password: '',
- database: 'db_adminpro',
+ database: 'db_society_community_portal',
host: process.env.DB_HOST || 'localhost',
logging: console.log,
seederStorage: 'sequelize',
diff --git a/backend/src/db/migrations/1750265371768.js b/backend/src/db/migrations/1750265371768.js
new file mode 100644
index 0000000..5f83729
--- /dev/null
+++ b/backend/src/db/migrations/1750265371768.js
@@ -0,0 +1,72 @@
+module.exports = {
+ /**
+ * @param {QueryInterface} queryInterface
+ * @param {Sequelize} Sequelize
+ * @returns {Promise}
+ */
+ async up(queryInterface, Sequelize) {
+ /**
+ * @type {Transaction}
+ */
+ const transaction = await queryInterface.sequelize.transaction();
+ try {
+ await queryInterface.createTable(
+ 'chatrooms',
+ {
+ id: {
+ type: Sequelize.DataTypes.UUID,
+ defaultValue: Sequelize.DataTypes.UUIDV4,
+ primaryKey: true,
+ },
+ createdById: {
+ type: Sequelize.DataTypes.UUID,
+ references: {
+ key: 'id',
+ model: 'users',
+ },
+ },
+ updatedById: {
+ type: Sequelize.DataTypes.UUID,
+ references: {
+ key: 'id',
+ model: 'users',
+ },
+ },
+ createdAt: { type: Sequelize.DataTypes.DATE },
+ updatedAt: { type: Sequelize.DataTypes.DATE },
+ deletedAt: { type: Sequelize.DataTypes.DATE },
+ importHash: {
+ type: Sequelize.DataTypes.STRING(255),
+ allowNull: true,
+ unique: true,
+ },
+ },
+ { transaction },
+ );
+
+ await transaction.commit();
+ } catch (err) {
+ await transaction.rollback();
+ throw err;
+ }
+ },
+ /**
+ * @param {QueryInterface} queryInterface
+ * @param {Sequelize} Sequelize
+ * @returns {Promise}
+ */
+ async down(queryInterface, Sequelize) {
+ /**
+ * @type {Transaction}
+ */
+ const transaction = await queryInterface.sequelize.transaction();
+ try {
+ await queryInterface.dropTable('chatrooms', { transaction });
+
+ await transaction.commit();
+ } catch (err) {
+ await transaction.rollback();
+ throw err;
+ }
+ },
+};
diff --git a/backend/src/db/migrations/1750265419400.js b/backend/src/db/migrations/1750265419400.js
new file mode 100644
index 0000000..ccdeb03
--- /dev/null
+++ b/backend/src/db/migrations/1750265419400.js
@@ -0,0 +1,47 @@
+module.exports = {
+ /**
+ * @param {QueryInterface} queryInterface
+ * @param {Sequelize} Sequelize
+ * @returns {Promise}
+ */
+ async up(queryInterface, Sequelize) {
+ /**
+ * @type {Transaction}
+ */
+ const transaction = await queryInterface.sequelize.transaction();
+ try {
+ await queryInterface.addColumn(
+ 'chatrooms',
+ 'name',
+ {
+ type: Sequelize.DataTypes.TEXT,
+ },
+ { transaction },
+ );
+
+ await transaction.commit();
+ } catch (err) {
+ await transaction.rollback();
+ throw err;
+ }
+ },
+ /**
+ * @param {QueryInterface} queryInterface
+ * @param {Sequelize} Sequelize
+ * @returns {Promise}
+ */
+ async down(queryInterface, Sequelize) {
+ /**
+ * @type {Transaction}
+ */
+ const transaction = await queryInterface.sequelize.transaction();
+ try {
+ await queryInterface.removeColumn('chatrooms', 'name', { transaction });
+
+ await transaction.commit();
+ } catch (err) {
+ await transaction.rollback();
+ throw err;
+ }
+ },
+};
diff --git a/backend/src/db/migrations/1750265455323.js b/backend/src/db/migrations/1750265455323.js
new file mode 100644
index 0000000..2093416
--- /dev/null
+++ b/backend/src/db/migrations/1750265455323.js
@@ -0,0 +1,49 @@
+module.exports = {
+ /**
+ * @param {QueryInterface} queryInterface
+ * @param {Sequelize} Sequelize
+ * @returns {Promise}
+ */
+ async up(queryInterface, Sequelize) {
+ /**
+ * @type {Transaction}
+ */
+ const transaction = await queryInterface.sequelize.transaction();
+ try {
+ await queryInterface.addColumn(
+ 'chatrooms',
+ 'type',
+ {
+ type: Sequelize.DataTypes.ENUM,
+
+ values: ['value'],
+ },
+ { transaction },
+ );
+
+ await transaction.commit();
+ } catch (err) {
+ await transaction.rollback();
+ throw err;
+ }
+ },
+ /**
+ * @param {QueryInterface} queryInterface
+ * @param {Sequelize} Sequelize
+ * @returns {Promise}
+ */
+ async down(queryInterface, Sequelize) {
+ /**
+ * @type {Transaction}
+ */
+ const transaction = await queryInterface.sequelize.transaction();
+ try {
+ await queryInterface.removeColumn('chatrooms', 'type', { transaction });
+
+ await transaction.commit();
+ } catch (err) {
+ await transaction.rollback();
+ throw err;
+ }
+ },
+};
diff --git a/backend/src/db/migrations/1750265515961.js b/backend/src/db/migrations/1750265515961.js
new file mode 100644
index 0000000..eb06bce
--- /dev/null
+++ b/backend/src/db/migrations/1750265515961.js
@@ -0,0 +1,72 @@
+module.exports = {
+ /**
+ * @param {QueryInterface} queryInterface
+ * @param {Sequelize} Sequelize
+ * @returns {Promise}
+ */
+ async up(queryInterface, Sequelize) {
+ /**
+ * @type {Transaction}
+ */
+ const transaction = await queryInterface.sequelize.transaction();
+ try {
+ await queryInterface.createTable(
+ 'chatmessages',
+ {
+ id: {
+ type: Sequelize.DataTypes.UUID,
+ defaultValue: Sequelize.DataTypes.UUIDV4,
+ primaryKey: true,
+ },
+ createdById: {
+ type: Sequelize.DataTypes.UUID,
+ references: {
+ key: 'id',
+ model: 'users',
+ },
+ },
+ updatedById: {
+ type: Sequelize.DataTypes.UUID,
+ references: {
+ key: 'id',
+ model: 'users',
+ },
+ },
+ createdAt: { type: Sequelize.DataTypes.DATE },
+ updatedAt: { type: Sequelize.DataTypes.DATE },
+ deletedAt: { type: Sequelize.DataTypes.DATE },
+ importHash: {
+ type: Sequelize.DataTypes.STRING(255),
+ allowNull: true,
+ unique: true,
+ },
+ },
+ { transaction },
+ );
+
+ await transaction.commit();
+ } catch (err) {
+ await transaction.rollback();
+ throw err;
+ }
+ },
+ /**
+ * @param {QueryInterface} queryInterface
+ * @param {Sequelize} Sequelize
+ * @returns {Promise}
+ */
+ async down(queryInterface, Sequelize) {
+ /**
+ * @type {Transaction}
+ */
+ const transaction = await queryInterface.sequelize.transaction();
+ try {
+ await queryInterface.dropTable('chatmessages', { transaction });
+
+ await transaction.commit();
+ } catch (err) {
+ await transaction.rollback();
+ throw err;
+ }
+ },
+};
diff --git a/backend/src/db/migrations/1750265548741.js b/backend/src/db/migrations/1750265548741.js
new file mode 100644
index 0000000..b78bd05
--- /dev/null
+++ b/backend/src/db/migrations/1750265548741.js
@@ -0,0 +1,49 @@
+module.exports = {
+ /**
+ * @param {QueryInterface} queryInterface
+ * @param {Sequelize} Sequelize
+ * @returns {Promise}
+ */
+ async up(queryInterface, Sequelize) {
+ /**
+ * @type {Transaction}
+ */
+ const transaction = await queryInterface.sequelize.transaction();
+ try {
+ await queryInterface.addColumn(
+ 'chatrooms',
+ 'created_date',
+ {
+ type: Sequelize.DataTypes.DATE,
+ },
+ { transaction },
+ );
+
+ await transaction.commit();
+ } catch (err) {
+ await transaction.rollback();
+ throw err;
+ }
+ },
+ /**
+ * @param {QueryInterface} queryInterface
+ * @param {Sequelize} Sequelize
+ * @returns {Promise}
+ */
+ async down(queryInterface, Sequelize) {
+ /**
+ * @type {Transaction}
+ */
+ const transaction = await queryInterface.sequelize.transaction();
+ try {
+ await queryInterface.removeColumn('chatrooms', 'created_date', {
+ transaction,
+ });
+
+ await transaction.commit();
+ } catch (err) {
+ await transaction.rollback();
+ throw err;
+ }
+ },
+};
diff --git a/backend/src/db/migrations/1750265578751.js b/backend/src/db/migrations/1750265578751.js
new file mode 100644
index 0000000..144a251
--- /dev/null
+++ b/backend/src/db/migrations/1750265578751.js
@@ -0,0 +1,49 @@
+module.exports = {
+ /**
+ * @param {QueryInterface} queryInterface
+ * @param {Sequelize} Sequelize
+ * @returns {Promise}
+ */
+ async up(queryInterface, Sequelize) {
+ /**
+ * @type {Transaction}
+ */
+ const transaction = await queryInterface.sequelize.transaction();
+ try {
+ await queryInterface.addColumn(
+ 'chatmessages',
+ 'content',
+ {
+ type: Sequelize.DataTypes.TEXT,
+ },
+ { transaction },
+ );
+
+ await transaction.commit();
+ } catch (err) {
+ await transaction.rollback();
+ throw err;
+ }
+ },
+ /**
+ * @param {QueryInterface} queryInterface
+ * @param {Sequelize} Sequelize
+ * @returns {Promise}
+ */
+ async down(queryInterface, Sequelize) {
+ /**
+ * @type {Transaction}
+ */
+ const transaction = await queryInterface.sequelize.transaction();
+ try {
+ await queryInterface.removeColumn('chatmessages', 'content', {
+ transaction,
+ });
+
+ await transaction.commit();
+ } catch (err) {
+ await transaction.rollback();
+ throw err;
+ }
+ },
+};
diff --git a/backend/src/db/migrations/1750265604945.js b/backend/src/db/migrations/1750265604945.js
new file mode 100644
index 0000000..037132a
--- /dev/null
+++ b/backend/src/db/migrations/1750265604945.js
@@ -0,0 +1,49 @@
+module.exports = {
+ /**
+ * @param {QueryInterface} queryInterface
+ * @param {Sequelize} Sequelize
+ * @returns {Promise}
+ */
+ async up(queryInterface, Sequelize) {
+ /**
+ * @type {Transaction}
+ */
+ const transaction = await queryInterface.sequelize.transaction();
+ try {
+ await queryInterface.addColumn(
+ 'chatmessages',
+ 'created_date',
+ {
+ type: Sequelize.DataTypes.DATE,
+ },
+ { transaction },
+ );
+
+ await transaction.commit();
+ } catch (err) {
+ await transaction.rollback();
+ throw err;
+ }
+ },
+ /**
+ * @param {QueryInterface} queryInterface
+ * @param {Sequelize} Sequelize
+ * @returns {Promise}
+ */
+ async down(queryInterface, Sequelize) {
+ /**
+ * @type {Transaction}
+ */
+ const transaction = await queryInterface.sequelize.transaction();
+ try {
+ await queryInterface.removeColumn('chatmessages', 'created_date', {
+ transaction,
+ });
+
+ await transaction.commit();
+ } catch (err) {
+ await transaction.rollback();
+ throw err;
+ }
+ },
+};
diff --git a/backend/src/db/migrations/1750265634411.js b/backend/src/db/migrations/1750265634411.js
new file mode 100644
index 0000000..9000cdf
--- /dev/null
+++ b/backend/src/db/migrations/1750265634411.js
@@ -0,0 +1,72 @@
+module.exports = {
+ /**
+ * @param {QueryInterface} queryInterface
+ * @param {Sequelize} Sequelize
+ * @returns {Promise}
+ */
+ async up(queryInterface, Sequelize) {
+ /**
+ * @type {Transaction}
+ */
+ const transaction = await queryInterface.sequelize.transaction();
+ try {
+ await queryInterface.createTable(
+ 'chatroomparticipants',
+ {
+ id: {
+ type: Sequelize.DataTypes.UUID,
+ defaultValue: Sequelize.DataTypes.UUIDV4,
+ primaryKey: true,
+ },
+ createdById: {
+ type: Sequelize.DataTypes.UUID,
+ references: {
+ key: 'id',
+ model: 'users',
+ },
+ },
+ updatedById: {
+ type: Sequelize.DataTypes.UUID,
+ references: {
+ key: 'id',
+ model: 'users',
+ },
+ },
+ createdAt: { type: Sequelize.DataTypes.DATE },
+ updatedAt: { type: Sequelize.DataTypes.DATE },
+ deletedAt: { type: Sequelize.DataTypes.DATE },
+ importHash: {
+ type: Sequelize.DataTypes.STRING(255),
+ allowNull: true,
+ unique: true,
+ },
+ },
+ { transaction },
+ );
+
+ await transaction.commit();
+ } catch (err) {
+ await transaction.rollback();
+ throw err;
+ }
+ },
+ /**
+ * @param {QueryInterface} queryInterface
+ * @param {Sequelize} Sequelize
+ * @returns {Promise}
+ */
+ async down(queryInterface, Sequelize) {
+ /**
+ * @type {Transaction}
+ */
+ const transaction = await queryInterface.sequelize.transaction();
+ try {
+ await queryInterface.dropTable('chatroomparticipants', { transaction });
+
+ await transaction.commit();
+ } catch (err) {
+ await transaction.rollback();
+ throw err;
+ }
+ },
+};
diff --git a/backend/src/db/migrations/1750265662159.js b/backend/src/db/migrations/1750265662159.js
new file mode 100644
index 0000000..ee3dbe9
--- /dev/null
+++ b/backend/src/db/migrations/1750265662159.js
@@ -0,0 +1,54 @@
+module.exports = {
+ /**
+ * @param {QueryInterface} queryInterface
+ * @param {Sequelize} Sequelize
+ * @returns {Promise}
+ */
+ async up(queryInterface, Sequelize) {
+ /**
+ * @type {Transaction}
+ */
+ const transaction = await queryInterface.sequelize.transaction();
+ try {
+ await queryInterface.addColumn(
+ 'chatroomparticipants',
+ 'chatroomId',
+ {
+ type: Sequelize.DataTypes.UUID,
+
+ references: {
+ model: 'chatrooms',
+ key: 'id',
+ },
+ },
+ { transaction },
+ );
+
+ await transaction.commit();
+ } catch (err) {
+ await transaction.rollback();
+ throw err;
+ }
+ },
+ /**
+ * @param {QueryInterface} queryInterface
+ * @param {Sequelize} Sequelize
+ * @returns {Promise}
+ */
+ async down(queryInterface, Sequelize) {
+ /**
+ * @type {Transaction}
+ */
+ const transaction = await queryInterface.sequelize.transaction();
+ try {
+ await queryInterface.removeColumn('chatroomparticipants', 'chatroomId', {
+ transaction,
+ });
+
+ await transaction.commit();
+ } catch (err) {
+ await transaction.rollback();
+ throw err;
+ }
+ },
+};
diff --git a/backend/src/db/migrations/1750265689235.js b/backend/src/db/migrations/1750265689235.js
new file mode 100644
index 0000000..55fb170
--- /dev/null
+++ b/backend/src/db/migrations/1750265689235.js
@@ -0,0 +1,54 @@
+module.exports = {
+ /**
+ * @param {QueryInterface} queryInterface
+ * @param {Sequelize} Sequelize
+ * @returns {Promise}
+ */
+ async up(queryInterface, Sequelize) {
+ /**
+ * @type {Transaction}
+ */
+ const transaction = await queryInterface.sequelize.transaction();
+ try {
+ await queryInterface.addColumn(
+ 'chatmessages',
+ 'chatroomId',
+ {
+ type: Sequelize.DataTypes.UUID,
+
+ references: {
+ model: 'chatrooms',
+ key: 'id',
+ },
+ },
+ { transaction },
+ );
+
+ await transaction.commit();
+ } catch (err) {
+ await transaction.rollback();
+ throw err;
+ }
+ },
+ /**
+ * @param {QueryInterface} queryInterface
+ * @param {Sequelize} Sequelize
+ * @returns {Promise}
+ */
+ async down(queryInterface, Sequelize) {
+ /**
+ * @type {Transaction}
+ */
+ const transaction = await queryInterface.sequelize.transaction();
+ try {
+ await queryInterface.removeColumn('chatmessages', 'chatroomId', {
+ transaction,
+ });
+
+ await transaction.commit();
+ } catch (err) {
+ await transaction.rollback();
+ throw err;
+ }
+ },
+};
diff --git a/backend/src/db/migrations/1750265715124.js b/backend/src/db/migrations/1750265715124.js
new file mode 100644
index 0000000..82a65c4
--- /dev/null
+++ b/backend/src/db/migrations/1750265715124.js
@@ -0,0 +1,54 @@
+module.exports = {
+ /**
+ * @param {QueryInterface} queryInterface
+ * @param {Sequelize} Sequelize
+ * @returns {Promise}
+ */
+ async up(queryInterface, Sequelize) {
+ /**
+ * @type {Transaction}
+ */
+ const transaction = await queryInterface.sequelize.transaction();
+ try {
+ await queryInterface.addColumn(
+ 'chatroomparticipants',
+ 'userId',
+ {
+ type: Sequelize.DataTypes.UUID,
+
+ references: {
+ model: 'users',
+ key: 'id',
+ },
+ },
+ { transaction },
+ );
+
+ await transaction.commit();
+ } catch (err) {
+ await transaction.rollback();
+ throw err;
+ }
+ },
+ /**
+ * @param {QueryInterface} queryInterface
+ * @param {Sequelize} Sequelize
+ * @returns {Promise}
+ */
+ async down(queryInterface, Sequelize) {
+ /**
+ * @type {Transaction}
+ */
+ const transaction = await queryInterface.sequelize.transaction();
+ try {
+ await queryInterface.removeColumn('chatroomparticipants', 'userId', {
+ transaction,
+ });
+
+ await transaction.commit();
+ } catch (err) {
+ await transaction.rollback();
+ throw err;
+ }
+ },
+};
diff --git a/backend/src/db/migrations/1750265741614.js b/backend/src/db/migrations/1750265741614.js
new file mode 100644
index 0000000..cf6f5ff
--- /dev/null
+++ b/backend/src/db/migrations/1750265741614.js
@@ -0,0 +1,54 @@
+module.exports = {
+ /**
+ * @param {QueryInterface} queryInterface
+ * @param {Sequelize} Sequelize
+ * @returns {Promise}
+ */
+ async up(queryInterface, Sequelize) {
+ /**
+ * @type {Transaction}
+ */
+ const transaction = await queryInterface.sequelize.transaction();
+ try {
+ await queryInterface.addColumn(
+ 'chatmessages',
+ 'senderId',
+ {
+ type: Sequelize.DataTypes.UUID,
+
+ references: {
+ model: 'users',
+ key: 'id',
+ },
+ },
+ { transaction },
+ );
+
+ await transaction.commit();
+ } catch (err) {
+ await transaction.rollback();
+ throw err;
+ }
+ },
+ /**
+ * @param {QueryInterface} queryInterface
+ * @param {Sequelize} Sequelize
+ * @returns {Promise}
+ */
+ async down(queryInterface, Sequelize) {
+ /**
+ * @type {Transaction}
+ */
+ const transaction = await queryInterface.sequelize.transaction();
+ try {
+ await queryInterface.removeColumn('chatmessages', 'senderId', {
+ transaction,
+ });
+
+ await transaction.commit();
+ } catch (err) {
+ await transaction.rollback();
+ throw err;
+ }
+ },
+};
diff --git a/backend/src/db/models/chatmessages.js b/backend/src/db/models/chatmessages.js
new file mode 100644
index 0000000..a28d989
--- /dev/null
+++ b/backend/src/db/models/chatmessages.js
@@ -0,0 +1,69 @@
+const config = require('../../config');
+const providers = config.providers;
+const crypto = require('crypto');
+const bcrypt = require('bcrypt');
+const moment = require('moment');
+
+module.exports = function (sequelize, DataTypes) {
+ const chatmessages = sequelize.define(
+ 'chatmessages',
+ {
+ id: {
+ type: DataTypes.UUID,
+ defaultValue: DataTypes.UUIDV4,
+ primaryKey: true,
+ },
+
+ content: {
+ type: DataTypes.TEXT,
+ },
+
+ created_date: {
+ type: DataTypes.DATE,
+ },
+
+ importHash: {
+ type: DataTypes.STRING(255),
+ allowNull: true,
+ unique: true,
+ },
+ },
+ {
+ timestamps: true,
+ paranoid: true,
+ freezeTableName: true,
+ },
+ );
+
+ chatmessages.associate = (db) => {
+ /// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity
+
+ //end loop
+
+ db.chatmessages.belongsTo(db.chatrooms, {
+ as: 'chatroom',
+ foreignKey: {
+ name: 'chatroomId',
+ },
+ constraints: false,
+ });
+
+ db.chatmessages.belongsTo(db.users, {
+ as: 'sender',
+ foreignKey: {
+ name: 'senderId',
+ },
+ constraints: false,
+ });
+
+ db.chatmessages.belongsTo(db.users, {
+ as: 'createdBy',
+ });
+
+ db.chatmessages.belongsTo(db.users, {
+ as: 'updatedBy',
+ });
+ };
+
+ return chatmessages;
+};
diff --git a/backend/src/db/models/chatroomparticipants.js b/backend/src/db/models/chatroomparticipants.js
new file mode 100644
index 0000000..cbdc007
--- /dev/null
+++ b/backend/src/db/models/chatroomparticipants.js
@@ -0,0 +1,61 @@
+const config = require('../../config');
+const providers = config.providers;
+const crypto = require('crypto');
+const bcrypt = require('bcrypt');
+const moment = require('moment');
+
+module.exports = function (sequelize, DataTypes) {
+ const chatroomparticipants = sequelize.define(
+ 'chatroomparticipants',
+ {
+ id: {
+ type: DataTypes.UUID,
+ defaultValue: DataTypes.UUIDV4,
+ primaryKey: true,
+ },
+
+ importHash: {
+ type: DataTypes.STRING(255),
+ allowNull: true,
+ unique: true,
+ },
+ },
+ {
+ timestamps: true,
+ paranoid: true,
+ freezeTableName: true,
+ },
+ );
+
+ chatroomparticipants.associate = (db) => {
+ /// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity
+
+ //end loop
+
+ db.chatroomparticipants.belongsTo(db.chatrooms, {
+ as: 'chatroom',
+ foreignKey: {
+ name: 'chatroomId',
+ },
+ constraints: false,
+ });
+
+ db.chatroomparticipants.belongsTo(db.users, {
+ as: 'user',
+ foreignKey: {
+ name: 'userId',
+ },
+ constraints: false,
+ });
+
+ db.chatroomparticipants.belongsTo(db.users, {
+ as: 'createdBy',
+ });
+
+ db.chatroomparticipants.belongsTo(db.users, {
+ as: 'updatedBy',
+ });
+ };
+
+ return chatroomparticipants;
+};
diff --git a/backend/src/db/models/chatrooms.js b/backend/src/db/models/chatrooms.js
new file mode 100644
index 0000000..b52d3b9
--- /dev/null
+++ b/backend/src/db/models/chatrooms.js
@@ -0,0 +1,75 @@
+const config = require('../../config');
+const providers = config.providers;
+const crypto = require('crypto');
+const bcrypt = require('bcrypt');
+const moment = require('moment');
+
+module.exports = function (sequelize, DataTypes) {
+ const chatrooms = sequelize.define(
+ 'chatrooms',
+ {
+ id: {
+ type: DataTypes.UUID,
+ defaultValue: DataTypes.UUIDV4,
+ primaryKey: true,
+ },
+
+ name: {
+ type: DataTypes.TEXT,
+ },
+
+ type: {
+ type: DataTypes.ENUM,
+
+ values: ['value'],
+ },
+
+ created_date: {
+ type: DataTypes.DATE,
+ },
+
+ importHash: {
+ type: DataTypes.STRING(255),
+ allowNull: true,
+ unique: true,
+ },
+ },
+ {
+ timestamps: true,
+ paranoid: true,
+ freezeTableName: true,
+ },
+ );
+
+ chatrooms.associate = (db) => {
+ /// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity
+
+ db.chatrooms.hasMany(db.chatmessages, {
+ as: 'chatmessages_chatroom',
+ foreignKey: {
+ name: 'chatroomId',
+ },
+ constraints: false,
+ });
+
+ db.chatrooms.hasMany(db.chatroomparticipants, {
+ as: 'chatroomparticipants_chatroom',
+ foreignKey: {
+ name: 'chatroomId',
+ },
+ constraints: false,
+ });
+
+ //end loop
+
+ db.chatrooms.belongsTo(db.users, {
+ as: 'createdBy',
+ });
+
+ db.chatrooms.belongsTo(db.users, {
+ as: 'updatedBy',
+ });
+ };
+
+ return chatrooms;
+};
diff --git a/backend/src/db/models/users.js b/backend/src/db/models/users.js
index 74e549e..60179d4 100644
--- a/backend/src/db/models/users.js
+++ b/backend/src/db/models/users.js
@@ -102,6 +102,22 @@ module.exports = function (sequelize, DataTypes) {
/// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity
+ db.users.hasMany(db.chatmessages, {
+ as: 'chatmessages_sender',
+ foreignKey: {
+ name: 'senderId',
+ },
+ constraints: false,
+ });
+
+ db.users.hasMany(db.chatroomparticipants, {
+ as: 'chatroomparticipants_user',
+ foreignKey: {
+ name: 'userId',
+ },
+ constraints: false,
+ });
+
//end loop
db.users.belongsTo(db.roles, {
diff --git a/backend/src/db/seeders/20200430130760-user-roles.js b/backend/src/db/seeders/20200430130760-user-roles.js
index 6314b53..ace75c1 100644
--- a/backend/src/db/seeders/20200430130760-user-roles.js
+++ b/backend/src/db/seeders/20200430130760-user-roles.js
@@ -104,6 +104,9 @@ module.exports = {
'reports',
'roles',
'permissions',
+ 'chatrooms',
+ 'chatmessages',
+ 'chatroomparticipants',
,
];
await queryInterface.bulkInsert(
@@ -529,6 +532,81 @@ primary key ("roles_permissionsId", "permissionId")
permissionId: getId('DELETE_PERMISSIONS'),
},
+ {
+ createdAt,
+ updatedAt,
+ roles_permissionsId: getId('Administrator'),
+ permissionId: getId('CREATE_CHATROOMS'),
+ },
+ {
+ createdAt,
+ updatedAt,
+ roles_permissionsId: getId('Administrator'),
+ permissionId: getId('READ_CHATROOMS'),
+ },
+ {
+ createdAt,
+ updatedAt,
+ roles_permissionsId: getId('Administrator'),
+ permissionId: getId('UPDATE_CHATROOMS'),
+ },
+ {
+ createdAt,
+ updatedAt,
+ roles_permissionsId: getId('Administrator'),
+ permissionId: getId('DELETE_CHATROOMS'),
+ },
+
+ {
+ createdAt,
+ updatedAt,
+ roles_permissionsId: getId('Administrator'),
+ permissionId: getId('CREATE_CHATMESSAGES'),
+ },
+ {
+ createdAt,
+ updatedAt,
+ roles_permissionsId: getId('Administrator'),
+ permissionId: getId('READ_CHATMESSAGES'),
+ },
+ {
+ createdAt,
+ updatedAt,
+ roles_permissionsId: getId('Administrator'),
+ permissionId: getId('UPDATE_CHATMESSAGES'),
+ },
+ {
+ createdAt,
+ updatedAt,
+ roles_permissionsId: getId('Administrator'),
+ permissionId: getId('DELETE_CHATMESSAGES'),
+ },
+
+ {
+ createdAt,
+ updatedAt,
+ roles_permissionsId: getId('Administrator'),
+ permissionId: getId('CREATE_CHATROOMPARTICIPANTS'),
+ },
+ {
+ createdAt,
+ updatedAt,
+ roles_permissionsId: getId('Administrator'),
+ permissionId: getId('READ_CHATROOMPARTICIPANTS'),
+ },
+ {
+ createdAt,
+ updatedAt,
+ roles_permissionsId: getId('Administrator'),
+ permissionId: getId('UPDATE_CHATROOMPARTICIPANTS'),
+ },
+ {
+ createdAt,
+ updatedAt,
+ roles_permissionsId: getId('Administrator'),
+ permissionId: getId('DELETE_CHATROOMPARTICIPANTS'),
+ },
+
{
createdAt,
updatedAt,
diff --git a/backend/src/db/seeders/20231127130745-sample-data.js b/backend/src/db/seeders/20231127130745-sample-data.js
index 53b3954..f0f7db6 100644
--- a/backend/src/db/seeders/20231127130745-sample-data.js
+++ b/backend/src/db/seeders/20231127130745-sample-data.js
@@ -5,6 +5,12 @@ const Departments = db.departments;
const Reports = db.reports;
+const Chatrooms = db.chatrooms;
+
+const Chatmessages = db.chatmessages;
+
+const Chatroomparticipants = db.chatroomparticipants;
+
const DepartmentsData = [
{
name: 'Human Resources',
@@ -29,6 +35,12 @@ const DepartmentsData = [
// type code here for "relation_many" field
},
+
+ {
+ name: 'Sales',
+
+ // type code here for "relation_many" field
+ },
];
const ReportsData = [
@@ -55,21 +67,391 @@ const ReportsData = [
created_date: new Date('2023-04-05T11:15:00Z'),
},
+
+ {
+ title: 'Sales Performance Review',
+
+ created_date: new Date('2023-05-25T16:45:00Z'),
+ },
+];
+
+const ChatroomsData = [
+ {
+ name: 'Charles Darwin',
+
+ type: 'value',
+
+ created_date: new Date(Date.now()),
+ },
+
+ {
+ name: 'Emil Fischer',
+
+ type: 'value',
+
+ created_date: new Date(Date.now()),
+ },
+
+ {
+ name: 'Johannes Kepler',
+
+ type: 'value',
+
+ created_date: new Date(Date.now()),
+ },
+
+ {
+ name: 'Dmitri Mendeleev',
+
+ type: 'value',
+
+ created_date: new Date(Date.now()),
+ },
+
+ {
+ name: 'August Kekule',
+
+ type: 'value',
+
+ created_date: new Date(Date.now()),
+ },
+];
+
+const ChatmessagesData = [
+ {
+ content: 'Max Planck',
+
+ created_date: new Date(Date.now()),
+
+ // type code here for "relation_one" field
+
+ // type code here for "relation_one" field
+ },
+
+ {
+ content: 'Enrico Fermi',
+
+ created_date: new Date(Date.now()),
+
+ // type code here for "relation_one" field
+
+ // type code here for "relation_one" field
+ },
+
+ {
+ content: 'Charles Darwin',
+
+ created_date: new Date(Date.now()),
+
+ // type code here for "relation_one" field
+
+ // type code here for "relation_one" field
+ },
+
+ {
+ content: 'Franz Boas',
+
+ created_date: new Date(Date.now()),
+
+ // type code here for "relation_one" field
+
+ // type code here for "relation_one" field
+ },
+
+ {
+ content: 'Dmitri Mendeleev',
+
+ created_date: new Date(Date.now()),
+
+ // type code here for "relation_one" field
+
+ // type code here for "relation_one" field
+ },
+];
+
+const ChatroomparticipantsData = [
+ {
+ // type code here for "relation_one" field
+ // type code here for "relation_one" field
+ },
+
+ {
+ // type code here for "relation_one" field
+ // type code here for "relation_one" field
+ },
+
+ {
+ // type code here for "relation_one" field
+ // type code here for "relation_one" field
+ },
+
+ {
+ // type code here for "relation_one" field
+ // type code here for "relation_one" field
+ },
+
+ {
+ // type code here for "relation_one" field
+ // type code here for "relation_one" field
+ },
];
// Similar logic for "relation_many"
// Similar logic for "relation_many"
+async function associateChatmessageWithChatroom() {
+ const relatedChatroom0 = await Chatrooms.findOne({
+ offset: Math.floor(Math.random() * (await Chatrooms.count())),
+ });
+ const Chatmessage0 = await Chatmessages.findOne({
+ order: [['id', 'ASC']],
+ offset: 0,
+ });
+ if (Chatmessage0?.setChatroom) {
+ await Chatmessage0.setChatroom(relatedChatroom0);
+ }
+
+ const relatedChatroom1 = await Chatrooms.findOne({
+ offset: Math.floor(Math.random() * (await Chatrooms.count())),
+ });
+ const Chatmessage1 = await Chatmessages.findOne({
+ order: [['id', 'ASC']],
+ offset: 1,
+ });
+ if (Chatmessage1?.setChatroom) {
+ await Chatmessage1.setChatroom(relatedChatroom1);
+ }
+
+ const relatedChatroom2 = await Chatrooms.findOne({
+ offset: Math.floor(Math.random() * (await Chatrooms.count())),
+ });
+ const Chatmessage2 = await Chatmessages.findOne({
+ order: [['id', 'ASC']],
+ offset: 2,
+ });
+ if (Chatmessage2?.setChatroom) {
+ await Chatmessage2.setChatroom(relatedChatroom2);
+ }
+
+ const relatedChatroom3 = await Chatrooms.findOne({
+ offset: Math.floor(Math.random() * (await Chatrooms.count())),
+ });
+ const Chatmessage3 = await Chatmessages.findOne({
+ order: [['id', 'ASC']],
+ offset: 3,
+ });
+ if (Chatmessage3?.setChatroom) {
+ await Chatmessage3.setChatroom(relatedChatroom3);
+ }
+
+ const relatedChatroom4 = await Chatrooms.findOne({
+ offset: Math.floor(Math.random() * (await Chatrooms.count())),
+ });
+ const Chatmessage4 = await Chatmessages.findOne({
+ order: [['id', 'ASC']],
+ offset: 4,
+ });
+ if (Chatmessage4?.setChatroom) {
+ await Chatmessage4.setChatroom(relatedChatroom4);
+ }
+}
+
+async function associateChatmessageWithSender() {
+ const relatedSender0 = await Users.findOne({
+ offset: Math.floor(Math.random() * (await Users.count())),
+ });
+ const Chatmessage0 = await Chatmessages.findOne({
+ order: [['id', 'ASC']],
+ offset: 0,
+ });
+ if (Chatmessage0?.setSender) {
+ await Chatmessage0.setSender(relatedSender0);
+ }
+
+ const relatedSender1 = await Users.findOne({
+ offset: Math.floor(Math.random() * (await Users.count())),
+ });
+ const Chatmessage1 = await Chatmessages.findOne({
+ order: [['id', 'ASC']],
+ offset: 1,
+ });
+ if (Chatmessage1?.setSender) {
+ await Chatmessage1.setSender(relatedSender1);
+ }
+
+ const relatedSender2 = await Users.findOne({
+ offset: Math.floor(Math.random() * (await Users.count())),
+ });
+ const Chatmessage2 = await Chatmessages.findOne({
+ order: [['id', 'ASC']],
+ offset: 2,
+ });
+ if (Chatmessage2?.setSender) {
+ await Chatmessage2.setSender(relatedSender2);
+ }
+
+ const relatedSender3 = await Users.findOne({
+ offset: Math.floor(Math.random() * (await Users.count())),
+ });
+ const Chatmessage3 = await Chatmessages.findOne({
+ order: [['id', 'ASC']],
+ offset: 3,
+ });
+ if (Chatmessage3?.setSender) {
+ await Chatmessage3.setSender(relatedSender3);
+ }
+
+ const relatedSender4 = await Users.findOne({
+ offset: Math.floor(Math.random() * (await Users.count())),
+ });
+ const Chatmessage4 = await Chatmessages.findOne({
+ order: [['id', 'ASC']],
+ offset: 4,
+ });
+ if (Chatmessage4?.setSender) {
+ await Chatmessage4.setSender(relatedSender4);
+ }
+}
+
+async function associateChatroomparticipantWithChatroom() {
+ const relatedChatroom0 = await Chatrooms.findOne({
+ offset: Math.floor(Math.random() * (await Chatrooms.count())),
+ });
+ const Chatroomparticipant0 = await Chatroomparticipants.findOne({
+ order: [['id', 'ASC']],
+ offset: 0,
+ });
+ if (Chatroomparticipant0?.setChatroom) {
+ await Chatroomparticipant0.setChatroom(relatedChatroom0);
+ }
+
+ const relatedChatroom1 = await Chatrooms.findOne({
+ offset: Math.floor(Math.random() * (await Chatrooms.count())),
+ });
+ const Chatroomparticipant1 = await Chatroomparticipants.findOne({
+ order: [['id', 'ASC']],
+ offset: 1,
+ });
+ if (Chatroomparticipant1?.setChatroom) {
+ await Chatroomparticipant1.setChatroom(relatedChatroom1);
+ }
+
+ const relatedChatroom2 = await Chatrooms.findOne({
+ offset: Math.floor(Math.random() * (await Chatrooms.count())),
+ });
+ const Chatroomparticipant2 = await Chatroomparticipants.findOne({
+ order: [['id', 'ASC']],
+ offset: 2,
+ });
+ if (Chatroomparticipant2?.setChatroom) {
+ await Chatroomparticipant2.setChatroom(relatedChatroom2);
+ }
+
+ const relatedChatroom3 = await Chatrooms.findOne({
+ offset: Math.floor(Math.random() * (await Chatrooms.count())),
+ });
+ const Chatroomparticipant3 = await Chatroomparticipants.findOne({
+ order: [['id', 'ASC']],
+ offset: 3,
+ });
+ if (Chatroomparticipant3?.setChatroom) {
+ await Chatroomparticipant3.setChatroom(relatedChatroom3);
+ }
+
+ const relatedChatroom4 = await Chatrooms.findOne({
+ offset: Math.floor(Math.random() * (await Chatrooms.count())),
+ });
+ const Chatroomparticipant4 = await Chatroomparticipants.findOne({
+ order: [['id', 'ASC']],
+ offset: 4,
+ });
+ if (Chatroomparticipant4?.setChatroom) {
+ await Chatroomparticipant4.setChatroom(relatedChatroom4);
+ }
+}
+
+async function associateChatroomparticipantWithUser() {
+ const relatedUser0 = await Users.findOne({
+ offset: Math.floor(Math.random() * (await Users.count())),
+ });
+ const Chatroomparticipant0 = await Chatroomparticipants.findOne({
+ order: [['id', 'ASC']],
+ offset: 0,
+ });
+ if (Chatroomparticipant0?.setUser) {
+ await Chatroomparticipant0.setUser(relatedUser0);
+ }
+
+ const relatedUser1 = await Users.findOne({
+ offset: Math.floor(Math.random() * (await Users.count())),
+ });
+ const Chatroomparticipant1 = await Chatroomparticipants.findOne({
+ order: [['id', 'ASC']],
+ offset: 1,
+ });
+ if (Chatroomparticipant1?.setUser) {
+ await Chatroomparticipant1.setUser(relatedUser1);
+ }
+
+ const relatedUser2 = await Users.findOne({
+ offset: Math.floor(Math.random() * (await Users.count())),
+ });
+ const Chatroomparticipant2 = await Chatroomparticipants.findOne({
+ order: [['id', 'ASC']],
+ offset: 2,
+ });
+ if (Chatroomparticipant2?.setUser) {
+ await Chatroomparticipant2.setUser(relatedUser2);
+ }
+
+ const relatedUser3 = await Users.findOne({
+ offset: Math.floor(Math.random() * (await Users.count())),
+ });
+ const Chatroomparticipant3 = await Chatroomparticipants.findOne({
+ order: [['id', 'ASC']],
+ offset: 3,
+ });
+ if (Chatroomparticipant3?.setUser) {
+ await Chatroomparticipant3.setUser(relatedUser3);
+ }
+
+ const relatedUser4 = await Users.findOne({
+ offset: Math.floor(Math.random() * (await Users.count())),
+ });
+ const Chatroomparticipant4 = await Chatroomparticipants.findOne({
+ order: [['id', 'ASC']],
+ offset: 4,
+ });
+ if (Chatroomparticipant4?.setUser) {
+ await Chatroomparticipant4.setUser(relatedUser4);
+ }
+}
+
module.exports = {
up: async (queryInterface, Sequelize) => {
await Departments.bulkCreate(DepartmentsData);
await Reports.bulkCreate(ReportsData);
+ await Chatrooms.bulkCreate(ChatroomsData);
+
+ await Chatmessages.bulkCreate(ChatmessagesData);
+
+ await Chatroomparticipants.bulkCreate(ChatroomparticipantsData);
+
await Promise.all([
// Similar logic for "relation_many"
+
// Similar logic for "relation_many"
+
+ await associateChatmessageWithChatroom(),
+
+ await associateChatmessageWithSender(),
+
+ await associateChatroomparticipantWithChatroom(),
+
+ await associateChatroomparticipantWithUser(),
]);
},
@@ -77,5 +459,11 @@ module.exports = {
await queryInterface.bulkDelete('departments', null, {});
await queryInterface.bulkDelete('reports', null, {});
+
+ await queryInterface.bulkDelete('chatrooms', null, {});
+
+ await queryInterface.bulkDelete('chatmessages', null, {});
+
+ await queryInterface.bulkDelete('chatroomparticipants', null, {});
},
};
diff --git a/backend/src/db/seeders/20250618164931.js b/backend/src/db/seeders/20250618164931.js
new file mode 100644
index 0000000..f3d55e8
--- /dev/null
+++ b/backend/src/db/seeders/20250618164931.js
@@ -0,0 +1,87 @@
+const { v4: uuid } = require('uuid');
+const db = require('../models');
+const Sequelize = require('sequelize');
+const config = require('../../config');
+
+module.exports = {
+ /**
+ * @param{import("sequelize").QueryInterface} queryInterface
+ * @return {Promise}
+ */
+ async up(queryInterface) {
+ const createdAt = new Date();
+ const updatedAt = new Date();
+
+ /** @type {Map} */
+ const idMap = new Map();
+
+ /**
+ * @param {string} key
+ * @return {string}
+ */
+ function getId(key) {
+ if (idMap.has(key)) {
+ return idMap.get(key);
+ }
+ const id = uuid();
+ idMap.set(key, id);
+ return id;
+ }
+
+ /**
+ * @param {string} name
+ */
+ function createPermissions(name) {
+ return [
+ {
+ id: getId(`CREATE_${name.toUpperCase()}`),
+ createdAt,
+ updatedAt,
+ name: `CREATE_${name.toUpperCase()}`,
+ },
+ {
+ id: getId(`READ_${name.toUpperCase()}`),
+ createdAt,
+ updatedAt,
+ name: `READ_${name.toUpperCase()}`,
+ },
+ {
+ id: getId(`UPDATE_${name.toUpperCase()}`),
+ createdAt,
+ updatedAt,
+ name: `UPDATE_${name.toUpperCase()}`,
+ },
+ {
+ id: getId(`DELETE_${name.toUpperCase()}`),
+ createdAt,
+ updatedAt,
+ name: `DELETE_${name.toUpperCase()}`,
+ },
+ ];
+ }
+
+ const entities = ['chatrooms'];
+
+ const createdPermissions = entities.flatMap(createPermissions);
+
+ // Add permissions to database
+ await queryInterface.bulkInsert('permissions', createdPermissions);
+ // Get permissions ids
+ const permissionsIds = createdPermissions.map((p) => p.id);
+ // Get admin role
+ const adminRole = await db.roles.findOne({
+ where: { name: config.roles.admin },
+ });
+
+ if (adminRole) {
+ // Add permissions to admin role if it exists
+ await adminRole.addPermissions(permissionsIds);
+ }
+ },
+ down: async (queryInterface, Sequelize) => {
+ await queryInterface.bulkDelete(
+ 'permissions',
+ entities.flatMap(createPermissions),
+ );
+ },
+};
diff --git a/backend/src/db/seeders/20250618165155.js b/backend/src/db/seeders/20250618165155.js
new file mode 100644
index 0000000..44634a0
--- /dev/null
+++ b/backend/src/db/seeders/20250618165155.js
@@ -0,0 +1,87 @@
+const { v4: uuid } = require('uuid');
+const db = require('../models');
+const Sequelize = require('sequelize');
+const config = require('../../config');
+
+module.exports = {
+ /**
+ * @param{import("sequelize").QueryInterface} queryInterface
+ * @return {Promise}
+ */
+ async up(queryInterface) {
+ const createdAt = new Date();
+ const updatedAt = new Date();
+
+ /** @type {Map} */
+ const idMap = new Map();
+
+ /**
+ * @param {string} key
+ * @return {string}
+ */
+ function getId(key) {
+ if (idMap.has(key)) {
+ return idMap.get(key);
+ }
+ const id = uuid();
+ idMap.set(key, id);
+ return id;
+ }
+
+ /**
+ * @param {string} name
+ */
+ function createPermissions(name) {
+ return [
+ {
+ id: getId(`CREATE_${name.toUpperCase()}`),
+ createdAt,
+ updatedAt,
+ name: `CREATE_${name.toUpperCase()}`,
+ },
+ {
+ id: getId(`READ_${name.toUpperCase()}`),
+ createdAt,
+ updatedAt,
+ name: `READ_${name.toUpperCase()}`,
+ },
+ {
+ id: getId(`UPDATE_${name.toUpperCase()}`),
+ createdAt,
+ updatedAt,
+ name: `UPDATE_${name.toUpperCase()}`,
+ },
+ {
+ id: getId(`DELETE_${name.toUpperCase()}`),
+ createdAt,
+ updatedAt,
+ name: `DELETE_${name.toUpperCase()}`,
+ },
+ ];
+ }
+
+ const entities = ['chatmessages'];
+
+ const createdPermissions = entities.flatMap(createPermissions);
+
+ // Add permissions to database
+ await queryInterface.bulkInsert('permissions', createdPermissions);
+ // Get permissions ids
+ const permissionsIds = createdPermissions.map((p) => p.id);
+ // Get admin role
+ const adminRole = await db.roles.findOne({
+ where: { name: config.roles.admin },
+ });
+
+ if (adminRole) {
+ // Add permissions to admin role if it exists
+ await adminRole.addPermissions(permissionsIds);
+ }
+ },
+ down: async (queryInterface, Sequelize) => {
+ await queryInterface.bulkDelete(
+ 'permissions',
+ entities.flatMap(createPermissions),
+ );
+ },
+};
diff --git a/backend/src/db/seeders/20250618165354.js b/backend/src/db/seeders/20250618165354.js
new file mode 100644
index 0000000..01ce861
--- /dev/null
+++ b/backend/src/db/seeders/20250618165354.js
@@ -0,0 +1,87 @@
+const { v4: uuid } = require('uuid');
+const db = require('../models');
+const Sequelize = require('sequelize');
+const config = require('../../config');
+
+module.exports = {
+ /**
+ * @param{import("sequelize").QueryInterface} queryInterface
+ * @return {Promise}
+ */
+ async up(queryInterface) {
+ const createdAt = new Date();
+ const updatedAt = new Date();
+
+ /** @type {Map} */
+ const idMap = new Map();
+
+ /**
+ * @param {string} key
+ * @return {string}
+ */
+ function getId(key) {
+ if (idMap.has(key)) {
+ return idMap.get(key);
+ }
+ const id = uuid();
+ idMap.set(key, id);
+ return id;
+ }
+
+ /**
+ * @param {string} name
+ */
+ function createPermissions(name) {
+ return [
+ {
+ id: getId(`CREATE_${name.toUpperCase()}`),
+ createdAt,
+ updatedAt,
+ name: `CREATE_${name.toUpperCase()}`,
+ },
+ {
+ id: getId(`READ_${name.toUpperCase()}`),
+ createdAt,
+ updatedAt,
+ name: `READ_${name.toUpperCase()}`,
+ },
+ {
+ id: getId(`UPDATE_${name.toUpperCase()}`),
+ createdAt,
+ updatedAt,
+ name: `UPDATE_${name.toUpperCase()}`,
+ },
+ {
+ id: getId(`DELETE_${name.toUpperCase()}`),
+ createdAt,
+ updatedAt,
+ name: `DELETE_${name.toUpperCase()}`,
+ },
+ ];
+ }
+
+ const entities = ['chatroomparticipants'];
+
+ const createdPermissions = entities.flatMap(createPermissions);
+
+ // Add permissions to database
+ await queryInterface.bulkInsert('permissions', createdPermissions);
+ // Get permissions ids
+ const permissionsIds = createdPermissions.map((p) => p.id);
+ // Get admin role
+ const adminRole = await db.roles.findOne({
+ where: { name: config.roles.admin },
+ });
+
+ if (adminRole) {
+ // Add permissions to admin role if it exists
+ await adminRole.addPermissions(permissionsIds);
+ }
+ },
+ down: async (queryInterface, Sequelize) => {
+ await queryInterface.bulkDelete(
+ 'permissions',
+ entities.flatMap(createPermissions),
+ );
+ },
+};
diff --git a/backend/src/index.js b/backend/src/index.js
index 91c52e2..dff70b2 100644
--- a/backend/src/index.js
+++ b/backend/src/index.js
@@ -29,6 +29,12 @@ const rolesRoutes = require('./routes/roles');
const permissionsRoutes = require('./routes/permissions');
+const chatroomsRoutes = require('./routes/chatrooms');
+
+const chatmessagesRoutes = require('./routes/chatmessages');
+
+const chatroomparticipantsRoutes = require('./routes/chatroomparticipants');
+
const getBaseUrl = (url) => {
if (!url) return '';
return url.endsWith('/api') ? url.slice(0, -4) : url;
@@ -39,9 +45,9 @@ const options = {
openapi: '3.0.0',
info: {
version: '1.0.0',
- title: 'Adminpro',
+ title: 'society-community-portal',
description:
- 'Adminpro Online REST API for Testing and Prototyping application. You can perform all major operations with your entities - create, delete and etc.',
+ 'society-community-portal Online REST API for Testing and Prototyping application. You can perform all major operations with your entities - create, delete and etc.',
},
servers: [
{
@@ -124,6 +130,24 @@ app.use(
permissionsRoutes,
);
+app.use(
+ '/api/chatrooms',
+ passport.authenticate('jwt', { session: false }),
+ chatroomsRoutes,
+);
+
+app.use(
+ '/api/chatmessages',
+ passport.authenticate('jwt', { session: false }),
+ chatmessagesRoutes,
+);
+
+app.use(
+ '/api/chatroomparticipants',
+ passport.authenticate('jwt', { session: false }),
+ chatroomparticipantsRoutes,
+);
+
app.use(
'/api/openai',
passport.authenticate('jwt', { session: false }),
diff --git a/backend/src/routes/chatmessages.js b/backend/src/routes/chatmessages.js
new file mode 100644
index 0000000..679a37d
--- /dev/null
+++ b/backend/src/routes/chatmessages.js
@@ -0,0 +1,442 @@
+const express = require('express');
+
+const ChatmessagesService = require('../services/chatmessages');
+const ChatmessagesDBApi = require('../db/api/chatmessages');
+const wrapAsync = require('../helpers').wrapAsync;
+
+const router = express.Router();
+
+const { parse } = require('json2csv');
+
+const { checkCrudPermissions } = require('../middlewares/check-permissions');
+
+router.use(checkCrudPermissions('chatmessages'));
+
+/**
+ * @swagger
+ * components:
+ * schemas:
+ * Chatmessages:
+ * type: object
+ * properties:
+
+ * content:
+ * type: string
+ * default: content
+
+ */
+
+/**
+ * @swagger
+ * tags:
+ * name: Chatmessages
+ * description: The Chatmessages managing API
+ */
+
+/**
+ * @swagger
+ * /api/chatmessages:
+ * post:
+ * security:
+ * - bearerAuth: []
+ * tags: [Chatmessages]
+ * summary: Add new item
+ * description: Add new item
+ * requestBody:
+ * required: true
+ * content:
+ * application/json:
+ * schema:
+ * properties:
+ * data:
+ * description: Data of the updated item
+ * type: object
+ * $ref: "#/components/schemas/Chatmessages"
+ * responses:
+ * 200:
+ * description: The item was successfully added
+ * content:
+ * application/json:
+ * schema:
+ * $ref: "#/components/schemas/Chatmessages"
+ * 401:
+ * $ref: "#/components/responses/UnauthorizedError"
+ * 405:
+ * description: Invalid input data
+ * 500:
+ * description: Some server error
+ */
+router.post(
+ '/',
+ wrapAsync(async (req, res) => {
+ const referer =
+ req.headers.referer ||
+ `${req.protocol}://${req.hostname}${req.originalUrl}`;
+ const link = new URL(referer);
+ await ChatmessagesService.create(
+ req.body.data,
+ req.currentUser,
+ true,
+ link.host,
+ );
+ const payload = true;
+ res.status(200).send(payload);
+ }),
+);
+
+/**
+ * @swagger
+ * /api/budgets/bulk-import:
+ * post:
+ * security:
+ * - bearerAuth: []
+ * tags: [Chatmessages]
+ * summary: Bulk import items
+ * description: Bulk import items
+ * requestBody:
+ * required: true
+ * content:
+ * application/json:
+ * schema:
+ * properties:
+ * data:
+ * description: Data of the updated items
+ * type: array
+ * items:
+ * $ref: "#/components/schemas/Chatmessages"
+ * responses:
+ * 200:
+ * description: The items were successfully imported
+ * content:
+ * application/json:
+ * schema:
+ * $ref: "#/components/schemas/Chatmessages"
+ * 401:
+ * $ref: "#/components/responses/UnauthorizedError"
+ * 405:
+ * description: Invalid input data
+ * 500:
+ * description: Some server error
+ *
+ */
+router.post(
+ '/bulk-import',
+ wrapAsync(async (req, res) => {
+ const referer =
+ req.headers.referer ||
+ `${req.protocol}://${req.hostname}${req.originalUrl}`;
+ const link = new URL(referer);
+ await ChatmessagesService.bulkImport(req, res, true, link.host);
+ const payload = true;
+ res.status(200).send(payload);
+ }),
+);
+
+/**
+ * @swagger
+ * /api/chatmessages/{id}:
+ * put:
+ * security:
+ * - bearerAuth: []
+ * tags: [Chatmessages]
+ * summary: Update the data of the selected item
+ * description: Update the data of the selected item
+ * parameters:
+ * - in: path
+ * name: id
+ * description: Item ID to update
+ * required: true
+ * schema:
+ * type: string
+ * requestBody:
+ * description: Set new item data
+ * required: true
+ * content:
+ * application/json:
+ * schema:
+ * properties:
+ * id:
+ * description: ID of the updated item
+ * type: string
+ * data:
+ * description: Data of the updated item
+ * type: object
+ * $ref: "#/components/schemas/Chatmessages"
+ * required:
+ * - id
+ * responses:
+ * 200:
+ * description: The item data was successfully updated
+ * content:
+ * application/json:
+ * schema:
+ * $ref: "#/components/schemas/Chatmessages"
+ * 400:
+ * description: Invalid ID supplied
+ * 401:
+ * $ref: "#/components/responses/UnauthorizedError"
+ * 404:
+ * description: Item not found
+ * 500:
+ * description: Some server error
+ */
+router.put(
+ '/:id',
+ wrapAsync(async (req, res) => {
+ await ChatmessagesService.update(
+ req.body.data,
+ req.body.id,
+ req.currentUser,
+ );
+ const payload = true;
+ res.status(200).send(payload);
+ }),
+);
+
+/**
+ * @swagger
+ * /api/chatmessages/{id}:
+ * delete:
+ * security:
+ * - bearerAuth: []
+ * tags: [Chatmessages]
+ * summary: Delete the selected item
+ * description: Delete the selected item
+ * parameters:
+ * - in: path
+ * name: id
+ * description: Item ID to delete
+ * required: true
+ * schema:
+ * type: string
+ * responses:
+ * 200:
+ * description: The item was successfully deleted
+ * content:
+ * application/json:
+ * schema:
+ * $ref: "#/components/schemas/Chatmessages"
+ * 400:
+ * description: Invalid ID supplied
+ * 401:
+ * $ref: "#/components/responses/UnauthorizedError"
+ * 404:
+ * description: Item not found
+ * 500:
+ * description: Some server error
+ */
+router.delete(
+ '/:id',
+ wrapAsync(async (req, res) => {
+ await ChatmessagesService.remove(req.params.id, req.currentUser);
+ const payload = true;
+ res.status(200).send(payload);
+ }),
+);
+
+/**
+ * @swagger
+ * /api/chatmessages/deleteByIds:
+ * post:
+ * security:
+ * - bearerAuth: []
+ * tags: [Chatmessages]
+ * summary: Delete the selected item list
+ * description: Delete the selected item list
+ * requestBody:
+ * required: true
+ * content:
+ * application/json:
+ * schema:
+ * properties:
+ * ids:
+ * description: IDs of the updated items
+ * type: array
+ * responses:
+ * 200:
+ * description: The items was successfully deleted
+ * content:
+ * application/json:
+ * schema:
+ * $ref: "#/components/schemas/Chatmessages"
+ * 401:
+ * $ref: "#/components/responses/UnauthorizedError"
+ * 404:
+ * description: Items not found
+ * 500:
+ * description: Some server error
+ */
+router.post(
+ '/deleteByIds',
+ wrapAsync(async (req, res) => {
+ await ChatmessagesService.deleteByIds(req.body.data, req.currentUser);
+ const payload = true;
+ res.status(200).send(payload);
+ }),
+);
+
+/**
+ * @swagger
+ * /api/chatmessages:
+ * get:
+ * security:
+ * - bearerAuth: []
+ * tags: [Chatmessages]
+ * summary: Get all chatmessages
+ * description: Get all chatmessages
+ * responses:
+ * 200:
+ * description: Chatmessages list successfully received
+ * content:
+ * application/json:
+ * schema:
+ * type: array
+ * items:
+ * $ref: "#/components/schemas/Chatmessages"
+ * 401:
+ * $ref: "#/components/responses/UnauthorizedError"
+ * 404:
+ * description: Data not found
+ * 500:
+ * description: Some server error
+ */
+router.get(
+ '/',
+ wrapAsync(async (req, res) => {
+ const filetype = req.query.filetype;
+
+ const currentUser = req.currentUser;
+ const payload = await ChatmessagesDBApi.findAll(req.query, { currentUser });
+ if (filetype && filetype === 'csv') {
+ const fields = ['id', 'content', 'created_date'];
+ const opts = { fields };
+ try {
+ const csv = parse(payload.rows, opts);
+ res.status(200).attachment(csv);
+ res.send(csv);
+ } catch (err) {
+ console.error(err);
+ }
+ } else {
+ res.status(200).send(payload);
+ }
+ }),
+);
+
+/**
+ * @swagger
+ * /api/chatmessages/count:
+ * get:
+ * security:
+ * - bearerAuth: []
+ * tags: [Chatmessages]
+ * summary: Count all chatmessages
+ * description: Count all chatmessages
+ * responses:
+ * 200:
+ * description: Chatmessages count successfully received
+ * content:
+ * application/json:
+ * schema:
+ * type: array
+ * items:
+ * $ref: "#/components/schemas/Chatmessages"
+ * 401:
+ * $ref: "#/components/responses/UnauthorizedError"
+ * 404:
+ * description: Data not found
+ * 500:
+ * description: Some server error
+ */
+router.get(
+ '/count',
+ wrapAsync(async (req, res) => {
+ const currentUser = req.currentUser;
+ const payload = await ChatmessagesDBApi.findAll(req.query, null, {
+ countOnly: true,
+ currentUser,
+ });
+
+ res.status(200).send(payload);
+ }),
+);
+
+/**
+ * @swagger
+ * /api/chatmessages/autocomplete:
+ * get:
+ * security:
+ * - bearerAuth: []
+ * tags: [Chatmessages]
+ * summary: Find all chatmessages that match search criteria
+ * description: Find all chatmessages that match search criteria
+ * responses:
+ * 200:
+ * description: Chatmessages list successfully received
+ * content:
+ * application/json:
+ * schema:
+ * type: array
+ * items:
+ * $ref: "#/components/schemas/Chatmessages"
+ * 401:
+ * $ref: "#/components/responses/UnauthorizedError"
+ * 404:
+ * description: Data not found
+ * 500:
+ * description: Some server error
+ */
+router.get('/autocomplete', async (req, res) => {
+ const payload = await ChatmessagesDBApi.findAllAutocomplete(
+ req.query.query,
+ req.query.limit,
+ req.query.offset,
+ );
+
+ res.status(200).send(payload);
+});
+
+/**
+ * @swagger
+ * /api/chatmessages/{id}:
+ * get:
+ * security:
+ * - bearerAuth: []
+ * tags: [Chatmessages]
+ * summary: Get selected item
+ * description: Get selected item
+ * parameters:
+ * - in: path
+ * name: id
+ * description: ID of item to get
+ * required: true
+ * schema:
+ * type: string
+ * responses:
+ * 200:
+ * description: Selected item successfully received
+ * content:
+ * application/json:
+ * schema:
+ * $ref: "#/components/schemas/Chatmessages"
+ * 400:
+ * description: Invalid ID supplied
+ * 401:
+ * $ref: "#/components/responses/UnauthorizedError"
+ * 404:
+ * description: Item not found
+ * 500:
+ * description: Some server error
+ */
+router.get(
+ '/:id',
+ wrapAsync(async (req, res) => {
+ const payload = await ChatmessagesDBApi.findBy({ id: req.params.id });
+
+ res.status(200).send(payload);
+ }),
+);
+
+router.use('/', require('../helpers').commonErrorHandler);
+
+module.exports = router;
diff --git a/backend/src/routes/chatroomparticipants.js b/backend/src/routes/chatroomparticipants.js
new file mode 100644
index 0000000..5806459
--- /dev/null
+++ b/backend/src/routes/chatroomparticipants.js
@@ -0,0 +1,445 @@
+const express = require('express');
+
+const ChatroomparticipantsService = require('../services/chatroomparticipants');
+const ChatroomparticipantsDBApi = require('../db/api/chatroomparticipants');
+const wrapAsync = require('../helpers').wrapAsync;
+
+const router = express.Router();
+
+const { parse } = require('json2csv');
+
+const { checkCrudPermissions } = require('../middlewares/check-permissions');
+
+router.use(checkCrudPermissions('chatroomparticipants'));
+
+/**
+ * @swagger
+ * components:
+ * schemas:
+ * Chatroomparticipants:
+ * type: object
+ * properties:
+
+ */
+
+/**
+ * @swagger
+ * tags:
+ * name: Chatroomparticipants
+ * description: The Chatroomparticipants managing API
+ */
+
+/**
+ * @swagger
+ * /api/chatroomparticipants:
+ * post:
+ * security:
+ * - bearerAuth: []
+ * tags: [Chatroomparticipants]
+ * summary: Add new item
+ * description: Add new item
+ * requestBody:
+ * required: true
+ * content:
+ * application/json:
+ * schema:
+ * properties:
+ * data:
+ * description: Data of the updated item
+ * type: object
+ * $ref: "#/components/schemas/Chatroomparticipants"
+ * responses:
+ * 200:
+ * description: The item was successfully added
+ * content:
+ * application/json:
+ * schema:
+ * $ref: "#/components/schemas/Chatroomparticipants"
+ * 401:
+ * $ref: "#/components/responses/UnauthorizedError"
+ * 405:
+ * description: Invalid input data
+ * 500:
+ * description: Some server error
+ */
+router.post(
+ '/',
+ wrapAsync(async (req, res) => {
+ const referer =
+ req.headers.referer ||
+ `${req.protocol}://${req.hostname}${req.originalUrl}`;
+ const link = new URL(referer);
+ await ChatroomparticipantsService.create(
+ req.body.data,
+ req.currentUser,
+ true,
+ link.host,
+ );
+ const payload = true;
+ res.status(200).send(payload);
+ }),
+);
+
+/**
+ * @swagger
+ * /api/budgets/bulk-import:
+ * post:
+ * security:
+ * - bearerAuth: []
+ * tags: [Chatroomparticipants]
+ * summary: Bulk import items
+ * description: Bulk import items
+ * requestBody:
+ * required: true
+ * content:
+ * application/json:
+ * schema:
+ * properties:
+ * data:
+ * description: Data of the updated items
+ * type: array
+ * items:
+ * $ref: "#/components/schemas/Chatroomparticipants"
+ * responses:
+ * 200:
+ * description: The items were successfully imported
+ * content:
+ * application/json:
+ * schema:
+ * $ref: "#/components/schemas/Chatroomparticipants"
+ * 401:
+ * $ref: "#/components/responses/UnauthorizedError"
+ * 405:
+ * description: Invalid input data
+ * 500:
+ * description: Some server error
+ *
+ */
+router.post(
+ '/bulk-import',
+ wrapAsync(async (req, res) => {
+ const referer =
+ req.headers.referer ||
+ `${req.protocol}://${req.hostname}${req.originalUrl}`;
+ const link = new URL(referer);
+ await ChatroomparticipantsService.bulkImport(req, res, true, link.host);
+ const payload = true;
+ res.status(200).send(payload);
+ }),
+);
+
+/**
+ * @swagger
+ * /api/chatroomparticipants/{id}:
+ * put:
+ * security:
+ * - bearerAuth: []
+ * tags: [Chatroomparticipants]
+ * summary: Update the data of the selected item
+ * description: Update the data of the selected item
+ * parameters:
+ * - in: path
+ * name: id
+ * description: Item ID to update
+ * required: true
+ * schema:
+ * type: string
+ * requestBody:
+ * description: Set new item data
+ * required: true
+ * content:
+ * application/json:
+ * schema:
+ * properties:
+ * id:
+ * description: ID of the updated item
+ * type: string
+ * data:
+ * description: Data of the updated item
+ * type: object
+ * $ref: "#/components/schemas/Chatroomparticipants"
+ * required:
+ * - id
+ * responses:
+ * 200:
+ * description: The item data was successfully updated
+ * content:
+ * application/json:
+ * schema:
+ * $ref: "#/components/schemas/Chatroomparticipants"
+ * 400:
+ * description: Invalid ID supplied
+ * 401:
+ * $ref: "#/components/responses/UnauthorizedError"
+ * 404:
+ * description: Item not found
+ * 500:
+ * description: Some server error
+ */
+router.put(
+ '/:id',
+ wrapAsync(async (req, res) => {
+ await ChatroomparticipantsService.update(
+ req.body.data,
+ req.body.id,
+ req.currentUser,
+ );
+ const payload = true;
+ res.status(200).send(payload);
+ }),
+);
+
+/**
+ * @swagger
+ * /api/chatroomparticipants/{id}:
+ * delete:
+ * security:
+ * - bearerAuth: []
+ * tags: [Chatroomparticipants]
+ * summary: Delete the selected item
+ * description: Delete the selected item
+ * parameters:
+ * - in: path
+ * name: id
+ * description: Item ID to delete
+ * required: true
+ * schema:
+ * type: string
+ * responses:
+ * 200:
+ * description: The item was successfully deleted
+ * content:
+ * application/json:
+ * schema:
+ * $ref: "#/components/schemas/Chatroomparticipants"
+ * 400:
+ * description: Invalid ID supplied
+ * 401:
+ * $ref: "#/components/responses/UnauthorizedError"
+ * 404:
+ * description: Item not found
+ * 500:
+ * description: Some server error
+ */
+router.delete(
+ '/:id',
+ wrapAsync(async (req, res) => {
+ await ChatroomparticipantsService.remove(req.params.id, req.currentUser);
+ const payload = true;
+ res.status(200).send(payload);
+ }),
+);
+
+/**
+ * @swagger
+ * /api/chatroomparticipants/deleteByIds:
+ * post:
+ * security:
+ * - bearerAuth: []
+ * tags: [Chatroomparticipants]
+ * summary: Delete the selected item list
+ * description: Delete the selected item list
+ * requestBody:
+ * required: true
+ * content:
+ * application/json:
+ * schema:
+ * properties:
+ * ids:
+ * description: IDs of the updated items
+ * type: array
+ * responses:
+ * 200:
+ * description: The items was successfully deleted
+ * content:
+ * application/json:
+ * schema:
+ * $ref: "#/components/schemas/Chatroomparticipants"
+ * 401:
+ * $ref: "#/components/responses/UnauthorizedError"
+ * 404:
+ * description: Items not found
+ * 500:
+ * description: Some server error
+ */
+router.post(
+ '/deleteByIds',
+ wrapAsync(async (req, res) => {
+ await ChatroomparticipantsService.deleteByIds(
+ req.body.data,
+ req.currentUser,
+ );
+ const payload = true;
+ res.status(200).send(payload);
+ }),
+);
+
+/**
+ * @swagger
+ * /api/chatroomparticipants:
+ * get:
+ * security:
+ * - bearerAuth: []
+ * tags: [Chatroomparticipants]
+ * summary: Get all chatroomparticipants
+ * description: Get all chatroomparticipants
+ * responses:
+ * 200:
+ * description: Chatroomparticipants list successfully received
+ * content:
+ * application/json:
+ * schema:
+ * type: array
+ * items:
+ * $ref: "#/components/schemas/Chatroomparticipants"
+ * 401:
+ * $ref: "#/components/responses/UnauthorizedError"
+ * 404:
+ * description: Data not found
+ * 500:
+ * description: Some server error
+ */
+router.get(
+ '/',
+ wrapAsync(async (req, res) => {
+ const filetype = req.query.filetype;
+
+ const currentUser = req.currentUser;
+ const payload = await ChatroomparticipantsDBApi.findAll(req.query, {
+ currentUser,
+ });
+ if (filetype && filetype === 'csv') {
+ const fields = ['id'];
+ const opts = { fields };
+ try {
+ const csv = parse(payload.rows, opts);
+ res.status(200).attachment(csv);
+ res.send(csv);
+ } catch (err) {
+ console.error(err);
+ }
+ } else {
+ res.status(200).send(payload);
+ }
+ }),
+);
+
+/**
+ * @swagger
+ * /api/chatroomparticipants/count:
+ * get:
+ * security:
+ * - bearerAuth: []
+ * tags: [Chatroomparticipants]
+ * summary: Count all chatroomparticipants
+ * description: Count all chatroomparticipants
+ * responses:
+ * 200:
+ * description: Chatroomparticipants count successfully received
+ * content:
+ * application/json:
+ * schema:
+ * type: array
+ * items:
+ * $ref: "#/components/schemas/Chatroomparticipants"
+ * 401:
+ * $ref: "#/components/responses/UnauthorizedError"
+ * 404:
+ * description: Data not found
+ * 500:
+ * description: Some server error
+ */
+router.get(
+ '/count',
+ wrapAsync(async (req, res) => {
+ const currentUser = req.currentUser;
+ const payload = await ChatroomparticipantsDBApi.findAll(req.query, null, {
+ countOnly: true,
+ currentUser,
+ });
+
+ res.status(200).send(payload);
+ }),
+);
+
+/**
+ * @swagger
+ * /api/chatroomparticipants/autocomplete:
+ * get:
+ * security:
+ * - bearerAuth: []
+ * tags: [Chatroomparticipants]
+ * summary: Find all chatroomparticipants that match search criteria
+ * description: Find all chatroomparticipants that match search criteria
+ * responses:
+ * 200:
+ * description: Chatroomparticipants list successfully received
+ * content:
+ * application/json:
+ * schema:
+ * type: array
+ * items:
+ * $ref: "#/components/schemas/Chatroomparticipants"
+ * 401:
+ * $ref: "#/components/responses/UnauthorizedError"
+ * 404:
+ * description: Data not found
+ * 500:
+ * description: Some server error
+ */
+router.get('/autocomplete', async (req, res) => {
+ const payload = await ChatroomparticipantsDBApi.findAllAutocomplete(
+ req.query.query,
+ req.query.limit,
+ req.query.offset,
+ );
+
+ res.status(200).send(payload);
+});
+
+/**
+ * @swagger
+ * /api/chatroomparticipants/{id}:
+ * get:
+ * security:
+ * - bearerAuth: []
+ * tags: [Chatroomparticipants]
+ * summary: Get selected item
+ * description: Get selected item
+ * parameters:
+ * - in: path
+ * name: id
+ * description: ID of item to get
+ * required: true
+ * schema:
+ * type: string
+ * responses:
+ * 200:
+ * description: Selected item successfully received
+ * content:
+ * application/json:
+ * schema:
+ * $ref: "#/components/schemas/Chatroomparticipants"
+ * 400:
+ * description: Invalid ID supplied
+ * 401:
+ * $ref: "#/components/responses/UnauthorizedError"
+ * 404:
+ * description: Item not found
+ * 500:
+ * description: Some server error
+ */
+router.get(
+ '/:id',
+ wrapAsync(async (req, res) => {
+ const payload = await ChatroomparticipantsDBApi.findBy({
+ id: req.params.id,
+ });
+
+ res.status(200).send(payload);
+ }),
+);
+
+router.use('/', require('../helpers').commonErrorHandler);
+
+module.exports = router;
diff --git a/backend/src/routes/chatrooms.js b/backend/src/routes/chatrooms.js
new file mode 100644
index 0000000..add4fa7
--- /dev/null
+++ b/backend/src/routes/chatrooms.js
@@ -0,0 +1,439 @@
+const express = require('express');
+
+const ChatroomsService = require('../services/chatrooms');
+const ChatroomsDBApi = require('../db/api/chatrooms');
+const wrapAsync = require('../helpers').wrapAsync;
+
+const router = express.Router();
+
+const { parse } = require('json2csv');
+
+const { checkCrudPermissions } = require('../middlewares/check-permissions');
+
+router.use(checkCrudPermissions('chatrooms'));
+
+/**
+ * @swagger
+ * components:
+ * schemas:
+ * Chatrooms:
+ * type: object
+ * properties:
+
+ * name:
+ * type: string
+ * default: name
+
+ *
+ */
+
+/**
+ * @swagger
+ * tags:
+ * name: Chatrooms
+ * description: The Chatrooms managing API
+ */
+
+/**
+ * @swagger
+ * /api/chatrooms:
+ * post:
+ * security:
+ * - bearerAuth: []
+ * tags: [Chatrooms]
+ * summary: Add new item
+ * description: Add new item
+ * requestBody:
+ * required: true
+ * content:
+ * application/json:
+ * schema:
+ * properties:
+ * data:
+ * description: Data of the updated item
+ * type: object
+ * $ref: "#/components/schemas/Chatrooms"
+ * responses:
+ * 200:
+ * description: The item was successfully added
+ * content:
+ * application/json:
+ * schema:
+ * $ref: "#/components/schemas/Chatrooms"
+ * 401:
+ * $ref: "#/components/responses/UnauthorizedError"
+ * 405:
+ * description: Invalid input data
+ * 500:
+ * description: Some server error
+ */
+router.post(
+ '/',
+ wrapAsync(async (req, res) => {
+ const referer =
+ req.headers.referer ||
+ `${req.protocol}://${req.hostname}${req.originalUrl}`;
+ const link = new URL(referer);
+ await ChatroomsService.create(
+ req.body.data,
+ req.currentUser,
+ true,
+ link.host,
+ );
+ const payload = true;
+ res.status(200).send(payload);
+ }),
+);
+
+/**
+ * @swagger
+ * /api/budgets/bulk-import:
+ * post:
+ * security:
+ * - bearerAuth: []
+ * tags: [Chatrooms]
+ * summary: Bulk import items
+ * description: Bulk import items
+ * requestBody:
+ * required: true
+ * content:
+ * application/json:
+ * schema:
+ * properties:
+ * data:
+ * description: Data of the updated items
+ * type: array
+ * items:
+ * $ref: "#/components/schemas/Chatrooms"
+ * responses:
+ * 200:
+ * description: The items were successfully imported
+ * content:
+ * application/json:
+ * schema:
+ * $ref: "#/components/schemas/Chatrooms"
+ * 401:
+ * $ref: "#/components/responses/UnauthorizedError"
+ * 405:
+ * description: Invalid input data
+ * 500:
+ * description: Some server error
+ *
+ */
+router.post(
+ '/bulk-import',
+ wrapAsync(async (req, res) => {
+ const referer =
+ req.headers.referer ||
+ `${req.protocol}://${req.hostname}${req.originalUrl}`;
+ const link = new URL(referer);
+ await ChatroomsService.bulkImport(req, res, true, link.host);
+ const payload = true;
+ res.status(200).send(payload);
+ }),
+);
+
+/**
+ * @swagger
+ * /api/chatrooms/{id}:
+ * put:
+ * security:
+ * - bearerAuth: []
+ * tags: [Chatrooms]
+ * summary: Update the data of the selected item
+ * description: Update the data of the selected item
+ * parameters:
+ * - in: path
+ * name: id
+ * description: Item ID to update
+ * required: true
+ * schema:
+ * type: string
+ * requestBody:
+ * description: Set new item data
+ * required: true
+ * content:
+ * application/json:
+ * schema:
+ * properties:
+ * id:
+ * description: ID of the updated item
+ * type: string
+ * data:
+ * description: Data of the updated item
+ * type: object
+ * $ref: "#/components/schemas/Chatrooms"
+ * required:
+ * - id
+ * responses:
+ * 200:
+ * description: The item data was successfully updated
+ * content:
+ * application/json:
+ * schema:
+ * $ref: "#/components/schemas/Chatrooms"
+ * 400:
+ * description: Invalid ID supplied
+ * 401:
+ * $ref: "#/components/responses/UnauthorizedError"
+ * 404:
+ * description: Item not found
+ * 500:
+ * description: Some server error
+ */
+router.put(
+ '/:id',
+ wrapAsync(async (req, res) => {
+ await ChatroomsService.update(req.body.data, req.body.id, req.currentUser);
+ const payload = true;
+ res.status(200).send(payload);
+ }),
+);
+
+/**
+ * @swagger
+ * /api/chatrooms/{id}:
+ * delete:
+ * security:
+ * - bearerAuth: []
+ * tags: [Chatrooms]
+ * summary: Delete the selected item
+ * description: Delete the selected item
+ * parameters:
+ * - in: path
+ * name: id
+ * description: Item ID to delete
+ * required: true
+ * schema:
+ * type: string
+ * responses:
+ * 200:
+ * description: The item was successfully deleted
+ * content:
+ * application/json:
+ * schema:
+ * $ref: "#/components/schemas/Chatrooms"
+ * 400:
+ * description: Invalid ID supplied
+ * 401:
+ * $ref: "#/components/responses/UnauthorizedError"
+ * 404:
+ * description: Item not found
+ * 500:
+ * description: Some server error
+ */
+router.delete(
+ '/:id',
+ wrapAsync(async (req, res) => {
+ await ChatroomsService.remove(req.params.id, req.currentUser);
+ const payload = true;
+ res.status(200).send(payload);
+ }),
+);
+
+/**
+ * @swagger
+ * /api/chatrooms/deleteByIds:
+ * post:
+ * security:
+ * - bearerAuth: []
+ * tags: [Chatrooms]
+ * summary: Delete the selected item list
+ * description: Delete the selected item list
+ * requestBody:
+ * required: true
+ * content:
+ * application/json:
+ * schema:
+ * properties:
+ * ids:
+ * description: IDs of the updated items
+ * type: array
+ * responses:
+ * 200:
+ * description: The items was successfully deleted
+ * content:
+ * application/json:
+ * schema:
+ * $ref: "#/components/schemas/Chatrooms"
+ * 401:
+ * $ref: "#/components/responses/UnauthorizedError"
+ * 404:
+ * description: Items not found
+ * 500:
+ * description: Some server error
+ */
+router.post(
+ '/deleteByIds',
+ wrapAsync(async (req, res) => {
+ await ChatroomsService.deleteByIds(req.body.data, req.currentUser);
+ const payload = true;
+ res.status(200).send(payload);
+ }),
+);
+
+/**
+ * @swagger
+ * /api/chatrooms:
+ * get:
+ * security:
+ * - bearerAuth: []
+ * tags: [Chatrooms]
+ * summary: Get all chatrooms
+ * description: Get all chatrooms
+ * responses:
+ * 200:
+ * description: Chatrooms list successfully received
+ * content:
+ * application/json:
+ * schema:
+ * type: array
+ * items:
+ * $ref: "#/components/schemas/Chatrooms"
+ * 401:
+ * $ref: "#/components/responses/UnauthorizedError"
+ * 404:
+ * description: Data not found
+ * 500:
+ * description: Some server error
+ */
+router.get(
+ '/',
+ wrapAsync(async (req, res) => {
+ const filetype = req.query.filetype;
+
+ const currentUser = req.currentUser;
+ const payload = await ChatroomsDBApi.findAll(req.query, { currentUser });
+ if (filetype && filetype === 'csv') {
+ const fields = ['id', 'name', 'created_date'];
+ const opts = { fields };
+ try {
+ const csv = parse(payload.rows, opts);
+ res.status(200).attachment(csv);
+ res.send(csv);
+ } catch (err) {
+ console.error(err);
+ }
+ } else {
+ res.status(200).send(payload);
+ }
+ }),
+);
+
+/**
+ * @swagger
+ * /api/chatrooms/count:
+ * get:
+ * security:
+ * - bearerAuth: []
+ * tags: [Chatrooms]
+ * summary: Count all chatrooms
+ * description: Count all chatrooms
+ * responses:
+ * 200:
+ * description: Chatrooms count successfully received
+ * content:
+ * application/json:
+ * schema:
+ * type: array
+ * items:
+ * $ref: "#/components/schemas/Chatrooms"
+ * 401:
+ * $ref: "#/components/responses/UnauthorizedError"
+ * 404:
+ * description: Data not found
+ * 500:
+ * description: Some server error
+ */
+router.get(
+ '/count',
+ wrapAsync(async (req, res) => {
+ const currentUser = req.currentUser;
+ const payload = await ChatroomsDBApi.findAll(req.query, null, {
+ countOnly: true,
+ currentUser,
+ });
+
+ res.status(200).send(payload);
+ }),
+);
+
+/**
+ * @swagger
+ * /api/chatrooms/autocomplete:
+ * get:
+ * security:
+ * - bearerAuth: []
+ * tags: [Chatrooms]
+ * summary: Find all chatrooms that match search criteria
+ * description: Find all chatrooms that match search criteria
+ * responses:
+ * 200:
+ * description: Chatrooms list successfully received
+ * content:
+ * application/json:
+ * schema:
+ * type: array
+ * items:
+ * $ref: "#/components/schemas/Chatrooms"
+ * 401:
+ * $ref: "#/components/responses/UnauthorizedError"
+ * 404:
+ * description: Data not found
+ * 500:
+ * description: Some server error
+ */
+router.get('/autocomplete', async (req, res) => {
+ const payload = await ChatroomsDBApi.findAllAutocomplete(
+ req.query.query,
+ req.query.limit,
+ req.query.offset,
+ );
+
+ res.status(200).send(payload);
+});
+
+/**
+ * @swagger
+ * /api/chatrooms/{id}:
+ * get:
+ * security:
+ * - bearerAuth: []
+ * tags: [Chatrooms]
+ * summary: Get selected item
+ * description: Get selected item
+ * parameters:
+ * - in: path
+ * name: id
+ * description: ID of item to get
+ * required: true
+ * schema:
+ * type: string
+ * responses:
+ * 200:
+ * description: Selected item successfully received
+ * content:
+ * application/json:
+ * schema:
+ * $ref: "#/components/schemas/Chatrooms"
+ * 400:
+ * description: Invalid ID supplied
+ * 401:
+ * $ref: "#/components/responses/UnauthorizedError"
+ * 404:
+ * description: Item not found
+ * 500:
+ * description: Some server error
+ */
+router.get(
+ '/:id',
+ wrapAsync(async (req, res) => {
+ const payload = await ChatroomsDBApi.findBy({ id: req.params.id });
+
+ res.status(200).send(payload);
+ }),
+);
+
+router.use('/', require('../helpers').commonErrorHandler);
+
+module.exports = router;
diff --git a/backend/src/services/chatmessages.js b/backend/src/services/chatmessages.js
new file mode 100644
index 0000000..0cd14ac
--- /dev/null
+++ b/backend/src/services/chatmessages.js
@@ -0,0 +1,117 @@
+const db = require('../db/models');
+const ChatmessagesDBApi = require('../db/api/chatmessages');
+const processFile = require('../middlewares/upload');
+const ValidationError = require('./notifications/errors/validation');
+const csv = require('csv-parser');
+const axios = require('axios');
+const config = require('../config');
+const stream = require('stream');
+
+module.exports = class ChatmessagesService {
+ static async create(data, currentUser) {
+ const transaction = await db.sequelize.transaction();
+ try {
+ await ChatmessagesDBApi.create(data, {
+ currentUser,
+ transaction,
+ });
+
+ await transaction.commit();
+ } catch (error) {
+ await transaction.rollback();
+ throw error;
+ }
+ }
+
+ static async bulkImport(req, res, sendInvitationEmails = true, host) {
+ const transaction = await db.sequelize.transaction();
+
+ try {
+ await processFile(req, res);
+ const bufferStream = new stream.PassThrough();
+ const results = [];
+
+ await bufferStream.end(Buffer.from(req.file.buffer, 'utf-8')); // convert Buffer to Stream
+
+ await new Promise((resolve, reject) => {
+ bufferStream
+ .pipe(csv())
+ .on('data', (data) => results.push(data))
+ .on('end', async () => {
+ console.log('CSV results', results);
+ resolve();
+ })
+ .on('error', (error) => reject(error));
+ });
+
+ await ChatmessagesDBApi.bulkImport(results, {
+ transaction,
+ ignoreDuplicates: true,
+ validate: true,
+ currentUser: req.currentUser,
+ });
+
+ await transaction.commit();
+ } catch (error) {
+ await transaction.rollback();
+ throw error;
+ }
+ }
+
+ static async update(data, id, currentUser) {
+ const transaction = await db.sequelize.transaction();
+ try {
+ let chatmessages = await ChatmessagesDBApi.findBy(
+ { id },
+ { transaction },
+ );
+
+ if (!chatmessages) {
+ throw new ValidationError('chatmessagesNotFound');
+ }
+
+ const updatedChatmessages = await ChatmessagesDBApi.update(id, data, {
+ currentUser,
+ transaction,
+ });
+
+ await transaction.commit();
+ return updatedChatmessages;
+ } catch (error) {
+ await transaction.rollback();
+ throw error;
+ }
+ }
+
+ static async deleteByIds(ids, currentUser) {
+ const transaction = await db.sequelize.transaction();
+
+ try {
+ await ChatmessagesDBApi.deleteByIds(ids, {
+ currentUser,
+ transaction,
+ });
+
+ await transaction.commit();
+ } catch (error) {
+ await transaction.rollback();
+ throw error;
+ }
+ }
+
+ static async remove(id, currentUser) {
+ const transaction = await db.sequelize.transaction();
+
+ try {
+ await ChatmessagesDBApi.remove(id, {
+ currentUser,
+ transaction,
+ });
+
+ await transaction.commit();
+ } catch (error) {
+ await transaction.rollback();
+ throw error;
+ }
+ }
+};
diff --git a/backend/src/services/chatroomparticipants.js b/backend/src/services/chatroomparticipants.js
new file mode 100644
index 0000000..675b57f
--- /dev/null
+++ b/backend/src/services/chatroomparticipants.js
@@ -0,0 +1,118 @@
+const db = require('../db/models');
+const ChatroomparticipantsDBApi = require('../db/api/chatroomparticipants');
+const processFile = require('../middlewares/upload');
+const ValidationError = require('./notifications/errors/validation');
+const csv = require('csv-parser');
+const axios = require('axios');
+const config = require('../config');
+const stream = require('stream');
+
+module.exports = class ChatroomparticipantsService {
+ static async create(data, currentUser) {
+ const transaction = await db.sequelize.transaction();
+ try {
+ await ChatroomparticipantsDBApi.create(data, {
+ currentUser,
+ transaction,
+ });
+
+ await transaction.commit();
+ } catch (error) {
+ await transaction.rollback();
+ throw error;
+ }
+ }
+
+ static async bulkImport(req, res, sendInvitationEmails = true, host) {
+ const transaction = await db.sequelize.transaction();
+
+ try {
+ await processFile(req, res);
+ const bufferStream = new stream.PassThrough();
+ const results = [];
+
+ await bufferStream.end(Buffer.from(req.file.buffer, 'utf-8')); // convert Buffer to Stream
+
+ await new Promise((resolve, reject) => {
+ bufferStream
+ .pipe(csv())
+ .on('data', (data) => results.push(data))
+ .on('end', async () => {
+ console.log('CSV results', results);
+ resolve();
+ })
+ .on('error', (error) => reject(error));
+ });
+
+ await ChatroomparticipantsDBApi.bulkImport(results, {
+ transaction,
+ ignoreDuplicates: true,
+ validate: true,
+ currentUser: req.currentUser,
+ });
+
+ await transaction.commit();
+ } catch (error) {
+ await transaction.rollback();
+ throw error;
+ }
+ }
+
+ static async update(data, id, currentUser) {
+ const transaction = await db.sequelize.transaction();
+ try {
+ let chatroomparticipants = await ChatroomparticipantsDBApi.findBy(
+ { id },
+ { transaction },
+ );
+
+ if (!chatroomparticipants) {
+ throw new ValidationError('chatroomparticipantsNotFound');
+ }
+
+ const updatedChatroomparticipants =
+ await ChatroomparticipantsDBApi.update(id, data, {
+ currentUser,
+ transaction,
+ });
+
+ await transaction.commit();
+ return updatedChatroomparticipants;
+ } catch (error) {
+ await transaction.rollback();
+ throw error;
+ }
+ }
+
+ static async deleteByIds(ids, currentUser) {
+ const transaction = await db.sequelize.transaction();
+
+ try {
+ await ChatroomparticipantsDBApi.deleteByIds(ids, {
+ currentUser,
+ transaction,
+ });
+
+ await transaction.commit();
+ } catch (error) {
+ await transaction.rollback();
+ throw error;
+ }
+ }
+
+ static async remove(id, currentUser) {
+ const transaction = await db.sequelize.transaction();
+
+ try {
+ await ChatroomparticipantsDBApi.remove(id, {
+ currentUser,
+ transaction,
+ });
+
+ await transaction.commit();
+ } catch (error) {
+ await transaction.rollback();
+ throw error;
+ }
+ }
+};
diff --git a/backend/src/services/chatrooms.js b/backend/src/services/chatrooms.js
new file mode 100644
index 0000000..428e211
--- /dev/null
+++ b/backend/src/services/chatrooms.js
@@ -0,0 +1,114 @@
+const db = require('../db/models');
+const ChatroomsDBApi = require('../db/api/chatrooms');
+const processFile = require('../middlewares/upload');
+const ValidationError = require('./notifications/errors/validation');
+const csv = require('csv-parser');
+const axios = require('axios');
+const config = require('../config');
+const stream = require('stream');
+
+module.exports = class ChatroomsService {
+ static async create(data, currentUser) {
+ const transaction = await db.sequelize.transaction();
+ try {
+ await ChatroomsDBApi.create(data, {
+ currentUser,
+ transaction,
+ });
+
+ await transaction.commit();
+ } catch (error) {
+ await transaction.rollback();
+ throw error;
+ }
+ }
+
+ static async bulkImport(req, res, sendInvitationEmails = true, host) {
+ const transaction = await db.sequelize.transaction();
+
+ try {
+ await processFile(req, res);
+ const bufferStream = new stream.PassThrough();
+ const results = [];
+
+ await bufferStream.end(Buffer.from(req.file.buffer, 'utf-8')); // convert Buffer to Stream
+
+ await new Promise((resolve, reject) => {
+ bufferStream
+ .pipe(csv())
+ .on('data', (data) => results.push(data))
+ .on('end', async () => {
+ console.log('CSV results', results);
+ resolve();
+ })
+ .on('error', (error) => reject(error));
+ });
+
+ await ChatroomsDBApi.bulkImport(results, {
+ transaction,
+ ignoreDuplicates: true,
+ validate: true,
+ currentUser: req.currentUser,
+ });
+
+ await transaction.commit();
+ } catch (error) {
+ await transaction.rollback();
+ throw error;
+ }
+ }
+
+ static async update(data, id, currentUser) {
+ const transaction = await db.sequelize.transaction();
+ try {
+ let chatrooms = await ChatroomsDBApi.findBy({ id }, { transaction });
+
+ if (!chatrooms) {
+ throw new ValidationError('chatroomsNotFound');
+ }
+
+ const updatedChatrooms = await ChatroomsDBApi.update(id, data, {
+ currentUser,
+ transaction,
+ });
+
+ await transaction.commit();
+ return updatedChatrooms;
+ } catch (error) {
+ await transaction.rollback();
+ throw error;
+ }
+ }
+
+ static async deleteByIds(ids, currentUser) {
+ const transaction = await db.sequelize.transaction();
+
+ try {
+ await ChatroomsDBApi.deleteByIds(ids, {
+ currentUser,
+ transaction,
+ });
+
+ await transaction.commit();
+ } catch (error) {
+ await transaction.rollback();
+ throw error;
+ }
+ }
+
+ static async remove(id, currentUser) {
+ const transaction = await db.sequelize.transaction();
+
+ try {
+ await ChatroomsDBApi.remove(id, {
+ currentUser,
+ transaction,
+ });
+
+ await transaction.commit();
+ } catch (error) {
+ await transaction.rollback();
+ throw error;
+ }
+ }
+};
diff --git a/backend/src/services/notifications/list.js b/backend/src/services/notifications/list.js
index 9a363a7..77a5101 100644
--- a/backend/src/services/notifications/list.js
+++ b/backend/src/services/notifications/list.js
@@ -1,6 +1,6 @@
const errors = {
app: {
- title: 'Adminpro',
+ title: 'society-community-portal',
},
auth: {
diff --git a/backend/src/services/search.js b/backend/src/services/search.js
index 05fd805..18ba9cd 100644
--- a/backend/src/services/search.js
+++ b/backend/src/services/search.js
@@ -46,6 +46,10 @@ module.exports = class SearchService {
departments: ['name'],
reports: ['title'],
+
+ chatrooms: ['name'],
+
+ chatmessages: ['content'],
};
const columnsInt = {};
diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml
index 6c9b041..6215971 100644
--- a/docker/docker-compose.yml
+++ b/docker/docker-compose.yml
@@ -25,7 +25,7 @@ services:
- ./data/db:/var/lib/postgresql/data
environment:
- POSTGRES_HOST_AUTH_METHOD=trust
- - POSTGRES_DB=db_adminpro
+ - POSTGRES_DB=db_society_community_portal
ports:
- "5432:5432"
logging:
diff --git a/frontend/README.md b/frontend/README.md
index c36bd6b..180586d 100644
--- a/frontend/README.md
+++ b/frontend/README.md
@@ -1,4 +1,4 @@
-# Adminpro
+# society-community-portal
## This project was generated by Flatlogic Platform.
diff --git a/frontend/src/components/AsideMenuLayer.tsx b/frontend/src/components/AsideMenuLayer.tsx
index 4008b92..6e6213c 100644
--- a/frontend/src/components/AsideMenuLayer.tsx
+++ b/frontend/src/components/AsideMenuLayer.tsx
@@ -45,7 +45,7 @@ export default function AsideMenuLayer({
>
- Adminpro
+ society-community-portal
)}
+
+ {hasPermission(currentUser, 'READ_CHATROOMS') && (
+