forked from Ivasoft/mattermost-mobile
Fix MM-46309 (#6567)
* Fix MM-46309 * Address feedback * Update dbPath and rename server databas instead of deleting * ios fixes Co-authored-by: Daniel Espino <danielespino@MacBook-Pro-de-Daniel.local>
This commit is contained in:
committed by
GitHub
parent
63e6290c76
commit
ea595f1ced
@@ -48,6 +48,9 @@ export async function appEntry(serverUrl: string, since = 0, isUpgrade = false)
|
||||
// Load data from other servers
|
||||
syncOtherServers(serverUrl);
|
||||
}
|
||||
|
||||
verifyPushProxy(serverUrl);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -95,8 +98,6 @@ async function restAppEntry(serverUrl: string, since = 0, isUpgrade = false) {
|
||||
const {config, license} = await getCommonSystemValues(database);
|
||||
await deferredAppEntryActions(serverUrl, lastDisconnectedAt, currentUserId, currentUserLocale, prefData.preferences, config, license, teamData, chData, initialTeamId, switchToChannel ? initialChannelId : undefined);
|
||||
|
||||
verifyPushProxy(serverUrl);
|
||||
|
||||
return {userId: currentUserId};
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ import {schema as appSchema} from '@database/schema/app';
|
||||
import {serverSchema} from '@database/schema/server';
|
||||
import {queryActiveServer, queryServer, queryServerByIdentifier} from '@queries/app/servers';
|
||||
import {deleteIOSDatabase} from '@utils/mattermost_managed';
|
||||
import {hashCode} from '@utils/security';
|
||||
import {urlSafeBase64Encode} from '@utils/security';
|
||||
import {removeProtocol} from '@utils/url';
|
||||
|
||||
import type {AppDatabase, CreateServerDatabaseArgs, Models, RegisterServerDatabaseArgs, ServerDatabase, ServerDatabases} from '@typings/database/database';
|
||||
@@ -132,7 +132,7 @@ class DatabaseManager {
|
||||
private initServerDatabase = async (serverUrl: string): Promise<void> => {
|
||||
await this.createServerDatabase({
|
||||
config: {
|
||||
dbName: hashCode(serverUrl),
|
||||
dbName: urlSafeBase64Encode(serverUrl),
|
||||
dbType: DatabaseType.SERVER,
|
||||
serverUrl,
|
||||
},
|
||||
@@ -306,7 +306,7 @@ class DatabaseManager {
|
||||
};
|
||||
|
||||
private deleteServerDatabaseFiles = async (serverUrl: string): Promise<void> => {
|
||||
const databaseName = hashCode(serverUrl);
|
||||
const databaseName = urlSafeBase64Encode(serverUrl);
|
||||
|
||||
if (Platform.OS === 'ios') {
|
||||
// On iOS, we'll delete the *.db file under the shared app-group/databases folder
|
||||
|
||||
@@ -23,10 +23,11 @@ import ServerDataOperator from '@database/operator/server_data_operator';
|
||||
import {schema as appSchema} from '@database/schema/app';
|
||||
import {serverSchema} from '@database/schema/server';
|
||||
import {queryActiveServer, queryServer, queryServerByIdentifier} from '@queries/app/servers';
|
||||
import {deleteLegacyFileCache} from '@utils/file';
|
||||
import {emptyFunction} from '@utils/general';
|
||||
import {logDebug, logError} from '@utils/log';
|
||||
import {deleteIOSDatabase, getIOSAppGroupDetails} from '@utils/mattermost_managed';
|
||||
import {hashCode} from '@utils/security';
|
||||
import {deleteIOSDatabase, getIOSAppGroupDetails, renameIOSDatabase} from '@utils/mattermost_managed';
|
||||
import {hashCode_DEPRECATED, urlSafeBase64Encode} from '@utils/security';
|
||||
import {removeProtocol} from '@utils/url';
|
||||
|
||||
import type {AppDatabase, CreateServerDatabaseArgs, RegisterServerDatabaseArgs, Models, ServerDatabase, ServerDatabases} from '@typings/database/database';
|
||||
@@ -126,7 +127,13 @@ class DatabaseManager {
|
||||
|
||||
if (serverUrl) {
|
||||
try {
|
||||
const databaseName = hashCode(serverUrl);
|
||||
const databaseName = urlSafeBase64Encode(serverUrl);
|
||||
const oldDatabaseName = hashCode_DEPRECATED(serverUrl);
|
||||
|
||||
// Remove any legacy database we may already have.
|
||||
await this.renameDatabase(oldDatabaseName, databaseName);
|
||||
deleteLegacyFileCache(serverUrl);
|
||||
|
||||
const databaseFilePath = this.getDatabaseFilePath(databaseName);
|
||||
const migrations = ServerDatabaseMigrations;
|
||||
const modelClasses = this.serverModels;
|
||||
@@ -192,9 +199,9 @@ class DatabaseManager {
|
||||
try {
|
||||
const appDatabase = this.appDatabase?.database;
|
||||
if (appDatabase) {
|
||||
const isServerPresent = await this.isServerPresent(serverUrl);
|
||||
const serverModel = await queryServer(appDatabase, serverUrl);
|
||||
|
||||
if (!isServerPresent) {
|
||||
if (!serverModel) {
|
||||
await appDatabase.write(async () => {
|
||||
const serversCollection = appDatabase.collections.get(SERVERS);
|
||||
await serversCollection.create((server: ServersModel) => {
|
||||
@@ -205,6 +212,12 @@ class DatabaseManager {
|
||||
server.lastActiveAt = 0;
|
||||
});
|
||||
});
|
||||
} else if (serverModel.dbPath !== databaseFilePath) {
|
||||
await appDatabase.write(async () => {
|
||||
await serverModel.update((s) => {
|
||||
s.dbPath = databaseFilePath;
|
||||
});
|
||||
});
|
||||
} else if (identifier) {
|
||||
await this.updateServerIdentifier(serverUrl, identifier, displayName);
|
||||
}
|
||||
@@ -412,8 +425,16 @@ class DatabaseManager {
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
private deleteServerDatabaseFiles = async (serverUrl: string): Promise<void> => {
|
||||
const databaseName = hashCode(serverUrl);
|
||||
const databaseName = urlSafeBase64Encode(serverUrl);
|
||||
this.deleteServerDatabaseFilesByName(databaseName);
|
||||
};
|
||||
|
||||
/**
|
||||
* deleteServerDatabaseFilesByName: Removes the *.db file from the App-Group directory for iOS or the files directory for Android, given the database name
|
||||
* @param {string} databaseName
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
private deleteServerDatabaseFilesByName = async (databaseName: string): Promise<void> => {
|
||||
if (Platform.OS === 'ios') {
|
||||
// On iOS, we'll delete the *.db file under the shared app-group/databases folder
|
||||
deleteIOSDatabase({databaseName});
|
||||
@@ -431,6 +452,47 @@ class DatabaseManager {
|
||||
FileSystem.unlink(databaseWal).catch(emptyFunction);
|
||||
};
|
||||
|
||||
/**
|
||||
* deleteServerDatabaseFilesByName: Removes the *.db file from the App-Group directory for iOS or the files directory for Android, given the database name
|
||||
* @param {string} databaseName
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
private renameDatabase = async (databaseName: string, newDBName: string): Promise<void> => {
|
||||
if (Platform.OS === 'ios') {
|
||||
// On iOS, we'll move the *.db file under the shared app-group/databases folder
|
||||
renameIOSDatabase(databaseName, newDBName);
|
||||
return;
|
||||
}
|
||||
|
||||
// On Android, we'll move the *.db, the *.db-shm and *.db-wal files
|
||||
const androidFilesDir = this.databaseDirectory;
|
||||
const databaseFile = `${androidFilesDir}${databaseName}.db`;
|
||||
const databaseShm = `${androidFilesDir}${databaseName}.db-shm`;
|
||||
const databaseWal = `${androidFilesDir}${databaseName}.db-wal`;
|
||||
|
||||
const newDatabaseFile = `${androidFilesDir}${newDBName}.db`;
|
||||
const newDatabaseShm = `${androidFilesDir}${newDBName}.db-shm`;
|
||||
const newDatabaseWal = `${androidFilesDir}${newDBName}.db-wal`;
|
||||
|
||||
if (await FileSystem.exists(newDatabaseFile)) {
|
||||
// Already renamed, do not try
|
||||
return;
|
||||
}
|
||||
|
||||
if (!await FileSystem.exists(databaseFile)) {
|
||||
// Nothing to rename, do not try
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await FileSystem.moveFile(databaseFile, newDatabaseFile);
|
||||
await FileSystem.moveFile(databaseShm, newDatabaseShm);
|
||||
await FileSystem.moveFile(databaseWal, newDatabaseWal);
|
||||
} catch (error) {
|
||||
// Do nothing
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* factoryReset: Removes the databases directory and all its contents on the respective platform
|
||||
* @param {boolean} shouldRemoveDirectory
|
||||
@@ -497,7 +559,7 @@ class DatabaseManager {
|
||||
* @returns {string}
|
||||
*/
|
||||
private getDatabaseFilePath = (dbName: string): string => {
|
||||
return Platform.OS === 'ios' ? `${this.databaseDirectory}/${dbName}.db` : `${this.databaseDirectory}/${dbName}.db`;
|
||||
return Platform.OS === 'ios' ? `${this.databaseDirectory}/${dbName}.db` : `${this.databaseDirectory}${dbName}.db`;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -18,7 +18,7 @@ import {generateId} from '@utils/general';
|
||||
import keyMirror from '@utils/key_mirror';
|
||||
import {logError} from '@utils/log';
|
||||
import {deleteEntititesFile, getIOSAppGroupDetails} from '@utils/mattermost_managed';
|
||||
import {hashCode} from '@utils/security';
|
||||
import {hashCode_DEPRECATED, urlSafeBase64Encode} from '@utils/security';
|
||||
|
||||
import type FileModel from '@typings/database/models/servers/file';
|
||||
|
||||
@@ -165,8 +165,17 @@ export async function deleteV1Data() {
|
||||
}
|
||||
|
||||
export async function deleteFileCache(serverUrl: string) {
|
||||
const serverDir = hashCode(serverUrl);
|
||||
const cacheDir = `${FileSystem.CachesDirectoryPath}/${serverDir}`;
|
||||
const serverDir = urlSafeBase64Encode(serverUrl);
|
||||
deleteFileCacheByDir(serverDir);
|
||||
}
|
||||
|
||||
export async function deleteLegacyFileCache(serverUrl: string) {
|
||||
const serverDir = hashCode_DEPRECATED(serverUrl);
|
||||
deleteFileCacheByDir(serverDir);
|
||||
}
|
||||
|
||||
async function deleteFileCacheByDir(dir: string) {
|
||||
const cacheDir = `${FileSystem.CachesDirectoryPath}/${dir}`;
|
||||
if (cacheDir) {
|
||||
const cacheDirInfo = await FileSystem.exists(cacheDir);
|
||||
if (cacheDirInfo) {
|
||||
@@ -340,9 +349,10 @@ export function getFileType(file: FileInfo): string {
|
||||
}
|
||||
|
||||
export function getLocalFilePathFromFile(serverUrl: string, file: FileInfo | FileModel) {
|
||||
const fileIdPath = file.id?.replace(/[^0-9a-z]/g, '');
|
||||
if (serverUrl) {
|
||||
const server = hashCode(serverUrl);
|
||||
if (file?.name) {
|
||||
const server = urlSafeBase64Encode(serverUrl);
|
||||
if (file?.name && !file.name.includes('/')) {
|
||||
let extension: string | undefined = file.extension;
|
||||
let filename = file.name;
|
||||
|
||||
@@ -362,9 +372,9 @@ export function getLocalFilePathFromFile(serverUrl: string, file: FileInfo | Fil
|
||||
}
|
||||
}
|
||||
|
||||
return `${FileSystem.CachesDirectoryPath}/${server}/${filename}-${hashCode(file.id!)}.${extension}`;
|
||||
return `${FileSystem.CachesDirectoryPath}/${server}/${filename}-${fileIdPath}.${extension}`;
|
||||
} else if (file?.id && file?.extension) {
|
||||
return `${FileSystem.CachesDirectoryPath}/${server}/${file.id}.${file.extension}`;
|
||||
return `${FileSystem.CachesDirectoryPath}/${server}/${fileIdPath}.${file.extension}`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -504,8 +514,9 @@ export const getAllFilesInCachesDirectory = async (serverUrl: string) => {
|
||||
try {
|
||||
const files: FileSystem.ReadDirItem[][] = [];
|
||||
|
||||
const directoryFiles = await FileSystem.readDir(`${FileSystem.CachesDirectoryPath}/${hashCode(serverUrl)}`);
|
||||
const directoryFiles = await FileSystem.readDir(`${FileSystem.CachesDirectoryPath}/${urlSafeBase64Encode(serverUrl)}`);
|
||||
files.push(directoryFiles);
|
||||
|
||||
const flattenedFiles = files.flat();
|
||||
const totalSize = flattenedFiles.reduce((acc, file) => acc + file.size, 0);
|
||||
return {
|
||||
|
||||
@@ -44,6 +44,15 @@ export const deleteIOSDatabase = ({
|
||||
MattermostManaged.deleteDatabaseDirectory(databaseName, shouldRemoveDirectory, () => null);
|
||||
};
|
||||
|
||||
/**
|
||||
* renameIOSDatabase renames the .db and any other related file to the new name.
|
||||
* @param {string} from original database name
|
||||
* @param {string} to new database name
|
||||
*/
|
||||
export const renameIOSDatabase = (from: string, to: string) => {
|
||||
MattermostManaged.renameDatabase(from, to, () => null);
|
||||
};
|
||||
|
||||
export const deleteEntititesFile = (callback?: (success: boolean) => void) => {
|
||||
if (Platform.OS === 'ios') {
|
||||
MattermostManaged.deleteEntititesFile((result: boolean) => {
|
||||
|
||||
@@ -2,13 +2,15 @@
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import CookieManager from '@react-native-cookies/cookies';
|
||||
import base64 from 'base-64';
|
||||
|
||||
export async function getCSRFFromCookie(url: string) {
|
||||
const cookies = await CookieManager.get(url, false);
|
||||
return cookies.MMCSRF?.value;
|
||||
}
|
||||
|
||||
export const hashCode = (str: string): string => {
|
||||
// This has been deprecated and is only used for migrations
|
||||
export const hashCode_DEPRECATED = (str: string): string => {
|
||||
let hash = 0;
|
||||
let i;
|
||||
let chr;
|
||||
@@ -23,3 +25,7 @@ export const hashCode = (str: string): string => {
|
||||
}
|
||||
return hash.toString();
|
||||
};
|
||||
|
||||
export const urlSafeBase64Encode = (str: string): string => {
|
||||
return base64.encode(str).replace(/\+/g, '-').replace(/\//g, '_');
|
||||
};
|
||||
|
||||
@@ -95,6 +95,48 @@ RCT_EXPORT_METHOD(deleteDatabaseDirectory: (NSString *)databaseName shouldRemov
|
||||
}
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(renameDatabase: (NSString *)databaseName to: (NSString *) newDBName callback: (RCTResponseSenderBlock)callback){
|
||||
@try {
|
||||
NSDictionary *appGroupDir = [self appGroupSharedDirectory];
|
||||
NSString *databaseDir;
|
||||
NSString *newDBDir;
|
||||
|
||||
|
||||
if(databaseName){
|
||||
databaseDir = [NSString stringWithFormat:@"%@/%@%@", appGroupDir[@"databasePath"], databaseName , @".db"];
|
||||
}
|
||||
|
||||
if (newDBName){
|
||||
newDBDir = [NSString stringWithFormat:@"%@/%@%@", appGroupDir[@"databasePath"], newDBName , @".db"];
|
||||
}
|
||||
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
NSError *error = nil;
|
||||
|
||||
BOOL destinationHasFile = [fileManager fileExistsAtPath:newDBDir];
|
||||
|
||||
if (!destinationHasFile && [fileManager fileExistsAtPath:[NSString stringWithFormat:@"%@-wal", databaseDir]]) {
|
||||
[fileManager moveItemAtPath:[NSString stringWithFormat:@"%@-wal", databaseDir] toPath:[NSString stringWithFormat:@"%@-wal", newDBDir] error:nil];
|
||||
}
|
||||
|
||||
if (!destinationHasFile && [fileManager fileExistsAtPath:[NSString stringWithFormat:@"%@-shm", databaseDir]]) {
|
||||
[fileManager moveItemAtPath:[NSString stringWithFormat:@"%@-shm", databaseDir] toPath:[NSString stringWithFormat:@"%@-shm", newDBDir] error:nil];
|
||||
}
|
||||
|
||||
BOOL successCode = destinationHasFile;
|
||||
if (!destinationHasFile && [fileManager fileExistsAtPath:databaseDir]){
|
||||
successCode = [fileManager moveItemAtPath:databaseDir toPath: newDBDir error:&error];
|
||||
}
|
||||
NSNumber *success= [NSNumber numberWithBool:successCode];
|
||||
|
||||
callback(@[(error ?: [NSNull null]), success]);
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
NSLog(@"%@", exception.reason);
|
||||
callback(@[exception.reason, @NO]);
|
||||
}
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(deleteEntititesFile: (RCTResponseSenderBlock) callback) {
|
||||
@try {
|
||||
NSDictionary *appGroupDir = [self appGroupSharedDirectory];
|
||||
|
||||
Reference in New Issue
Block a user