Files
dokploy/packages/server/src/services/overview-shared.ts
T

184 lines
4.8 KiB
TypeScript

// No db/schema imports here — client components import this file directly, and dockerode/ssh2 (child_process) would break the browser bundle.
export type OverviewServiceType =
| "application"
| "postgres"
| "mysql"
| "mariadb"
| "mongo"
| "redis"
| "compose"
| "libsql";
export interface OverviewService {
id: string;
type: OverviewServiceType;
name: string;
appName: string;
status: string | null;
createdAt: string;
serverId: string | null;
serverName: string | null;
icon: string | null;
projectId: string;
projectName: string;
environmentId: string;
environmentName: string;
lastDeployAt: string | null;
}
export type OverviewSortBy =
| "name-asc"
| "name-desc"
| "type-asc"
| "type-desc"
| "createdAt-asc"
| "createdAt-desc"
| "lastDeploy-asc"
| "lastDeploy-desc";
export const sortOverviewServices = (
services: OverviewService[],
sortBy: OverviewSortBy,
): OverviewService[] => {
const [field, direction] = sortBy.split("-") as [string, "asc" | "desc"];
return [...services].sort((a, b) => {
if (field === "name") {
const cmp = a.name.localeCompare(b.name);
return direction === "asc" ? cmp : -cmp;
}
if (field === "type") {
const cmp = a.type.localeCompare(b.type);
return direction === "asc" ? cmp : -cmp;
}
if (field === "createdAt") {
const cmp =
new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime();
return direction === "asc" ? cmp : -cmp;
}
// lastDeploy: services without a deploy always sort to the end.
const aDate = a.lastDeployAt ? new Date(a.lastDeployAt).getTime() : null;
const bDate = b.lastDeployAt ? new Date(b.lastDeployAt).getTime() : null;
if (aDate === null && bDate === null) return 0;
if (aDate === null) return 1;
if (bDate === null) return -1;
return direction === "desc" ? bDate - aDate : aDate - bDate;
});
};
export const DB_ENGINE_ICON_TYPES = new Set([
"postgres",
"mariadb",
"mysql",
"mongo",
"redis",
"libsql",
]);
export const getServiceOverviewIcon = (service: {
type: OverviewServiceType;
icon: string | null;
}):
| { kind: "db"; engine: string }
| { kind: "custom"; url: string }
| { kind: "generic"; type: "application" | "compose" } => {
if (DB_ENGINE_ICON_TYPES.has(service.type)) {
return { kind: "db", engine: service.type };
}
if (service.icon) {
return { kind: "custom", url: service.icon };
}
return { kind: "generic", type: service.type as "application" | "compose" };
};
export type OverviewBackupKind = "backup" | "volumeBackup";
export interface OverviewBackup {
deploymentId: string;
kind: OverviewBackupKind;
status: string | null;
createdAt: string;
destinationId: string;
destinationName: string;
databaseType: string | null;
serviceType: string | null;
backupType: "database" | "compose" | null;
serviceName: string;
serviceOwnerId: string | null;
serviceOwnerType: OverviewServiceType | "web-server";
serverId: string | null;
projectId: string | null;
environmentId: string | null;
}
export type OverviewDomainOwnerType = "application" | "compose";
export interface OverviewDomain {
domainId: string;
host: string;
path: string | null;
port: number | null;
customEntrypoint: string | null;
https: boolean;
certificateType: "letsencrypt" | "none" | "custom";
createdAt: string;
enabled: boolean;
domainType: "application" | "compose" | "preview" | null;
serviceOwnerId: string;
serviceOwnerType: OverviewDomainOwnerType;
serviceName: string;
projectId: string;
projectName: string;
environmentId: string;
environmentName: string;
}
export type OverviewDomainSortBy =
| "createdAt-asc"
| "createdAt-desc"
| "port-asc"
| "port-desc";
export const sortOverviewDomains = (
domains: OverviewDomain[],
sortBy: OverviewDomainSortBy,
): OverviewDomain[] => {
const [field, direction] = sortBy.split("-") as [string, "asc" | "desc"];
return [...domains].sort((a, b) => {
if (field === "port") {
// Domains without a port sort to the end regardless of direction.
const aPort = a.port;
const bPort = b.port;
if (aPort === null && bPort === null) return 0;
if (aPort === null) return 1;
if (bPort === null) return -1;
return direction === "asc" ? aPort - bPort : bPort - aPort;
}
const cmp =
new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime();
return direction === "asc" ? cmp : -cmp;
});
};
export const getBackupOverviewIcon = (
row: Pick<
OverviewBackup,
"databaseType" | "serviceType" | "serviceOwnerType"
>,
):
| { kind: "db"; engine: string }
| { kind: "generic"; type: "application" | "compose" }
| { kind: "webServer" } => {
const engine = row.databaseType ?? row.serviceType;
if (engine === "web-server") {
return { kind: "webServer" };
}
if (engine && DB_ENGINE_ICON_TYPES.has(engine)) {
return { kind: "db", engine };
}
if (row.serviceOwnerType === "compose") {
return { kind: "generic", type: "compose" };
}
return { kind: "generic", type: "application" };
};