106 lines
2.6 KiB
TypeScript
106 lines
2.6 KiB
TypeScript
import { defineCommand } from "citty";
|
|
import { apiRequest, type ApiRequestInit } from "../lib/api";
|
|
import { ctx } from "../lib/context";
|
|
import { toTable } from "../lib/format";
|
|
import { getComposeS3Claims, type S3Claim } from "../lib/storage";
|
|
|
|
const GARAGE_UI_URL = "http://garage-ui.garage-system.svc.cluster.local";
|
|
|
|
export type S3ApiRequest = <T>(
|
|
path: string,
|
|
init?: ApiRequestInit,
|
|
) => Promise<T>;
|
|
|
|
export function getRemoteS3Credentials(
|
|
project: string,
|
|
claim: S3Claim,
|
|
request: S3ApiRequest = apiRequest,
|
|
): Promise<Record<string, string>> {
|
|
return request<Record<string, string>>(
|
|
`/workspaces/${encodeURIComponent(project)}/storage/credentials`,
|
|
{ method: "POST", json: { claim } },
|
|
);
|
|
}
|
|
|
|
export function getS3UiUrl(bucket: string): string {
|
|
return `${GARAGE_UI_URL}/buckets/${encodeURIComponent(bucket)}/objects`;
|
|
}
|
|
|
|
async function getServiceClaim(service: string): Promise<S3Claim> {
|
|
const claim = getComposeS3Claims(await ctx().compose()).find(
|
|
(entry) => entry.service === service,
|
|
);
|
|
if (!claim) {
|
|
throw new Error(`Service ${service} does not declare a managed S3 volume.`);
|
|
}
|
|
return claim;
|
|
}
|
|
|
|
const list = defineCommand({
|
|
meta: {
|
|
name: "ls",
|
|
description: "List managed S3 claims",
|
|
},
|
|
async run() {
|
|
const claims = getComposeS3Claims(await ctx().compose());
|
|
if (claims.length === 0) {
|
|
console.log("No managed S3 volumes declared");
|
|
return;
|
|
}
|
|
|
|
console.log(
|
|
toTable(
|
|
claims.map((claim) => ({
|
|
service: claim.service,
|
|
key: claim.key,
|
|
bucket: claim.bucket,
|
|
})),
|
|
),
|
|
);
|
|
},
|
|
});
|
|
|
|
const creds = defineCommand({
|
|
meta: {
|
|
name: "creds",
|
|
description: "Print S3 environment variables for a service",
|
|
},
|
|
async run({ args }) {
|
|
const service = args._[0];
|
|
if (!service) throw new Error("Service name is required");
|
|
|
|
const credentials = await getRemoteS3Credentials(
|
|
ctx().project,
|
|
await getServiceClaim(service),
|
|
);
|
|
for (const [name, value] of Object.entries(credentials)) {
|
|
console.log(`${name}=${value}`);
|
|
}
|
|
},
|
|
});
|
|
|
|
const ui = defineCommand({
|
|
meta: {
|
|
name: "ui",
|
|
description: "Print the Garage UI URL for a service bucket",
|
|
},
|
|
async run({ args }) {
|
|
const service = args._[0];
|
|
if (!service) throw new Error("Service name is required");
|
|
|
|
console.log(getS3UiUrl((await getServiceClaim(service)).bucket));
|
|
},
|
|
});
|
|
|
|
export const s3 = defineCommand({
|
|
meta: {
|
|
name: "s3",
|
|
description: "Inspect managed S3 storage",
|
|
},
|
|
subCommands: {
|
|
creds,
|
|
ls: list,
|
|
ui,
|
|
},
|
|
});
|