refactor: local repo path concat as a code migration
This commit is contained in:
parent
9a68d1039f
commit
c1c8138197
4 changed files with 99 additions and 2178 deletions
|
|
@ -1,23 +0,0 @@
|
||||||
UPDATE `repositories_table`
|
|
||||||
SET
|
|
||||||
`config` = json_set(
|
|
||||||
`config`,
|
|
||||||
'$.path',
|
|
||||||
CASE
|
|
||||||
WHEN json_extract(`config`, '$.path') IS NULL OR trim(json_extract(`config`, '$.path')) = ''
|
|
||||||
THEN '/var/lib/zerobyte/repositories/' || json_extract(`config`, '$.name')
|
|
||||||
ELSE rtrim(json_extract(`config`, '$.path'), '/') || '/' || json_extract(`config`, '$.name')
|
|
||||||
END
|
|
||||||
),
|
|
||||||
`updated_at` = (unixepoch() * 1000)
|
|
||||||
WHERE `type` = 'local'
|
|
||||||
AND json_extract(`config`, '$.name') IS NOT NULL
|
|
||||||
AND trim(json_extract(`config`, '$.name')) <> ''
|
|
||||||
AND (
|
|
||||||
json_extract(`config`, '$.path') IS NULL
|
|
||||||
OR trim(json_extract(`config`, '$.path')) = ''
|
|
||||||
OR substr(
|
|
||||||
rtrim(json_extract(`config`, '$.path'), '/'),
|
|
||||||
-length('/' || json_extract(`config`, '$.name'))
|
|
||||||
) != '/' || json_extract(`config`, '$.name')
|
|
||||||
);
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -2,6 +2,7 @@ import { logger } from "../../utils/logger";
|
||||||
import { v00001 } from "./migrations/00001-retag-snapshots";
|
import { v00001 } from "./migrations/00001-retag-snapshots";
|
||||||
import { v00002 } from "./migrations/00002-isolate-restic-passwords";
|
import { v00002 } from "./migrations/00002-isolate-restic-passwords";
|
||||||
import { v00003 } from "./migrations/00003-assign-organization";
|
import { v00003 } from "./migrations/00003-assign-organization";
|
||||||
|
import { v00004 } from "./migrations/00004-concat-path-name";
|
||||||
import { sql } from "drizzle-orm";
|
import { sql } from "drizzle-orm";
|
||||||
import { appMetadataTable, usersTable } from "../../db/schema";
|
import { appMetadataTable, usersTable } from "../../db/schema";
|
||||||
import { db } from "../../db/db";
|
import { db } from "../../db/db";
|
||||||
|
|
@ -36,7 +37,7 @@ type MigrationEntity = {
|
||||||
dependsOn?: string[];
|
dependsOn?: string[];
|
||||||
};
|
};
|
||||||
|
|
||||||
const registry: MigrationEntity[] = [v00001, v00002, v00003];
|
const registry: MigrationEntity[] = [v00001, v00002, v00003, v00004];
|
||||||
|
|
||||||
export const runMigrations = async () => {
|
export const runMigrations = async () => {
|
||||||
const userCount = await db.select({ count: sql<number>`count(*)` }).from(usersTable);
|
const userCount = await db.select({ count: sql<number>`count(*)` }).from(usersTable);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,97 @@
|
||||||
|
import { and, eq } from "drizzle-orm";
|
||||||
|
import { db } from "../../../db/db";
|
||||||
|
import { repositoriesTable } from "../../../db/schema";
|
||||||
|
import { logger } from "../../../utils/logger";
|
||||||
|
import { toMessage } from "~/server/utils/errors";
|
||||||
|
|
||||||
|
const DEFAULT_LOCAL_REPOSITORY_ROOT = "/var/lib/zerobyte/repositories";
|
||||||
|
|
||||||
|
type MigrationError = { name: string; error: string };
|
||||||
|
|
||||||
|
const asString = (value: unknown): string | null => {
|
||||||
|
if (typeof value !== "string") {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
};
|
||||||
|
|
||||||
|
const hasValue = (value: string | null): value is string => {
|
||||||
|
return value !== null && value.trim() !== "";
|
||||||
|
};
|
||||||
|
|
||||||
|
const trimTrailingSlashes = (value: string): string => {
|
||||||
|
return value.replace(/\/+$/, "");
|
||||||
|
};
|
||||||
|
|
||||||
|
const isPathAlreadyMigrated = (path: string, name: string): boolean => {
|
||||||
|
const normalizedPath = trimTrailingSlashes(path);
|
||||||
|
return normalizedPath.endsWith(`/${name}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
const buildPath = (path: string | null, name: string): string => {
|
||||||
|
if (path === null || path.trim() === "") {
|
||||||
|
return `${DEFAULT_LOCAL_REPOSITORY_ROOT}/${name}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${trimTrailingSlashes(path)}/${name}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const execute = async () => {
|
||||||
|
const errors: MigrationError[] = [];
|
||||||
|
const localRepositories = await db.query.repositoriesTable.findMany({ where: { type: "local" } });
|
||||||
|
let migratedCount = 0;
|
||||||
|
|
||||||
|
for (const repository of localRepositories) {
|
||||||
|
try {
|
||||||
|
const configValue = repository.config as unknown;
|
||||||
|
|
||||||
|
if (typeof configValue !== "object" || configValue === null || Array.isArray(configValue)) {
|
||||||
|
errors.push({
|
||||||
|
name: `repository:${repository.id}`,
|
||||||
|
error: "Repository config is not a valid JSON object",
|
||||||
|
});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const config = { ...(configValue as Record<string, unknown>) };
|
||||||
|
const localRepositoryName = asString(config.name);
|
||||||
|
|
||||||
|
if (!hasValue(localRepositoryName)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentPath = asString(config.path);
|
||||||
|
if (hasValue(currentPath) && isPathAlreadyMigrated(currentPath, localRepositoryName)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
config.path = buildPath(currentPath, localRepositoryName);
|
||||||
|
|
||||||
|
await db
|
||||||
|
.update(repositoriesTable)
|
||||||
|
.set({
|
||||||
|
config: config as typeof repository.config,
|
||||||
|
updatedAt: Date.now(),
|
||||||
|
})
|
||||||
|
.where(and(eq(repositoriesTable.id, repository.id), eq(repositoriesTable.type, "local")));
|
||||||
|
|
||||||
|
migratedCount += 1;
|
||||||
|
} catch (err) {
|
||||||
|
errors.push({
|
||||||
|
name: `repository:${repository.id}`,
|
||||||
|
error: toMessage(err),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info(`Migration 00004-concat-path-name updated ${migratedCount} local repositories.`);
|
||||||
|
|
||||||
|
return { success: errors.length === 0, errors };
|
||||||
|
};
|
||||||
|
|
||||||
|
export const v00004 = {
|
||||||
|
execute,
|
||||||
|
id: "00004-concat-path-name",
|
||||||
|
type: "maintenance" as const,
|
||||||
|
};
|
||||||
Loading…
Reference in a new issue