mirror of
https://github.com/nicotsx/ironmount.git
synced 2025-12-10 12:10:51 +01:00
* refactor: use short ids to allow changing the name of volumes & repos * refactor: address PR feedbacks * fix: make short_id non null after initial population
376 lines
7.6 KiB
TypeScript
376 lines
7.6 KiB
TypeScript
import { type } from "arktype";
|
|
import { describeRoute, resolver } from "hono-openapi";
|
|
import { BACKEND_STATUS, BACKEND_TYPES, volumeConfigSchema } from "~/schemas/volumes";
|
|
|
|
export const volumeSchema = type({
|
|
id: "number",
|
|
shortId: "string",
|
|
name: "string",
|
|
type: type.valueOf(BACKEND_TYPES),
|
|
status: type.valueOf(BACKEND_STATUS),
|
|
lastError: "string | null",
|
|
createdAt: "number",
|
|
updatedAt: "number",
|
|
lastHealthCheck: "number",
|
|
config: volumeConfigSchema,
|
|
autoRemount: "boolean",
|
|
});
|
|
|
|
export type VolumeDto = typeof volumeSchema.infer;
|
|
|
|
/**
|
|
* List all volumes
|
|
*/
|
|
export const listVolumesResponse = volumeSchema.array();
|
|
export type ListVolumesDto = typeof listVolumesResponse.infer;
|
|
|
|
export const listVolumesDto = describeRoute({
|
|
description: "List all volumes",
|
|
tags: ["Volumes"],
|
|
operationId: "listVolumes",
|
|
responses: {
|
|
200: {
|
|
description: "A list of volumes",
|
|
content: {
|
|
"application/json": {
|
|
schema: resolver(listVolumesResponse),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
/**
|
|
* Create a new volume
|
|
*/
|
|
export const createVolumeBody = type({
|
|
name: "string",
|
|
config: volumeConfigSchema,
|
|
});
|
|
|
|
export const createVolumeResponse = volumeSchema;
|
|
export type CreateVolumeDto = typeof createVolumeResponse.infer;
|
|
|
|
export const createVolumeDto = describeRoute({
|
|
description: "Create a new volume",
|
|
operationId: "createVolume",
|
|
tags: ["Volumes"],
|
|
responses: {
|
|
201: {
|
|
description: "Volume created successfully",
|
|
content: {
|
|
"application/json": {
|
|
schema: resolver(createVolumeResponse),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
/**
|
|
* Delete a volume
|
|
*/
|
|
export const deleteVolumeResponse = type({
|
|
message: "string",
|
|
});
|
|
export type DeleteVolumeDto = typeof deleteVolumeResponse.infer;
|
|
|
|
export const deleteVolumeDto = describeRoute({
|
|
description: "Delete a volume",
|
|
operationId: "deleteVolume",
|
|
tags: ["Volumes"],
|
|
responses: {
|
|
200: {
|
|
description: "Volume deleted successfully",
|
|
content: {
|
|
"application/json": {
|
|
schema: resolver(deleteVolumeResponse),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
const statfsSchema = type({
|
|
total: "number",
|
|
used: "number",
|
|
free: "number",
|
|
});
|
|
|
|
const getVolumeResponse = type({
|
|
volume: volumeSchema,
|
|
statfs: statfsSchema,
|
|
});
|
|
|
|
export type GetVolumeDto = typeof getVolumeResponse.infer;
|
|
/**
|
|
* Get a volume
|
|
*/
|
|
export const getVolumeDto = describeRoute({
|
|
description: "Get a volume by name",
|
|
operationId: "getVolume",
|
|
tags: ["Volumes"],
|
|
responses: {
|
|
200: {
|
|
description: "Volume details",
|
|
content: {
|
|
"application/json": {
|
|
schema: resolver(getVolumeResponse),
|
|
},
|
|
},
|
|
},
|
|
404: {
|
|
description: "Volume not found",
|
|
},
|
|
},
|
|
});
|
|
|
|
/**
|
|
* Update a volume
|
|
*/
|
|
export const updateVolumeBody = type({
|
|
name: "string?",
|
|
autoRemount: "boolean?",
|
|
config: volumeConfigSchema.optional(),
|
|
});
|
|
|
|
export type UpdateVolumeBody = typeof updateVolumeBody.infer;
|
|
|
|
export const updateVolumeResponse = volumeSchema;
|
|
export type UpdateVolumeDto = typeof updateVolumeResponse.infer;
|
|
|
|
export const updateVolumeDto = describeRoute({
|
|
description: "Update a volume's configuration",
|
|
operationId: "updateVolume",
|
|
tags: ["Volumes"],
|
|
responses: {
|
|
200: {
|
|
description: "Volume updated successfully",
|
|
content: {
|
|
"application/json": {
|
|
schema: resolver(updateVolumeResponse),
|
|
},
|
|
},
|
|
},
|
|
404: {
|
|
description: "Volume not found",
|
|
},
|
|
},
|
|
});
|
|
|
|
/**
|
|
* Test connection
|
|
*/
|
|
export const testConnectionBody = type({
|
|
config: volumeConfigSchema,
|
|
});
|
|
|
|
export const testConnectionResponse = type({
|
|
success: "boolean",
|
|
message: "string",
|
|
});
|
|
export type TestConnectionDto = typeof testConnectionResponse.infer;
|
|
|
|
export const testConnectionDto = describeRoute({
|
|
description: "Test connection to backend",
|
|
operationId: "testConnection",
|
|
tags: ["Volumes"],
|
|
responses: {
|
|
200: {
|
|
description: "Connection test result",
|
|
content: {
|
|
"application/json": {
|
|
schema: resolver(testConnectionResponse),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
/**
|
|
* Mount volume
|
|
*/
|
|
export const mountVolumeResponse = type({
|
|
error: "string?",
|
|
status: type.valueOf(BACKEND_STATUS),
|
|
});
|
|
export type MountVolumeDto = typeof mountVolumeResponse.infer;
|
|
|
|
export const mountVolumeDto = describeRoute({
|
|
description: "Mount a volume",
|
|
operationId: "mountVolume",
|
|
tags: ["Volumes"],
|
|
responses: {
|
|
200: {
|
|
description: "Volume mounted successfully",
|
|
content: {
|
|
"application/json": {
|
|
schema: resolver(mountVolumeResponse),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
/**
|
|
* Unmount volume
|
|
*/
|
|
export const unmountVolumeResponse = type({
|
|
error: "string?",
|
|
status: type.valueOf(BACKEND_STATUS),
|
|
});
|
|
export type UnmountVolumeDto = typeof unmountVolumeResponse.infer;
|
|
|
|
export const unmountVolumeDto = describeRoute({
|
|
description: "Unmount a volume",
|
|
operationId: "unmountVolume",
|
|
tags: ["Volumes"],
|
|
responses: {
|
|
200: {
|
|
description: "Volume unmounted successfully",
|
|
content: {
|
|
"application/json": {
|
|
schema: resolver(unmountVolumeResponse),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
export const healthCheckResponse = type({
|
|
error: "string?",
|
|
status: type.valueOf(BACKEND_STATUS),
|
|
});
|
|
export type HealthCheckDto = typeof healthCheckResponse.infer;
|
|
|
|
export const healthCheckDto = describeRoute({
|
|
description: "Perform a health check on a volume",
|
|
operationId: "healthCheckVolume",
|
|
tags: ["Volumes"],
|
|
responses: {
|
|
200: {
|
|
description: "Volume health check result",
|
|
content: {
|
|
"application/json": {
|
|
schema: resolver(healthCheckResponse),
|
|
},
|
|
},
|
|
},
|
|
404: {
|
|
description: "Volume not found",
|
|
},
|
|
},
|
|
});
|
|
|
|
/**
|
|
* Get containers using a volume
|
|
*/
|
|
const containerSchema = type({
|
|
id: "string",
|
|
name: "string",
|
|
state: "string",
|
|
image: "string",
|
|
});
|
|
|
|
export const listContainersResponse = containerSchema.array();
|
|
export type ListContainersDto = typeof listContainersResponse.infer;
|
|
|
|
export const getContainersDto = describeRoute({
|
|
description: "Get containers using a volume by name",
|
|
operationId: "getContainersUsingVolume",
|
|
tags: ["Volumes"],
|
|
responses: {
|
|
200: {
|
|
description: "List of containers using the volume",
|
|
content: {
|
|
"application/json": {
|
|
schema: resolver(listContainersResponse),
|
|
},
|
|
},
|
|
},
|
|
404: {
|
|
description: "Volume not found",
|
|
},
|
|
},
|
|
});
|
|
|
|
/**
|
|
* List files in a volume
|
|
*/
|
|
const fileEntrySchema = type({
|
|
name: "string",
|
|
path: "string",
|
|
type: type.enumerated("file", "directory"),
|
|
size: "number?",
|
|
modifiedAt: "number?",
|
|
});
|
|
|
|
export const listFilesResponse = type({
|
|
files: fileEntrySchema.array(),
|
|
path: "string",
|
|
});
|
|
export type ListFilesDto = typeof listFilesResponse.infer;
|
|
|
|
export const listFilesDto = describeRoute({
|
|
description: "List files in a volume directory",
|
|
operationId: "listFiles",
|
|
tags: ["Volumes"],
|
|
parameters: [
|
|
{
|
|
in: "query",
|
|
name: "path",
|
|
required: false,
|
|
schema: {
|
|
type: "string",
|
|
},
|
|
description: "Subdirectory path to list (relative to volume root)",
|
|
},
|
|
],
|
|
responses: {
|
|
200: {
|
|
description: "List of files in the volume",
|
|
content: {
|
|
"application/json": {
|
|
schema: resolver(listFilesResponse),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
/**
|
|
* Browse filesystem directories
|
|
*/
|
|
export const browseFilesystemResponse = type({
|
|
directories: fileEntrySchema.array(),
|
|
path: "string",
|
|
});
|
|
export type BrowseFilesystemDto = typeof browseFilesystemResponse.infer;
|
|
|
|
export const browseFilesystemDto = describeRoute({
|
|
description: "Browse directories on the host filesystem",
|
|
operationId: "browseFilesystem",
|
|
tags: ["Volumes"],
|
|
parameters: [
|
|
{
|
|
in: "query",
|
|
name: "path",
|
|
required: false,
|
|
schema: {
|
|
type: "string",
|
|
},
|
|
description: "Directory path to browse (absolute path, defaults to /)",
|
|
},
|
|
],
|
|
responses: {
|
|
200: {
|
|
description: "List of directories in the specified path",
|
|
content: {
|
|
"application/json": {
|
|
schema: resolver(browseFilesystemResponse),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
});
|