mirror of
https://github.com/fosrl/pangolin.git
synced 2026-10-20 11:39:06 +02:00
Add export all blueprints cli command
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
import { CommandModule } from "yargs";
|
||||
import { stringify as stringifyYaml } from "yaml";
|
||||
import { asc, eq } from "drizzle-orm";
|
||||
import { db, orgs, resources, siteResources } from "@server/db";
|
||||
import {
|
||||
buildPrivateResourceBlueprintConfig,
|
||||
buildPublicResourceBlueprintConfig,
|
||||
formatBlueprintNotes
|
||||
} from "@server/lib/blueprints/exportResourceBlueprint";
|
||||
|
||||
type ExportBlueprintsArgs = {
|
||||
orgId?: string;
|
||||
};
|
||||
|
||||
async function buildOrgBlueprint(orgId: string): Promise<string | null> {
|
||||
const [publicRows, privateRows] = await Promise.all([
|
||||
db
|
||||
.select({ resourceId: resources.resourceId })
|
||||
.from(resources)
|
||||
.where(eq(resources.orgId, orgId))
|
||||
.orderBy(asc(resources.resourceId)),
|
||||
db
|
||||
.select({ siteResourceId: siteResources.siteResourceId })
|
||||
.from(siteResources)
|
||||
.where(eq(siteResources.orgId, orgId))
|
||||
.orderBy(asc(siteResources.siteResourceId))
|
||||
]);
|
||||
|
||||
if (publicRows.length === 0 && privateRows.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const notes: string[] = [];
|
||||
const proxyResources: Record<string, any> = {};
|
||||
const clientResources: Record<string, any> = {};
|
||||
|
||||
for (const { resourceId } of publicRows) {
|
||||
const built = await buildPublicResourceBlueprintConfig(resourceId);
|
||||
proxyResources[built.niceId] = built.config;
|
||||
notes.push(...built.notes.map((n) => `${built.niceId}: ${n}`));
|
||||
}
|
||||
|
||||
for (const { siteResourceId } of privateRows) {
|
||||
const built = await buildPrivateResourceBlueprintConfig(siteResourceId);
|
||||
clientResources[built.niceId] = built.config;
|
||||
}
|
||||
|
||||
const config: Record<string, any> = {};
|
||||
if (publicRows.length > 0) {
|
||||
config["proxy-resources"] = proxyResources;
|
||||
}
|
||||
if (privateRows.length > 0) {
|
||||
config["client-resources"] = clientResources;
|
||||
}
|
||||
|
||||
return (
|
||||
`# org: ${orgId}\n` +
|
||||
formatBlueprintNotes(notes) +
|
||||
stringifyYaml(config)
|
||||
);
|
||||
}
|
||||
|
||||
export const exportBlueprints: CommandModule<{}, ExportBlueprintsArgs> = {
|
||||
command: "export-blueprints",
|
||||
describe:
|
||||
"Export all public resources and site resources as blueprint YAML to stdout (one document per organization)",
|
||||
builder: (yargs) => {
|
||||
return yargs.option("orgId", {
|
||||
type: "string",
|
||||
describe: "Only export resources for this organization ID"
|
||||
});
|
||||
},
|
||||
handler: async (argv: ExportBlueprintsArgs) => {
|
||||
try {
|
||||
let orgIds: string[];
|
||||
if (argv.orgId) {
|
||||
const [org] = await db
|
||||
.select({ orgId: orgs.orgId })
|
||||
.from(orgs)
|
||||
.where(eq(orgs.orgId, argv.orgId))
|
||||
.limit(1);
|
||||
if (!org) {
|
||||
console.error(
|
||||
`Error: Organization "${argv.orgId}" not found.`
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
orgIds = [org.orgId];
|
||||
} else {
|
||||
const rows = await db
|
||||
.select({ orgId: orgs.orgId })
|
||||
.from(orgs)
|
||||
.orderBy(asc(orgs.orgId));
|
||||
orgIds = rows.map((r) => r.orgId);
|
||||
}
|
||||
|
||||
// Only YAML goes to stdout so the output can be piped to a file
|
||||
const documents: string[] = [];
|
||||
for (const orgId of orgIds) {
|
||||
const doc = await buildOrgBlueprint(orgId);
|
||||
if (doc) {
|
||||
documents.push(doc);
|
||||
}
|
||||
}
|
||||
|
||||
if (documents.length === 0) {
|
||||
console.error("No resources found to export.");
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
process.stdout.write(documents.join("---\n"));
|
||||
|
||||
process.exit(0);
|
||||
} catch (error) {
|
||||
console.error("Error exporting blueprints:", error);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -1,5 +1,6 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import "@server/extendZod";
|
||||
import yargs from "yargs";
|
||||
import { hideBin } from "yargs/helpers";
|
||||
import { setAdminCredentials } from "./commands/setAdminCredentials";
|
||||
@@ -12,6 +13,7 @@ import { generateOrgCaKeys } from "./commands/generateOrgCaKeys";
|
||||
import { clearCertificates } from "./commands/clearCertificates";
|
||||
import { disableUser2fa } from "./commands/disableUser2fa";
|
||||
import { setServerAdmin } from "./commands/setServerAdmin";
|
||||
import { exportBlueprints } from "./commands/exportBlueprints";
|
||||
|
||||
yargs(hideBin(process.argv))
|
||||
.scriptName("pangctl")
|
||||
@@ -25,5 +27,6 @@ yargs(hideBin(process.argv))
|
||||
.command(clearCertificates)
|
||||
.command(disableUser2fa)
|
||||
.command(setServerAdmin)
|
||||
.command(exportBlueprints)
|
||||
.demandCommand()
|
||||
.help().argv;
|
||||
|
||||
@@ -365,9 +365,13 @@ async function buildAiProvidersAndBudget(input:
|
||||
return { aiProvidersConfig, aiBudgetConfig };
|
||||
}
|
||||
|
||||
export async function generatePublicResourceBlueprintYaml(
|
||||
export async function buildPublicResourceBlueprintConfig(
|
||||
resourceId: number
|
||||
): Promise<{ niceId: string; contents: string }> {
|
||||
): Promise<{
|
||||
niceId: string;
|
||||
config: Record<string, any>;
|
||||
notes: string[];
|
||||
}> {
|
||||
const [resource] = await db
|
||||
.select()
|
||||
.from(resources)
|
||||
@@ -539,26 +543,32 @@ export async function generatePublicResourceBlueprintYaml(
|
||||
}
|
||||
}
|
||||
|
||||
const config = {
|
||||
"proxy-resources": {
|
||||
[resource.niceId]: resourceConfig
|
||||
}
|
||||
};
|
||||
return { niceId: resource.niceId, config: resourceConfig, notes };
|
||||
}
|
||||
|
||||
const notesPrefix =
|
||||
notes.length > 0
|
||||
? notes.map((n) => `# ${n}`).join("\n") + "\n\n"
|
||||
: "";
|
||||
export function formatBlueprintNotes(notes: string[]): string {
|
||||
return notes.length > 0
|
||||
? notes.map((n) => `# ${n}`).join("\n") + "\n\n"
|
||||
: "";
|
||||
}
|
||||
|
||||
export async function generatePublicResourceBlueprintYaml(
|
||||
resourceId: number
|
||||
): Promise<{ niceId: string; contents: string }> {
|
||||
const { niceId, config, notes } =
|
||||
await buildPublicResourceBlueprintConfig(resourceId);
|
||||
|
||||
return {
|
||||
niceId: resource.niceId,
|
||||
contents: notesPrefix + stringifyYaml(config)
|
||||
niceId,
|
||||
contents:
|
||||
formatBlueprintNotes(notes) +
|
||||
stringifyYaml({ "proxy-resources": { [niceId]: config } })
|
||||
};
|
||||
}
|
||||
|
||||
export async function generatePrivateResourceBlueprintYaml(
|
||||
export async function buildPrivateResourceBlueprintConfig(
|
||||
siteResourceId: number
|
||||
): Promise<{ niceId: string; contents: string }> {
|
||||
): Promise<{ niceId: string; config: Record<string, any> }> {
|
||||
const [siteResource] = await db
|
||||
.select()
|
||||
.from(siteResources)
|
||||
@@ -676,14 +686,17 @@ export async function generatePrivateResourceBlueprintYaml(
|
||||
}
|
||||
}
|
||||
|
||||
const config = {
|
||||
"client-resources": {
|
||||
[siteResource.niceId]: resourceConfig
|
||||
}
|
||||
};
|
||||
return { niceId: siteResource.niceId, config: resourceConfig };
|
||||
}
|
||||
|
||||
export async function generatePrivateResourceBlueprintYaml(
|
||||
siteResourceId: number
|
||||
): Promise<{ niceId: string; contents: string }> {
|
||||
const { niceId, config } =
|
||||
await buildPrivateResourceBlueprintConfig(siteResourceId);
|
||||
|
||||
return {
|
||||
niceId: siteResource.niceId,
|
||||
contents: stringifyYaml(config)
|
||||
niceId,
|
||||
contents: stringifyYaml({ "client-resources": { [niceId]: config } })
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user