From f1b7cfe5103e52634b588a7e00842d028587e13e Mon Sep 17 00:00:00 2001 From: Diego Mello Date: Thu, 19 Oct 2023 16:57:13 -0300 Subject: [PATCH 1/5] fix: some details on supported versions (#5284) * Ignore patches * Fix null versions find * Fix cloud request * Fix inverted date comparison to trigger action sheet --- .../methods/checkSupportedVersions.test.ts | 21 +++++++++++++++++++ app/lib/methods/checkSupportedVersions.ts | 6 ++++-- app/lib/methods/getServerInfo.ts | 13 ++++++++---- app/sagas/login.js | 3 +-- 4 files changed, 35 insertions(+), 8 deletions(-) diff --git a/app/lib/methods/checkSupportedVersions.test.ts b/app/lib/methods/checkSupportedVersions.test.ts index ad86f118a..fb0639adf 100644 --- a/app/lib/methods/checkSupportedVersions.test.ts +++ b/app/lib/methods/checkSupportedVersions.test.ts @@ -120,6 +120,27 @@ jest.useFakeTimers('modern'); jest.setSystemTime(new Date(TODAY)); describe('checkSupportedVersions', () => { + describe('General', () => { + test('ignore the patch and compare as minor', () => { + expect( + checkSupportedVersions({ + supportedVersions: MOCK, + serverVersion: '1.5.1' + }) + ).toMatchObject({ + status: 'supported' + }); + expect( + checkSupportedVersions({ + supportedVersions: MOCK, + serverVersion: '1.2.1' + }) + ).toMatchObject({ + status: 'expired' + }); + }); + }); + describe('Built-in supported versions', () => { test('no supported versions', () => { expect(checkSupportedVersions({ supportedVersions: undefined, serverVersion: '1.5.0' })).toMatchObject({ diff --git a/app/lib/methods/checkSupportedVersions.ts b/app/lib/methods/checkSupportedVersions.ts index f1f4a154e..4aac383fe 100644 --- a/app/lib/methods/checkSupportedVersions.ts +++ b/app/lib/methods/checkSupportedVersions.ts @@ -1,5 +1,6 @@ import moment from 'moment'; import coerce from 'semver/functions/coerce'; +import satisfies from 'semver/functions/satisfies'; import { ISupportedVersionsData, TSVDictionary, TSVMessage, TSVStatus } from '../../definitions'; import builtInSupportedVersions from '../../../app-supportedversions.json'; @@ -40,6 +41,7 @@ export const checkSupportedVersions = function ({ i18n?: TSVDictionary; expiration?: string; } { + const serverVersionTilde = `~${serverVersion.split('.').slice(0, 2).join('.')}`; let sv: ISupportedVersionsData; if (!supportedVersions || supportedVersions.timestamp < builtInSupportedVersions.timestamp) { // Built-in supported versions @@ -49,7 +51,7 @@ export const checkSupportedVersions = function ({ sv = supportedVersions; } - const versionInfo = sv.versions.find(({ version }) => coerce(version)?.version === serverVersion); + const versionInfo = sv.versions.find(({ version }) => satisfies(coerce(version)?.version ?? '', serverVersionTilde)); if (versionInfo && new Date(versionInfo.expiration) >= new Date()) { const messages = versionInfo?.messages || sv?.messages; const message = getMessage({ messages, expiration: versionInfo.expiration }); @@ -62,7 +64,7 @@ export const checkSupportedVersions = function ({ } // Exceptions - const exception = sv.exceptions?.versions.find(({ version }) => coerce(version)?.version === serverVersion); + const exception = sv.exceptions?.versions?.find(({ version }) => satisfies(coerce(version)?.version ?? '', serverVersionTilde)); const messages = exception?.messages || sv.exceptions?.messages || versionInfo?.messages || sv.messages; const message = getMessage({ messages, expiration: exception?.expiration }); const status = getStatus({ expiration: exception?.expiration, message }); diff --git a/app/lib/methods/getServerInfo.ts b/app/lib/methods/getServerInfo.ts index 98a605424..ecb95354e 100644 --- a/app/lib/methods/getServerInfo.ts +++ b/app/lib/methods/getServerInfo.ts @@ -57,7 +57,7 @@ export async function getServerInfo(server: string): Promise // if backend doesn't have supported versions or JWT is invalid, request from cloud if (!supportedVersions) { - const cloudInfo = await getCloudInfo(); + const cloudInfo = await getCloudInfo(server); // Makes use of signed JWT to get supported versions const supportedVersionsCloud = verifyJWT(cloudInfo?.signed); @@ -96,9 +96,14 @@ export async function getServerInfo(server: string): Promise }; } -export const getCloudInfo = async (): Promise => { - const uniqueId = store.getState().settings.uniqueID as string; - const domain = store.getState().server.server; +const getUniqueId = async (server: string): Promise => { + const response = await fetch(`${server}/api/v1/settings.public?query={"_id": "uniqueID"}`); + const result = await response.json(); + return result?.settings?.[0]?.value; +}; + +export const getCloudInfo = async (domain: string): Promise => { + const uniqueId = await getUniqueId(domain); const response = await getSupportedVersionsCloud(uniqueId, domain); return response.json() as unknown as TCloudInfo; }; diff --git a/app/sagas/login.js b/app/sagas/login.js index efff9db6d..e1fa2b549 100644 --- a/app/sagas/login.js +++ b/app/sagas/login.js @@ -56,8 +56,7 @@ const showSupportedVersionsWarning = function* showSupportedVersionsWarning(serv } const serverRecord = yield getServerById(server); const isMasterDetail = yield select(state => state.app.isMasterDetail); - - if (!serverRecord || moment(serverRecord?.supportedVersionsWarningAt).diff(new Date(), 'hours') <= 12) { + if (!serverRecord || moment(new Date()).diff(serverRecord?.supportedVersionsWarningAt, 'hours') <= 12) { return; } From 51756965550228597b4534fd50573a4ddfb0d4b5 Mon Sep 17 00:00:00 2001 From: Diego Mello Date: Mon, 23 Oct 2023 16:35:18 -0300 Subject: [PATCH 2/5] feat: Cache supported versions timestamp to fetch from cloud once every 12h (#5292) --- app/definitions/IServer.ts | 1 + app/lib/database/model/servers/Server.js | 2 ++ app/lib/database/model/servers/migrations.js | 15 +++++++++++++++ app/lib/database/schema/servers.js | 5 +++-- app/lib/methods/getServerInfo.ts | 16 ++++++++++++++++ app/sagas/selectServer.ts | 4 +++- 6 files changed, 40 insertions(+), 3 deletions(-) diff --git a/app/definitions/IServer.ts b/app/definitions/IServer.ts index a3ac17e0b..62664cd87 100644 --- a/app/definitions/IServer.ts +++ b/app/definitions/IServer.ts @@ -81,6 +81,7 @@ export interface IServer { E2E_Enable: boolean; supportedVersions?: ISupportedVersionsData; supportedVersionsWarningAt?: Date; + supportedVersionsUpdatedAt?: Date; } export type TServerModel = IServer & Model; diff --git a/app/lib/database/model/servers/Server.js b/app/lib/database/model/servers/Server.js index 11718cd98..2d57113e4 100644 --- a/app/lib/database/model/servers/Server.js +++ b/app/lib/database/model/servers/Server.js @@ -38,4 +38,6 @@ export default class Server extends Model { @json('supported_versions', sanitizer) supportedVersions; @date('supported_versions_warning_at') supportedVersionsWarningAt; + + @date('supported_versions_updated_at') supportedVersionsUpdatedAt; } diff --git a/app/lib/database/model/servers/migrations.js b/app/lib/database/model/servers/migrations.js index 31d28ee41..5a3fa8ff9 100644 --- a/app/lib/database/model/servers/migrations.js +++ b/app/lib/database/model/servers/migrations.js @@ -131,6 +131,21 @@ export default schemaMigrations({ ] }) ] + }, + { + toVersion: 15, + steps: [ + addColumns({ + table: 'servers', + columns: [ + { + name: 'supported_versions_updated_at', + type: 'number', + isOptional: true + } + ] + }) + ] } ] }); diff --git a/app/lib/database/schema/servers.js b/app/lib/database/schema/servers.js index 911566a9c..4aedcd93f 100644 --- a/app/lib/database/schema/servers.js +++ b/app/lib/database/schema/servers.js @@ -1,7 +1,7 @@ import { appSchema, tableSchema } from '@nozbe/watermelondb'; export default appSchema({ - version: 14, + version: 15, tables: [ tableSchema({ name: 'users', @@ -40,7 +40,8 @@ export default appSchema({ { name: 'enterprise_modules', type: 'string', isOptional: true }, { name: 'e2e_enable', type: 'boolean', isOptional: true }, { name: 'supported_versions', type: 'string', isOptional: true }, - { name: 'supported_versions_warning_at', type: 'number', isOptional: true } + { name: 'supported_versions_warning_at', type: 'number', isOptional: true }, + { name: 'supported_versions_updated_at', type: 'number', isOptional: true } ] }), tableSchema({ diff --git a/app/lib/methods/getServerInfo.ts b/app/lib/methods/getServerInfo.ts index ecb95354e..4954c01dd 100644 --- a/app/lib/methods/getServerInfo.ts +++ b/app/lib/methods/getServerInfo.ts @@ -1,6 +1,7 @@ import RNFetchBlob from 'rn-fetch-blob'; import { settings as RocketChatSettings } from '@rocket.chat/sdk'; import { KJUR } from 'jsrsasign'; +import moment from 'moment'; import { getSupportedVersionsCloud } from '../services/restApi'; import { TCloudInfo, IServerInfo, ISupportedVersions, ISupportedVersionsData, IApiServerInfo } from '../../definitions'; @@ -8,6 +9,7 @@ import { selectServerFailure } from '../../actions/server'; import { store } from '../store/auxStore'; import I18n from '../../i18n'; import { SIGNED_SUPPORTED_VERSIONS_PUBLIC_KEY } from '../constants'; +import { getServerById } from '../database/services/Server'; interface IServerInfoFailure { success: false; @@ -20,6 +22,8 @@ interface IServerInfoSuccess extends IServerInfo { export type TServerInfoResult = IServerInfoSuccess | IServerInfoFailure; +const SV_CLOUD_UPDATE_INTERVAL = 12; // hours + // Verifies if JWT is valid and returns the payload const verifyJWT = (jwt?: string): ISupportedVersionsData | null => { try { @@ -57,6 +61,18 @@ export async function getServerInfo(server: string): Promise // if backend doesn't have supported versions or JWT is invalid, request from cloud if (!supportedVersions) { + // fetches from cloud only every 12h + const serverRecord = await getServerById(server); + if ( + serverRecord?.supportedVersionsUpdatedAt && + moment(new Date()).diff(serverRecord?.supportedVersionsUpdatedAt, 'hours') <= SV_CLOUD_UPDATE_INTERVAL + ) { + return { + ...jsonRes, + success: true + }; + } + const cloudInfo = await getCloudInfo(server); // Makes use of signed JWT to get supported versions diff --git a/app/sagas/selectServer.ts b/app/sagas/selectServer.ts index cfb26697b..40ec2ebec 100644 --- a/app/sagas/selectServer.ts +++ b/app/sagas/selectServer.ts @@ -73,6 +73,7 @@ const upsertServer = async function ({ server, serverInfo }: { server: string; s r.version = serverVersion; if (serverInfo.supportedVersions) { r.supportedVersions = serverInfo.supportedVersions; + r.supportedVersionsUpdatedAt = new Date(); } }); }); @@ -83,10 +84,11 @@ const upsertServer = async function ({ server, serverInfo }: { server: string; s await serversDB.write(async () => { newRecord = await serversCollection.create(r => { r._raw = sanitizedRaw({ id: server }, serversCollection.schema); + r.version = serverVersion; if (serverInfo.supportedVersions) { r.supportedVersions = serverInfo.supportedVersions; + r.supportedVersionsUpdatedAt = new Date(); } - r.version = serverVersion; }); }); if (newRecord) { From 13af95f8cbb500f27a6d3f319f68a1ae3799ad02 Mon Sep 17 00:00:00 2001 From: Gleidson Daniel Silva Date: Wed, 25 Oct 2023 11:23:07 -0300 Subject: [PATCH 3/5] fix: fixes the import of the serverInfo function (#5295) --- app/sagas/deepLinking.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/sagas/deepLinking.js b/app/sagas/deepLinking.js index 474e4a7f2..4be3cf8e7 100644 --- a/app/sagas/deepLinking.js +++ b/app/sagas/deepLinking.js @@ -14,7 +14,7 @@ import { loginRequest } from '../actions/login'; import log from '../lib/methods/helpers/log'; import { RootEnum } from '../definitions'; import { CURRENT_SERVER, TOKEN_KEY } from '../lib/constants'; -import { callJitsi, callJitsiWithoutServer, canOpenRoom } from '../lib/methods'; +import { callJitsi, callJitsiWithoutServer, canOpenRoom, getServerInfo } from '../lib/methods'; import { Services } from '../lib/services'; const roomTypes = { @@ -163,7 +163,7 @@ const handleOpen = function* handleOpen({ params }) { // do nothing? } // if deep link is from a different server - const result = yield Services.getServerInfo(host); + const result = yield getServerInfo(host); if (!result.success) { // Fallback to prevent the app from being stuck on splash screen yield fallbackNavigation(); From 36ca0384b73ce2c89ec7c124ef36bf0a9c0f70ef Mon Sep 17 00:00:00 2001 From: Diego Mello Date: Wed, 25 Oct 2023 15:19:40 -0300 Subject: [PATCH 4/5] fix: minor supported versions details (#5296) --- app/lib/methods/checkSupportedVersions.ts | 2 +- app/views/SidebarView/index.tsx | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/app/lib/methods/checkSupportedVersions.ts b/app/lib/methods/checkSupportedVersions.ts index 4aac383fe..bca4fd0a3 100644 --- a/app/lib/methods/checkSupportedVersions.ts +++ b/app/lib/methods/checkSupportedVersions.ts @@ -16,7 +16,7 @@ export const getMessage = ({ return; } const sortedMessages = messages.sort((a, b) => a.remainingDays - b.remainingDays); - return sortedMessages.find(({ remainingDays }) => moment(expiration).diff(new Date(), 'days') <= remainingDays); + return sortedMessages.find(({ remainingDays }) => moment(expiration).diff(new Date(), 'hours') <= remainingDays * 24); }; const getStatus = ({ expiration, message }: { expiration?: string; message?: TSVMessage }): TSVStatus => { diff --git a/app/views/SidebarView/index.tsx b/app/views/SidebarView/index.tsx index 6eb1e7bec..8e8976cf4 100644 --- a/app/views/SidebarView/index.tsx +++ b/app/views/SidebarView/index.tsx @@ -73,6 +73,7 @@ class Sidebar extends Component { useRealName, theme, Presence_broadcast_disabled, + supportedVersionsStatus, viewStatisticsPermission, viewRoomAdministrationPermission, viewUserAdministrationPermission, @@ -112,6 +113,9 @@ class Sidebar extends Component { if (nextProps.Presence_broadcast_disabled !== Presence_broadcast_disabled) { return true; } + if (nextProps.supportedVersionsStatus !== supportedVersionsStatus) { + return true; + } if (!dequal(nextProps.viewStatisticsPermission, viewStatisticsPermission)) { return true; } From 9e5ad538e2da995290e49e9c15a4a8d3a85ee230 Mon Sep 17 00:00:00 2001 From: Diego Mello Date: Mon, 13 Nov 2023 13:23:46 -0300 Subject: [PATCH 5/5] fix: Catch cloud request errors (#5312) --- android/app/build.gradle | 2 +- app/lib/methods/getServerInfo.ts | 20 +++++++++++++++++--- ios/RocketChatRN.xcodeproj/project.pbxproj | 4 ++-- ios/RocketChatRN/Info.plist | 2 +- ios/ShareRocketChatRN/Info.plist | 2 +- package.json | 2 +- 6 files changed, 23 insertions(+), 9 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 46bb77c94..face812f3 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -147,7 +147,7 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion versionCode VERSIONCODE as Integer - versionName "4.42.0" + versionName "4.42.2" vectorDrawables.useSupportLibrary = true if (!isFoss) { manifestPlaceholders = [BugsnagAPIKey: BugsnagAPIKey as String] diff --git a/app/lib/methods/getServerInfo.ts b/app/lib/methods/getServerInfo.ts index 4954c01dd..c05d4e8f8 100644 --- a/app/lib/methods/getServerInfo.ts +++ b/app/lib/methods/getServerInfo.ts @@ -10,6 +10,7 @@ import { store } from '../store/auxStore'; import I18n from '../../i18n'; import { SIGNED_SUPPORTED_VERSIONS_PUBLIC_KEY } from '../constants'; import { getServerById } from '../database/services/Server'; +import log from './helpers/log'; interface IServerInfoFailure { success: false; @@ -75,6 +76,14 @@ export async function getServerInfo(server: string): Promise const cloudInfo = await getCloudInfo(server); + // Allows airgapped servers to use the app until enforcementStartDate + if (!cloudInfo) { + return { + ...jsonRes, + success: true + }; + } + // Makes use of signed JWT to get supported versions const supportedVersionsCloud = verifyJWT(cloudInfo?.signed); @@ -119,7 +128,12 @@ const getUniqueId = async (server: string): Promise => { }; export const getCloudInfo = async (domain: string): Promise => { - const uniqueId = await getUniqueId(domain); - const response = await getSupportedVersionsCloud(uniqueId, domain); - return response.json() as unknown as TCloudInfo; + try { + const uniqueId = await getUniqueId(domain); + const response = await getSupportedVersionsCloud(uniqueId, domain); + return response.json() as unknown as TCloudInfo; + } catch (e) { + log(e); + return null; + } }; diff --git a/ios/RocketChatRN.xcodeproj/project.pbxproj b/ios/RocketChatRN.xcodeproj/project.pbxproj index 997fc9474..b213f2392 100644 --- a/ios/RocketChatRN.xcodeproj/project.pbxproj +++ b/ios/RocketChatRN.xcodeproj/project.pbxproj @@ -1760,7 +1760,7 @@ INFOPLIST_FILE = NotificationService/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 4.42.0; + MARKETING_VERSION = 4.42.2; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG"; @@ -1799,7 +1799,7 @@ INFOPLIST_FILE = NotificationService/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 4.42.0; + MARKETING_VERSION = 4.42.2; MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; PRODUCT_BUNDLE_IDENTIFIER = chat.rocket.reactnative.NotificationService; diff --git a/ios/RocketChatRN/Info.plist b/ios/RocketChatRN/Info.plist index c1b98975a..a51b4631c 100644 --- a/ios/RocketChatRN/Info.plist +++ b/ios/RocketChatRN/Info.plist @@ -26,7 +26,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 4.42.0 + 4.42.2 CFBundleSignature ???? CFBundleURLTypes diff --git a/ios/ShareRocketChatRN/Info.plist b/ios/ShareRocketChatRN/Info.plist index 2cd2cdb42..02368260c 100644 --- a/ios/ShareRocketChatRN/Info.plist +++ b/ios/ShareRocketChatRN/Info.plist @@ -26,7 +26,7 @@ CFBundlePackageType XPC! CFBundleShortVersionString - 4.42.0 + 4.42.2 CFBundleVersion 1 KeychainGroup diff --git a/package.json b/package.json index 4c79513cd..add4a6b93 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rocket-chat-reactnative", - "version": "4.42.0", + "version": "4.42.2", "private": true, "scripts": { "start": "react-native start",