feat(releases-widget): define providers as integrations (#3253)
Co-authored-by: Meier Lukas <meierschlumpf@gmail.com>
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}"`);
|
||||
}
|
||||
6
packages/db/migrations/custom/index.ts
Normal file
6
packages/db/migrations/custom/index.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import type { Database } from "../..";
|
||||
import { migrateReleaseWidgetProviderToOptionsAsync } from "./0000_release_widget_provider_to_options";
|
||||
|
||||
export const applyCustomMigrationsAsync = async (db: Database) => {
|
||||
await migrateReleaseWidgetProviderToOptionsAsync(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 { database } from "../../driver";
|
||||
|
||||
applyCustomMigrationsAsync(database)
|
||||
.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);
|
||||
});
|
||||
@@ -5,6 +5,7 @@ import mysql from "mysql2";
|
||||
import type { Database } from "../..";
|
||||
import { env } from "../../env";
|
||||
import * as mysqlSchema from "../../schema/mysql";
|
||||
import { applyCustomMigrationsAsync } from "../custom";
|
||||
import { seedDataAsync } from "../seed";
|
||||
|
||||
const migrationsFolder = process.argv[2] ?? ".";
|
||||
@@ -30,6 +31,7 @@ const migrateAsync = async () => {
|
||||
|
||||
await migrate(db, { migrationsFolder });
|
||||
await seedDataAsync(db as unknown as Database);
|
||||
await applyCustomMigrationsAsync(db as unknown as Database);
|
||||
};
|
||||
|
||||
migrateAsync()
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import { objectKeys } from "@homarr/common";
|
||||
import { createDocumentationLink, everyoneGroup } from "@homarr/definitions";
|
||||
import {
|
||||
createDocumentationLink,
|
||||
everyoneGroup,
|
||||
getIntegrationDefaultUrl,
|
||||
getIntegrationName,
|
||||
integrationKinds,
|
||||
} from "@homarr/definitions";
|
||||
import { defaultServerSettings, defaultServerSettingsKeys } from "@homarr/server-settings";
|
||||
|
||||
import type { Database } from "..";
|
||||
@@ -9,7 +15,8 @@ import {
|
||||
insertServerSettingByKeyAsync,
|
||||
updateServerSettingByKeyAsync,
|
||||
} from "../queries/server-setting";
|
||||
import { onboarding, searchEngines } from "../schema";
|
||||
import { integrations, onboarding, searchEngines } from "../schema";
|
||||
import type { Integration } from "../schema";
|
||||
import { groups } from "../schema/mysql";
|
||||
|
||||
export const seedDataAsync = async (db: Database) => {
|
||||
@@ -17,6 +24,7 @@ export const seedDataAsync = async (db: Database) => {
|
||||
await seedOnboardingAsync(db);
|
||||
await seedServerSettingsAsync(db);
|
||||
await seedDefaultSearchEnginesAsync(db);
|
||||
await seedDefaultIntegrationsAsync(db);
|
||||
};
|
||||
|
||||
const seedEveryoneGroupAsync = async (db: Database) => {
|
||||
@@ -131,3 +139,53 @@ const seedServerSettingsAsync = async (db: Database) => {
|
||||
console.log(`Updated serverSetting through seed key=${settingsKey}`);
|
||||
}
|
||||
};
|
||||
|
||||
const seedDefaultIntegrationsAsync = async (db: Database) => {
|
||||
const defaultIntegrations = integrationKinds.reduce<Integration[]>((acc, kind) => {
|
||||
const name = getIntegrationName(kind);
|
||||
const defaultUrl = getIntegrationDefaultUrl(kind);
|
||||
|
||||
if (defaultUrl !== undefined) {
|
||||
acc.push({
|
||||
id: "new",
|
||||
name: `${name} Default`,
|
||||
url: defaultUrl,
|
||||
kind,
|
||||
});
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
if (defaultIntegrations.length === 0) {
|
||||
console.warn("No default integrations found to seed");
|
||||
return;
|
||||
}
|
||||
|
||||
let createdCount = 0;
|
||||
await Promise.all(
|
||||
defaultIntegrations.map(async (integration) => {
|
||||
const existingKind = await db.$count(integrations, eq(integrations.kind, integration.kind));
|
||||
|
||||
if (existingKind > 0) {
|
||||
console.log(`Skipping seeding of default ${integration.kind} integration as one already exists`);
|
||||
return;
|
||||
}
|
||||
|
||||
const newIntegration = {
|
||||
...integration,
|
||||
id: createId(),
|
||||
};
|
||||
|
||||
await db.insert(integrations).values(newIntegration);
|
||||
createdCount++;
|
||||
}),
|
||||
);
|
||||
|
||||
if (createdCount === 0) {
|
||||
console.log("No default integrations were created as they already exist");
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`Created ${createdCount} default integrations through seeding process`);
|
||||
};
|
||||
|
||||
@@ -4,6 +4,7 @@ import { migrate } from "drizzle-orm/better-sqlite3/migrator";
|
||||
|
||||
import { env } from "../../env";
|
||||
import * as sqliteSchema from "../../schema/sqlite";
|
||||
import { applyCustomMigrationsAsync } from "../custom";
|
||||
import { seedDataAsync } from "../seed";
|
||||
|
||||
const migrationsFolder = process.argv[2] ?? ".";
|
||||
@@ -16,6 +17,7 @@ const migrateAsync = async () => {
|
||||
migrate(db, { migrationsFolder });
|
||||
|
||||
await seedDataAsync(db);
|
||||
await applyCustomMigrationsAsync(db);
|
||||
};
|
||||
|
||||
migrateAsync()
|
||||
|
||||
@@ -23,12 +23,13 @@
|
||||
"clean": "rm -rf .turbo node_modules",
|
||||
"format": "prettier --check . --ignore-path ../../.gitignore",
|
||||
"lint": "eslint",
|
||||
"migration:custom": "pnpm with-env tsx ./migrations/custom/run-custom.ts",
|
||||
"migration:mysql:drop": "pnpm with-env drizzle-kit drop --config ./configs/mysql.config.ts",
|
||||
"migration:mysql:generate": "pnpm with-env drizzle-kit generate --config ./configs/mysql.config.ts",
|
||||
"migration:mysql:run": "pnpm with-env drizzle-kit migrate --config ./configs/mysql.config.ts && pnpm run seed",
|
||||
"migration:mysql:run": "pnpm with-env drizzle-kit migrate --config ./configs/mysql.config.ts && pnpm run seed && pnpm run migration:custom",
|
||||
"migration:sqlite:drop": "pnpm with-env drizzle-kit drop --config ./configs/sqlite.config.ts",
|
||||
"migration:sqlite:generate": "pnpm with-env drizzle-kit generate --config ./configs/sqlite.config.ts",
|
||||
"migration:sqlite:run": "pnpm with-env drizzle-kit migrate --config ./configs/sqlite.config.ts && pnpm run seed",
|
||||
"migration:sqlite:run": "pnpm with-env drizzle-kit migrate --config ./configs/sqlite.config.ts && pnpm run seed && pnpm run migration:custom",
|
||||
"push:mysql": "pnpm with-env drizzle-kit push --config ./configs/mysql.config.ts",
|
||||
"push:sqlite": "pnpm with-env drizzle-kit push --config ./configs/sqlite.config.ts",
|
||||
"seed": "pnpm with-env tsx ./migrations/run-seed.ts",
|
||||
@@ -52,7 +53,8 @@
|
||||
"drizzle-kit": "^0.31.4",
|
||||
"drizzle-orm": "^0.44.2",
|
||||
"drizzle-zod": "^0.7.1",
|
||||
"mysql2": "3.14.2"
|
||||
"mysql2": "3.14.2",
|
||||
"superjson": "2.2.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
|
||||
Reference in New Issue
Block a user