2024-10-05 23:17:20 +02:00
|
|
|
/*
|
|
|
|
|
* Copyright (C) Contributors to the Suwayomi project
|
|
|
|
|
*
|
|
|
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
import { APP_METADATA_KEY_PREFIX, METADATA_MIGRATIONS } from '@/modules/metadata/Metadata.constants.ts';
|
|
|
|
|
import {
|
|
|
|
|
doesMetadataKeyExistIn,
|
|
|
|
|
getAppMetadataFrom,
|
|
|
|
|
getMetadataKey,
|
|
|
|
|
} from '@/modules/metadata/services/MetadataReader.ts';
|
|
|
|
|
import { IMetadataMigration, Metadata } from '@/modules/metadata/Metadata.types.ts';
|
|
|
|
|
|
|
|
|
|
export const getAppKeyPrefixForMigration = (migrationId: number): string => {
|
|
|
|
|
const appKeyPrefix = METADATA_MIGRATIONS.slice(0, migrationId)
|
|
|
|
|
.reverse()
|
|
|
|
|
.find((migration) => !!migration.appKeyPrefix);
|
|
|
|
|
|
|
|
|
|
return appKeyPrefix?.appKeyPrefix?.newPrefix ?? APP_METADATA_KEY_PREFIX;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const applyAppKeyPrefixMigration = (meta: Metadata, migration: IMetadataMigration): Metadata => {
|
|
|
|
|
const migratedMetadata: Metadata = { ...meta };
|
|
|
|
|
|
|
|
|
|
if (!migration.appKeyPrefix) {
|
|
|
|
|
return migratedMetadata;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const { oldPrefix, newPrefix } = migration.appKeyPrefix;
|
|
|
|
|
|
2024-10-28 18:03:29 +01:00
|
|
|
const oldAppMetadata = getAppMetadataFrom(meta, undefined, oldPrefix);
|
|
|
|
|
const newAppMetadata = getAppMetadataFrom(meta, undefined, newPrefix);
|
2024-10-05 23:17:20 +02:00
|
|
|
|
|
|
|
|
const missingMetadataKeys = Object.keys(oldAppMetadata).filter((key) => !Object.keys(newAppMetadata).includes(key));
|
|
|
|
|
|
|
|
|
|
const isMissingOldMetadata = missingMetadataKeys.length;
|
|
|
|
|
if (isMissingOldMetadata) {
|
|
|
|
|
missingMetadataKeys.forEach((oldKey) => {
|
|
|
|
|
const keyWithNewPrefix = oldKey.replace(oldPrefix, newPrefix);
|
|
|
|
|
migratedMetadata[keyWithNewPrefix] = oldAppMetadata[oldKey];
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return migratedMetadata;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const applyMetadataValueMigration = (meta: Metadata, migration: IMetadataMigration, appKeyPrefix: string): Metadata => {
|
|
|
|
|
const migratedMetadata: Metadata = { ...meta };
|
|
|
|
|
|
|
|
|
|
if (!migration.values) {
|
|
|
|
|
return migratedMetadata;
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-28 18:03:29 +01:00
|
|
|
const appMetadata = getAppMetadataFrom(meta, undefined, appKeyPrefix);
|
2024-10-05 23:17:20 +02:00
|
|
|
const metadataValueChanges = migration.values;
|
|
|
|
|
|
|
|
|
|
metadataValueChanges.forEach(({ key, oldValue, newValue }) => {
|
|
|
|
|
const migrateValue = (metaKey: string) => {
|
2024-12-10 21:13:10 +01:00
|
|
|
if (meta[metaKey].match(oldValue)) {
|
2024-12-10 21:14:26 +01:00
|
|
|
migratedMetadata[metaKey] = typeof newValue === 'function' ? newValue(meta[metaKey]) : newValue;
|
2024-10-05 23:17:20 +02:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const migrateValueOfAllAppKeys = key === undefined;
|
|
|
|
|
if (migrateValueOfAllAppKeys) {
|
|
|
|
|
Object.keys(appMetadata).forEach((metaKey) => {
|
|
|
|
|
migrateValue(metaKey);
|
|
|
|
|
});
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-28 18:03:29 +01:00
|
|
|
if (!doesMetadataKeyExistIn(meta, key, undefined, appKeyPrefix)) {
|
2024-10-05 23:17:20 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-28 18:03:29 +01:00
|
|
|
migrateValue(getMetadataKey(key, undefined, appKeyPrefix));
|
2024-10-05 23:17:20 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return migratedMetadata;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const applyMetadataKeyMigration = (meta: Metadata, migration: IMetadataMigration): Metadata => {
|
|
|
|
|
const migratedMetadata: Metadata = { ...meta };
|
|
|
|
|
|
|
|
|
|
if (!migration.keys) {
|
|
|
|
|
return migratedMetadata;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const metadataKeyChanges = migration.keys;
|
|
|
|
|
|
|
|
|
|
metadataKeyChanges.forEach(({ oldKey, newKey }) => {
|
|
|
|
|
if (!doesMetadataKeyExistIn(meta, oldKey)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (doesMetadataKeyExistIn(meta, newKey)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
migratedMetadata[getMetadataKey(newKey)] = meta[getMetadataKey(oldKey)];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return migratedMetadata;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const applyMetadataMigrations = (meta?: Metadata): Metadata | undefined => {
|
|
|
|
|
if (!meta) {
|
|
|
|
|
return undefined;
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-26 17:25:04 +02:00
|
|
|
const appliedMigrationId = Number.isNaN(Number(meta.migration)) ? 0 : Math.max(0, Number(meta.migration) - 1);
|
|
|
|
|
const migrationToMetadata: [number, Metadata][] = [[appliedMigrationId, meta]];
|
2024-10-05 23:17:20 +02:00
|
|
|
|
|
|
|
|
METADATA_MIGRATIONS.forEach((migration, index) => {
|
|
|
|
|
const migrationId = index + 1;
|
|
|
|
|
const metadataToMigrate = migrationToMetadata[migrationId - 1][1];
|
|
|
|
|
const appKeyPrefixMigrated = applyAppKeyPrefixMigration(metadataToMigrate, migration);
|
|
|
|
|
const metadataValuesMigrated = applyMetadataValueMigration(
|
|
|
|
|
appKeyPrefixMigrated,
|
|
|
|
|
migration,
|
|
|
|
|
getAppKeyPrefixForMigration(migrationId),
|
|
|
|
|
);
|
|
|
|
|
const metadataKeysMigrated = applyMetadataKeyMigration(metadataValuesMigrated, migration);
|
|
|
|
|
|
|
|
|
|
migrationToMetadata.push([migrationId, metadataKeysMigrated]);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const appliedMigration = migrationToMetadata.length > 1;
|
|
|
|
|
if (!appliedMigration) {
|
|
|
|
|
return { ...meta };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return migrationToMetadata.pop()![1];
|
|
|
|
|
};
|