diff --git a/src/database/migrations/20220809182156-AddFileStoreSubscribedColumn.js b/src/database/migrations/20220809182156-AddFileStoreSubscribedColumn.js new file mode 100644 index 00000000..289d5f87 --- /dev/null +++ b/src/database/migrations/20220809182156-AddFileStoreSubscribedColumn.js @@ -0,0 +1,22 @@ +'use strict'; + +export default { + async up(queryInterface, Sequelize) { + await Promise.all( + ['organizations'].map((table) => { + queryInterface.addColumn(table, 'fileStoreSubscribed', { + type: Sequelize.STRING, + allowNull: true, + }); + }), + ); + }, + + async down(queryInterface) { + await Promise.all( + ['organizations'].map((table) => { + queryInterface.removeColumn(table, 'fileStoreSubscribed'); + }), + ); + }, +}; diff --git a/src/database/migrations/index.js b/src/database/migrations/index.js index cda834c0..50938c19 100644 --- a/src/database/migrations/index.js +++ b/src/database/migrations/index.js @@ -25,6 +25,7 @@ import RepopulateVirtualTables from './20220515223227-re-populate-virtual-tables import AddAuthorColumnToAuditTable from './20220708210357-adding-author-column-to-audit-table'; import CreateFileStore from './20220724212553-create-file-store'; import AddOptionalMethodology2FieldToProject from './20220721212845-add-optional-methodology2-field-to-project'; +import AddFiltStoreSubscribedColumnToProject from './20220809182156-AddFileStoreSubscribedColumn'; export const migrations = [ { @@ -139,4 +140,8 @@ export const migrations = [ migration: AddOptionalMethodology2FieldToProject, name: '20220721212845-add-optional-methodology2-field-to-project', }, + { + migration: AddFiltStoreSubscribedColumnToProject, + name: '20220724161782-add-file-store-subscribed-column-to-project', + }, ]; diff --git a/src/datalayer/persistance.js b/src/datalayer/persistance.js index dd24bf62..632ae120 100644 --- a/src/datalayer/persistance.js +++ b/src/datalayer/persistance.js @@ -210,6 +210,35 @@ export const dataLayerAvailable = async () => { } }; +export const unsubscribeFromDataLayerStore = async (storeId) => { + const options = { + url: `${rpcUrl}/unsubscribe`, + body: JSON.stringify({ + id: storeId, + }), + }; + + logger.info(`RPC Call: ${rpcUrl}/unsubscribe ${storeId}`); + + try { + const response = await request( + Object.assign({}, getBaseOptions(), options), + ); + + const data = JSON.parse(response); + + if (Object.keys(data).includes('success') && data.success) { + logger.info(`Successfully UnSubscribed: ${storeId}`); + return data; + } + + return false; + } catch (error) { + logger.info(`Error UnSubscribing: ${error}`); + return false; + } +}; + export const subscribeToStoreOnDataLayer = async (storeId, ip, port) => { const options = { url: `${rpcUrl}/subscribe`, diff --git a/src/datalayer/syncService.js b/src/datalayer/syncService.js index 3903ef9e..6ebc3f76 100644 --- a/src/datalayer/syncService.js +++ b/src/datalayer/syncService.js @@ -167,6 +167,12 @@ const dataLayerWasUpdated = async () => { return updateStoreInfo; }; +const unsubscribeFromDataLayerStore = async (storeId, ip, port) => { + if (!USE_SIMULATOR) { + return dataLayer.unsubscribeFromDataLayerStore(storeId, ip, port); + } +}; + const subscribeToStoreOnDataLayer = async (storeId, ip, port) => { if (USE_SIMULATOR) { return simulator.subscribeToStoreOnDataLayer(storeId, ip, port); @@ -333,4 +339,5 @@ export default { getStoreIfUpdated, POLLING_INTERVAL, getCurrentStoreData, + unsubscribeFromDataLayerStore, }; diff --git a/src/models/file-store/file-store.model.js b/src/models/file-store/file-store.model.js index 6fd04da4..1ee88c97 100644 --- a/src/models/file-store/file-store.model.js +++ b/src/models/file-store/file-store.model.js @@ -8,7 +8,7 @@ import Sequelize from 'sequelize'; const { Model } = Sequelize; import { sequelize } from '../../database'; -import { Organization } from '../organizations'; +import { Organization, Meta } from '../'; import datalayer from '../../datalayer'; import { encodeHex } from '../../utils/datalayer-utils'; @@ -16,6 +16,48 @@ import { encodeHex } from '../../utils/datalayer-utils'; import ModelTypes from './file-store.modeltypes.cjs'; class FileStore extends Model { + static async subscribeToFileStore(orgUid) { + const organization = await Organization.findByPk(orgUid, { raw: true }); + if (!organization) { + throw new Error(`Org ${orgUid} does not exist`); + } + + if (!organization.fileStoreId) { + throw new Error( + `Org ${orgUid} does not have a file store to subscribe to`, + ); + } + + const orgMeta = await Meta.findOne( + { where: { metaKey: organization.orgUid } }, + { raw: true }, + ); + + if (!orgMeta) { + throw new Error( + `Org ${orgUid} can not find the ip and port to subscribe to its filestore, re-importing the org can fix this.`, + ); + } + + const { ip, port } = JSON.parse(orgMeta.metaValue); + + datalayer.subscribeToStoreOnDataLayer(organization.fileStoreId, ip, port); + Organization.update({ fileStoreSubscribed: true }); + } + + static async unsubscribeFromFileStore(orgUid) { + const organization = await Organization.findByPk(orgUid, { raw: true }); + if (!organization) { + throw new Error( + `Org ${orgUid} does not have a file store to unsubscribe from.`, + ); + } + + FileStore.destroy({ where: { orgUid: organization.orgUid } }); + datalayer.unsubscribeFromDataLayerStore(organization.fileStoreId); + Organization.update({ fileStoreSubscribed: false }); + } + static async addFileToFileStore(SHA256, fileName, base64File) { const myOrganization = await Organization.getHomeOrg(); let fileStoreId = myOrganization.fileStoreId; diff --git a/src/models/organizations/organizations.model.js b/src/models/organizations/organizations.model.js index b0fbe622..70640989 100644 --- a/src/models/organizations/organizations.model.js +++ b/src/models/organizations/organizations.model.js @@ -3,6 +3,7 @@ import Sequelize from 'sequelize'; const { Model } = Sequelize; import { sequelize } from '../../database'; +import { Meta } from '../meta'; import datalayer from '../../datalayer'; @@ -39,6 +40,7 @@ class Organization extends Model { if (myOrganization && includeAddress) { myOrganization.xchAddress = await datalayer.getPublicAddress(); + myOrganization.fileStoreSubscribed = true; return myOrganization; } @@ -47,7 +49,14 @@ class Organization extends Model { static async getOrgsMap() { const organizations = await Organization.findAll({ - attributes: ['orgUid', 'name', 'icon', 'isHome', 'subscribed'], + attributes: [ + 'orgUid', + 'name', + 'icon', + 'isHome', + 'subscribed', + 'fileStoreSubscribed', + ], }); for (let i = 0; i < organizations.length; i++) { @@ -227,6 +236,14 @@ class Organization extends Model { ); } + await Meta.upsert({ + metaKey: orgData.orgUid, + metaValue: JSON.stringify({ + ip, + port, + }), + }); + logger.info(`IMPORTING REGISTRY: ${orgData.registryId}`); const registryData = await datalayer.getSubscribedStoreData( diff --git a/src/models/organizations/organizations.modeltypes.cjs b/src/models/organizations/organizations.modeltypes.cjs index bc2960d1..5b7f5766 100644 --- a/src/models/organizations/organizations.modeltypes.cjs +++ b/src/models/organizations/organizations.modeltypes.cjs @@ -16,6 +16,10 @@ module.exports = { registryId: Sequelize.STRING, registryHash: Sequelize.STRING, fileStoreId: Sequelize.STRING, + fileStoreSubscribed: { + type: Sequelize.BOOLEAN, + defaultValue: false, + }, subscribed: { type: Sequelize.BOOLEAN, defaultValue: false,