A Docker-like CLI and HTTP API for managing headless VMs
1import { Table } from "@cliffy/table";
2import dayjs from "dayjs";
3import relativeTime from "dayjs/plugin/relativeTime.js";
4import utc from "dayjs/plugin/utc.js";
5import { Effect, pipe } from "effect";
6import type { Volume } from "../db.ts";
7import type { DbError } from "../errors.ts";
8import { deleteVolume, getVolume, listVolumes } from "../volumes.ts";
9
10dayjs.extend(relativeTime);
11dayjs.extend(utc);
12
13const createTable = () =>
14 Effect.succeed(new Table(["NAME", "VOLUME ID", "CREATED"]));
15
16const populateTable = (table: Table, volumes: Volume[]) =>
17 Effect.sync(() => {
18 for (const volume of volumes) {
19 table.push([
20 volume.name,
21 volume.id,
22 dayjs.utc(volume.createdAt).local().fromNow(),
23 ]);
24 }
25 return table;
26 });
27
28const displayTable = (table: Table) =>
29 Effect.sync(() => {
30 console.log(table.padding(2).toString());
31 });
32
33const handleError = (error: DbError | Error) =>
34 Effect.sync(() => {
35 console.error(`Failed to fetch volumes: ${error}`);
36 Deno.exit(1);
37 });
38
39const lsEffect = () =>
40 pipe(
41 Effect.all([listVolumes(), createTable()]),
42 Effect.flatMap(([volumes, table]) => populateTable(table, volumes)),
43 Effect.flatMap(displayTable),
44 Effect.catchAll(handleError),
45 );
46
47export async function list() {
48 await Effect.runPromise(lsEffect());
49}
50
51export async function remove(name: string) {
52 await Effect.runPromise(
53 pipe(
54 getVolume(name),
55 Effect.flatMap((volume) =>
56 volume
57 ? deleteVolume(volume.id)
58 : Effect.fail(new Error(`Volume with name or ID ${name} not found.`))
59 ),
60 Effect.tap(() =>
61 Effect.sync(() => {
62 console.log(`Volume ${name} deleted successfully.`);
63 })
64 ),
65 Effect.catchAll((error) =>
66 Effect.sync(() => {
67 console.error(`An error occurred: ${error}`);
68 Deno.exit(1);
69 })
70 ),
71 ),
72 );
73}
74
75export async function inspect(name: string) {
76 await Effect.runPromise(
77 pipe(
78 getVolume(name),
79 Effect.flatMap((volume) =>
80 volume
81 ? Effect.sync(() => {
82 console.log(volume);
83 })
84 : Effect.fail(new Error(`Volume with name or ID ${name} not found.`))
85 ),
86 Effect.catchAll((error) =>
87 Effect.sync(() => {
88 console.error(`An error occurred: ${error}`);
89 Deno.exit(1);
90 })
91 ),
92 ),
93 );
94}