refactor: address PR feedbacks
This commit is contained in:
parent
c416864e4b
commit
5d896555fa
3 changed files with 77 additions and 34 deletions
|
|
@ -73,8 +73,6 @@ runDbMigrations();
|
||||||
await migrateToShortIds();
|
await migrateToShortIds();
|
||||||
await validateRequiredMigrations(REQUIRED_MIGRATIONS);
|
await validateRequiredMigrations(REQUIRED_MIGRATIONS);
|
||||||
|
|
||||||
await validateRequiredMigrations(["v0.14.0"]);
|
|
||||||
|
|
||||||
const { docker } = await getCapabilities();
|
const { docker } = await getCapabilities();
|
||||||
|
|
||||||
if (docker) {
|
if (docker) {
|
||||||
|
|
|
||||||
|
|
@ -44,12 +44,12 @@ export const validateRequiredMigrations = async (requiredVersions: string[]): Pr
|
||||||
if (isFreshInstall) {
|
if (isFreshInstall) {
|
||||||
logger.info("Fresh installation detected, skipping migration checkpoint validation.");
|
logger.info("Fresh installation detected, skipping migration checkpoint validation.");
|
||||||
|
|
||||||
REQUIRED_MIGRATIONS.forEach(async (version) => {
|
for (const version of requiredVersions) {
|
||||||
const hasCheckpoint = await hasMigrationCheckpoint(version);
|
const hasCheckpoint = await hasMigrationCheckpoint(version);
|
||||||
if (!hasCheckpoint) {
|
if (!hasCheckpoint) {
|
||||||
await recordMigrationCheckpoint(version);
|
await recordMigrationCheckpoint(version);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,24 @@ import type { RepositoryConfig } from "~/schemas/restic";
|
||||||
|
|
||||||
const MIGRATION_VERSION = "v0.14.0";
|
const MIGRATION_VERSION = "v0.14.0";
|
||||||
|
|
||||||
|
interface MigrationResult {
|
||||||
|
success: boolean;
|
||||||
|
errors: Array<{ name: string; error: string }>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MigrationError extends Error {
|
||||||
|
version: string;
|
||||||
|
failedItems: Array<{ name: string; error: string }>;
|
||||||
|
|
||||||
|
constructor(version: string, failedItems: Array<{ name: string; error: string }>) {
|
||||||
|
const itemNames = failedItems.map((e) => e.name).join(", ");
|
||||||
|
super(`Migration ${version} failed for: ${itemNames}`);
|
||||||
|
this.version = version;
|
||||||
|
this.failedItems = failedItems;
|
||||||
|
this.name = "MigrationError";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const migrateToShortIds = async () => {
|
export const migrateToShortIds = async () => {
|
||||||
const alreadyMigrated = await hasMigrationCheckpoint(MIGRATION_VERSION);
|
const alreadyMigrated = await hasMigrationCheckpoint(MIGRATION_VERSION);
|
||||||
if (alreadyMigrated) {
|
if (alreadyMigrated) {
|
||||||
|
|
@ -19,15 +37,25 @@ export const migrateToShortIds = async () => {
|
||||||
|
|
||||||
logger.info(`Starting short ID migration (${MIGRATION_VERSION})...`);
|
logger.info(`Starting short ID migration (${MIGRATION_VERSION})...`);
|
||||||
|
|
||||||
await migrateVolumeFolders();
|
const volumeResult = await migrateVolumeFolders();
|
||||||
await migrateRepositoryFolders();
|
const repoResult = await migrateRepositoryFolders();
|
||||||
|
|
||||||
|
const allErrors = [...volumeResult.errors, ...repoResult.errors];
|
||||||
|
|
||||||
|
if (allErrors.length > 0) {
|
||||||
|
for (const err of allErrors) {
|
||||||
|
logger.error(`Migration failure - ${err.name}: ${err.error}`);
|
||||||
|
}
|
||||||
|
throw new MigrationError(MIGRATION_VERSION, allErrors);
|
||||||
|
}
|
||||||
|
|
||||||
await recordMigrationCheckpoint(MIGRATION_VERSION);
|
await recordMigrationCheckpoint(MIGRATION_VERSION);
|
||||||
|
|
||||||
logger.info(`Short ID migration (${MIGRATION_VERSION}) complete.`);
|
logger.info(`Short ID migration (${MIGRATION_VERSION}) complete.`);
|
||||||
};
|
};
|
||||||
|
|
||||||
const migrateVolumeFolders = async () => {
|
const migrateVolumeFolders = async (): Promise<MigrationResult> => {
|
||||||
|
const errors: Array<{ name: string; error: string }> = [];
|
||||||
const volumes = await db.query.volumesTable.findMany({});
|
const volumes = await db.query.volumesTable.findMany({});
|
||||||
|
|
||||||
for (const volume of volumes) {
|
for (const volume of volumes) {
|
||||||
|
|
@ -47,7 +75,8 @@ const migrateVolumeFolders = async () => {
|
||||||
await fs.rename(oldPath, newPath);
|
await fs.rename(oldPath, newPath);
|
||||||
logger.info(`Successfully migrated volume folder for "${volume.name}"`);
|
logger.info(`Successfully migrated volume folder for "${volume.name}"`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(`Failed to migrate volume folder for "${volume.name}": ${error}`);
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||||
|
errors.push({ name: `volume:${volume.name}`, error: errorMessage });
|
||||||
}
|
}
|
||||||
} else if (oldExists && newExists) {
|
} else if (oldExists && newExists) {
|
||||||
logger.warn(
|
logger.warn(
|
||||||
|
|
@ -55,9 +84,12 @@ const migrateVolumeFolders = async () => {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return { success: errors.length === 0, errors };
|
||||||
};
|
};
|
||||||
|
|
||||||
const migrateRepositoryFolders = async () => {
|
const migrateRepositoryFolders = async (): Promise<MigrationResult> => {
|
||||||
|
const errors: Array<{ name: string; error: string }> = [];
|
||||||
const repositories = await db.query.repositoriesTable.findMany({});
|
const repositories = await db.query.repositoriesTable.findMany({});
|
||||||
|
|
||||||
for (const repo of repositories) {
|
for (const repo of repositories) {
|
||||||
|
|
@ -98,44 +130,57 @@ const migrateRepositoryFolders = async () => {
|
||||||
|
|
||||||
logger.info(`Successfully migrated repository folder and config for "${repo.name}"`);
|
logger.info(`Successfully migrated repository folder and config for "${repo.name}"`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(`Failed to migrate repository folder for "${repo.name}": ${error}`);
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||||
|
errors.push({ name: `repository:${repo.name}`, error: errorMessage });
|
||||||
}
|
}
|
||||||
} else if (oldExists && newExists) {
|
} else if (oldExists && newExists) {
|
||||||
logger.warn(
|
logger.warn(
|
||||||
`Both old (${oldPath}) and new (${newPath}) paths exist for repository "${repo.name}". Manual intervention may be required.`,
|
`Both old (${oldPath}) and new (${newPath}) paths exist for repository "${repo.name}". Manual intervention may be required.`,
|
||||||
);
|
);
|
||||||
} else if (!oldExists && !newExists) {
|
} else if (!oldExists && !newExists) {
|
||||||
logger.info(`Updating config.name for repository "${repo.name}" (no folder exists yet)`);
|
try {
|
||||||
|
logger.info(`Updating config.name for repository "${repo.name}" (no folder exists yet)`);
|
||||||
|
|
||||||
const updatedConfig: RepositoryConfig = {
|
const updatedConfig: RepositoryConfig = {
|
||||||
...config,
|
...config,
|
||||||
name: repo.shortId,
|
name: repo.shortId,
|
||||||
};
|
};
|
||||||
|
|
||||||
await db
|
await db
|
||||||
.update(repositoriesTable)
|
.update(repositoriesTable)
|
||||||
.set({
|
.set({
|
||||||
config: updatedConfig,
|
config: updatedConfig,
|
||||||
updatedAt: Math.floor(Date.now() / 1000),
|
updatedAt: Math.floor(Date.now() / 1000),
|
||||||
})
|
})
|
||||||
.where(eq(repositoriesTable.id, repo.id));
|
.where(eq(repositoriesTable.id, repo.id));
|
||||||
|
} catch (error) {
|
||||||
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||||
|
errors.push({ name: `repository:${repo.name}`, error: errorMessage });
|
||||||
|
}
|
||||||
} else if (newExists && !oldExists && config.name !== repo.shortId) {
|
} else if (newExists && !oldExists && config.name !== repo.shortId) {
|
||||||
logger.info(`Folder already at new path, updating config.name for repository "${repo.name}"`);
|
try {
|
||||||
|
logger.info(`Folder already at new path, updating config.name for repository "${repo.name}"`);
|
||||||
|
|
||||||
const updatedConfig: RepositoryConfig = {
|
const updatedConfig: RepositoryConfig = {
|
||||||
...config,
|
...config,
|
||||||
name: repo.shortId,
|
name: repo.shortId,
|
||||||
};
|
};
|
||||||
|
|
||||||
await db
|
await db
|
||||||
.update(repositoriesTable)
|
.update(repositoriesTable)
|
||||||
.set({
|
.set({
|
||||||
config: updatedConfig,
|
config: updatedConfig,
|
||||||
updatedAt: Math.floor(Date.now() / 1000),
|
updatedAt: Math.floor(Date.now() / 1000),
|
||||||
})
|
})
|
||||||
.where(eq(repositoriesTable.id, repo.id));
|
.where(eq(repositoriesTable.id, repo.id));
|
||||||
|
} catch (error) {
|
||||||
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||||
|
errors.push({ name: `repository:${repo.name}`, error: errorMessage });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return { success: errors.length === 0, errors };
|
||||||
};
|
};
|
||||||
|
|
||||||
const pathExists = async (p: string): Promise<boolean> => {
|
const pathExists = async (p: string): Promise<boolean> => {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue