Replace entire codebase with homarr-labs/homarr
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
import SuperJSON from "superjson";
|
||||
|
||||
import { createId } from "@homarr/common";
|
||||
import type { IntegrationKind } from "@homarr/definitions";
|
||||
import { getIntegrationKindsByCategory } from "@homarr/definitions";
|
||||
|
||||
import { eq } from "../..";
|
||||
import type { Database } from "../..";
|
||||
import { items } from "../../schema";
|
||||
|
||||
export async function migrateReleaseWidgetProviderToOptionsAsync(db: Database) {
|
||||
const existingItems = await db.query.items.findMany({
|
||||
where: (items, { eq }) => eq(items.kind, "releases"),
|
||||
});
|
||||
|
||||
const integrationKinds = getIntegrationKindsByCategory("releasesProvider");
|
||||
const providerIntegrations = await db.query.integrations.findMany({
|
||||
where: (integrations, { inArray }) => inArray(integrations.kind, integrationKinds),
|
||||
columns: {
|
||||
id: true,
|
||||
kind: true,
|
||||
},
|
||||
});
|
||||
|
||||
const providerIntegrationMap = new Map<IntegrationKind, string>(
|
||||
providerIntegrations.map((integration) => [integration.kind, integration.id]),
|
||||
);
|
||||
|
||||
const updates: {
|
||||
id: string;
|
||||
options: object;
|
||||
}[] = [];
|
||||
for (const item of existingItems) {
|
||||
const options = SuperJSON.parse<object>(item.options);
|
||||
if (!("repositories" in options)) continue;
|
||||
if (!Array.isArray(options.repositories)) continue;
|
||||
if (options.repositories.length === 0) continue;
|
||||
if (!options.repositories.some((repository) => "providerKey" in repository)) continue;
|
||||
|
||||
const updatedRepositories = options.repositories.map(
|
||||
({ providerKey, ...otherFields }: { providerKey: string; [key: string]: unknown }) => {
|
||||
// Ensure providerKey is camelCase
|
||||
const provider = providerKey.charAt(0).toLowerCase() + providerKey.slice(1);
|
||||
|
||||
return {
|
||||
id: createId(),
|
||||
providerIntegrationId: providerIntegrationMap.get(provider as IntegrationKind) ?? null,
|
||||
...otherFields,
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
updates.push({
|
||||
id: item.id,
|
||||
options: {
|
||||
...options,
|
||||
repositories: updatedRepositories,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
for (const update of updates) {
|
||||
await db
|
||||
.update(items)
|
||||
.set({
|
||||
options: SuperJSON.stringify(update.options),
|
||||
})
|
||||
.where(eq(items.id, update.id));
|
||||
}
|
||||
|
||||
console.log(`Migrated release widget providers to integrations count="${updates.length}"`);
|
||||
}
|
||||
52
packages/db/migrations/custom/0001_opnsense_credentials.ts
Normal file
52
packages/db/migrations/custom/0001_opnsense_credentials.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import type { Database } from "../..";
|
||||
import { and, eq } from "../..";
|
||||
import { integrationSecrets } from "../../schema";
|
||||
|
||||
/**
|
||||
* Previously the credentials for OPNsense were stored as username and password.
|
||||
* However it should have been the api key and secret.
|
||||
* For more information see:
|
||||
* https://docs.opnsense.org/development/how-tos/api.html#creating-keys
|
||||
*/
|
||||
export async function migrateOpnsenseCredentialsAsync(db: Database) {
|
||||
const existingIntegrations = await db.query.integrations.findMany({
|
||||
where: (table, { eq }) => eq(table.kind, "opnsense"),
|
||||
with: {
|
||||
secrets: true,
|
||||
},
|
||||
});
|
||||
|
||||
await Promise.all(
|
||||
existingIntegrations.map(async (integration) => {
|
||||
const username = integration.secrets.find((secret) => secret.kind === "username");
|
||||
if (!username) return;
|
||||
await db
|
||||
.update(integrationSecrets)
|
||||
.set({
|
||||
kind: "opnsenseApiKey",
|
||||
})
|
||||
.where(
|
||||
and(eq(integrationSecrets.integrationId, username.integrationId), eq(integrationSecrets.kind, "username")),
|
||||
);
|
||||
}),
|
||||
);
|
||||
|
||||
await Promise.all(
|
||||
existingIntegrations.map(async (integration) => {
|
||||
const password = integration.secrets.find((secret) => secret.kind === "password");
|
||||
if (!password) return;
|
||||
await db
|
||||
.update(integrationSecrets)
|
||||
.set({
|
||||
kind: "opnsenseApiSecret",
|
||||
})
|
||||
.where(
|
||||
and(eq(integrationSecrets.integrationId, password.integrationId), eq(integrationSecrets.kind, "password")),
|
||||
);
|
||||
}),
|
||||
);
|
||||
|
||||
if (existingIntegrations.length > 0) {
|
||||
console.log(`Migrated OPNsense credentials count="${existingIntegrations.length}"`);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import SuperJSON from "superjson";
|
||||
|
||||
import { eq } from "../..";
|
||||
import type { Database } from "../..";
|
||||
import { items } from "../../schema";
|
||||
|
||||
/**
|
||||
* To support showing the description in the widget itself we replaced
|
||||
* the tooltip show option with display mode.
|
||||
*/
|
||||
export async function migrateAppWidgetShowDescriptionTooltipToDisplayModeAsync(db: Database) {
|
||||
const existingAppItems = await db.query.items.findMany({
|
||||
where: (table, { eq }) => eq(table.kind, "app"),
|
||||
});
|
||||
|
||||
const itemsToUpdate = existingAppItems
|
||||
.map((item) => ({
|
||||
id: item.id,
|
||||
options: SuperJSON.parse<{ showDescriptionTooltip?: boolean }>(item.options),
|
||||
}))
|
||||
.filter((item) => item.options.showDescriptionTooltip !== undefined);
|
||||
|
||||
console.log(
|
||||
`Migrating app items with showDescriptionTooltip to descriptionDisplayMode count=${itemsToUpdate.length}`,
|
||||
);
|
||||
|
||||
await Promise.all(
|
||||
itemsToUpdate.map(async (item) => {
|
||||
const { showDescriptionTooltip, ...options } = item.options;
|
||||
await db
|
||||
.update(items)
|
||||
.set({
|
||||
options: SuperJSON.stringify({
|
||||
...options,
|
||||
descriptionDisplayMode: showDescriptionTooltip ? "tooltip" : "hidden",
|
||||
}),
|
||||
})
|
||||
.where(eq(items.id, item.id));
|
||||
}),
|
||||
);
|
||||
|
||||
console.log(`Migrated app items with showDescriptionTooltip to descriptionDisplayMode count=${itemsToUpdate.length}`);
|
||||
}
|
||||
10
packages/db/migrations/custom/index.ts
Normal file
10
packages/db/migrations/custom/index.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import type { Database } from "../..";
|
||||
import { migrateReleaseWidgetProviderToOptionsAsync } from "./0000_release_widget_provider_to_options";
|
||||
import { migrateOpnsenseCredentialsAsync } from "./0001_opnsense_credentials";
|
||||
import { migrateAppWidgetShowDescriptionTooltipToDisplayModeAsync } from "./0002_app_widget_show_description_tooltip_to_display_mode";
|
||||
|
||||
export const applyCustomMigrationsAsync = async (db: Database) => {
|
||||
await migrateReleaseWidgetProviderToOptionsAsync(db);
|
||||
await migrateOpnsenseCredentialsAsync(db);
|
||||
await migrateAppWidgetShowDescriptionTooltipToDisplayModeAsync(db);
|
||||
};
|
||||
12
packages/db/migrations/custom/run-custom.ts
Normal file
12
packages/db/migrations/custom/run-custom.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { applyCustomMigrationsAsync } from ".";
|
||||
import { db } from "../..";
|
||||
|
||||
applyCustomMigrationsAsync(db)
|
||||
.then(() => {
|
||||
console.log("Custom migrations applied successfully");
|
||||
process.exit(0);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log("Failed to apply custom migrations\n\t", err);
|
||||
process.exit(1);
|
||||
});
|
||||
Reference in New Issue
Block a user