fix: further typos

This commit is contained in:
Tam Nguyen
2026-03-30 08:59:14 +11:00
parent 290a03ccfb
commit 815b8136fa
37 changed files with 747 additions and 747 deletions

View File

@@ -292,7 +292,7 @@ networks:
dokploy-network:
`;
test("It shoudn't add suffix to dokploy-network", () => {
test("It shouldn't add suffix to dokploy-network", () => {
const composeData = parse(composeFile7) as ComposeSpecification;
const suffix = generateRandomHash();

View File

@@ -1,7 +1,7 @@
import type { ComposeSpecification } from "@dokploy/server";
import {
addSuffixToServiceNetworks,
generateRandomHash,
addSuffixToServiceNetworks,
generateRandomHash,
} from "@dokploy/server";
import { expect, test } from "vitest";
import { parse } from "yaml";
@@ -23,30 +23,30 @@ services:
`;
test("Add suffix to networks in services", () => {
const composeData = parse(composeFile) as ComposeSpecification;
const composeData = parse(composeFile) as ComposeSpecification;
const suffix = generateRandomHash();
const suffix = generateRandomHash();
if (!composeData?.services) {
return;
}
const services = addSuffixToServiceNetworks(composeData.services, suffix);
const actualComposeData = { ...composeData, services };
if (!composeData?.services) {
return;
}
const services = addSuffixToServiceNetworks(composeData.services, suffix);
const actualComposeData = { ...composeData, services };
expect(actualComposeData?.services?.web?.networks).toContain(
`frontend-${suffix}`,
);
expect(actualComposeData?.services?.web?.networks).toContain(
`frontend-${suffix}`,
);
expect(actualComposeData?.services?.api?.networks).toContain(
`backend-${suffix}`,
);
expect(actualComposeData?.services?.api?.networks).toContain(
`backend-${suffix}`,
);
const apiNetworks = actualComposeData?.services?.api?.networks;
const apiNetworks = actualComposeData?.services?.api?.networks;
expect(apiNetworks).toBeDefined();
expect(actualComposeData?.services?.api?.networks).toContain(
`backend-${suffix}`,
);
expect(apiNetworks).toBeDefined();
expect(actualComposeData?.services?.api?.networks).toContain(
`backend-${suffix}`,
);
});
// Caso 2: Objeto con aliases
@@ -67,29 +67,29 @@ networks:
`;
test("Add suffix to networks in services with aliases", () => {
const composeData = parse(composeFile2) as ComposeSpecification;
const composeData = parse(composeFile2) as ComposeSpecification;
const suffix = generateRandomHash();
const suffix = generateRandomHash();
if (!composeData?.services) {
return;
}
const services = addSuffixToServiceNetworks(composeData.services, suffix);
const actualComposeData = { ...composeData, services };
if (!composeData?.services) {
return;
}
const services = addSuffixToServiceNetworks(composeData.services, suffix);
const actualComposeData = { ...composeData, services };
expect(actualComposeData.services?.api?.networks).toHaveProperty(
`frontend-${suffix}`,
);
expect(actualComposeData.services?.api?.networks).toHaveProperty(
`frontend-${suffix}`,
);
const networkConfig = actualComposeData?.services?.api?.networks as {
[key: string]: { aliases?: string[] };
};
expect(networkConfig[`frontend-${suffix}`]).toBeDefined();
expect(networkConfig[`frontend-${suffix}`]?.aliases).toContain("api");
const networkConfig = actualComposeData?.services?.api?.networks as {
[key: string]: { aliases?: string[] };
};
expect(networkConfig[`frontend-${suffix}`]).toBeDefined();
expect(networkConfig[`frontend-${suffix}`]?.aliases).toContain("api");
expect(actualComposeData.services?.api?.networks).not.toHaveProperty(
"frontend-ash",
);
expect(actualComposeData.services?.api?.networks).not.toHaveProperty(
"frontend-ash",
);
});
const composeFile3 = `
@@ -107,19 +107,19 @@ networks:
`;
test("Add suffix to networks in services (Object with simple networks)", () => {
const composeData = parse(composeFile3) as ComposeSpecification;
const composeData = parse(composeFile3) as ComposeSpecification;
const suffix = generateRandomHash();
const suffix = generateRandomHash();
if (!composeData?.services) {
return;
}
const services = addSuffixToServiceNetworks(composeData.services, suffix);
const actualComposeData = { ...composeData, services };
if (!composeData?.services) {
return;
}
const services = addSuffixToServiceNetworks(composeData.services, suffix);
const actualComposeData = { ...composeData, services };
expect(actualComposeData.services?.redis?.networks).toHaveProperty(
`backend-${suffix}`,
);
expect(actualComposeData.services?.redis?.networks).toHaveProperty(
`backend-${suffix}`,
);
});
const composeFileCombined = `
@@ -153,36 +153,36 @@ networks:
`;
test("Add suffix to networks in services (combined case)", () => {
const composeData = parse(composeFileCombined) as ComposeSpecification;
const composeData = parse(composeFileCombined) as ComposeSpecification;
const suffix = generateRandomHash();
const suffix = generateRandomHash();
if (!composeData?.services) {
return;
}
const services = addSuffixToServiceNetworks(composeData.services, suffix);
const actualComposeData = { ...composeData, services };
if (!composeData?.services) {
return;
}
const services = addSuffixToServiceNetworks(composeData.services, suffix);
const actualComposeData = { ...composeData, services };
// Caso 1: ListOfStrings
expect(actualComposeData.services?.web?.networks).toContain(
`frontend-${suffix}`,
);
expect(actualComposeData.services?.web?.networks).toContain(
`backend-${suffix}`,
);
// Caso 1: ListOfStrings
expect(actualComposeData.services?.web?.networks).toContain(
`frontend-${suffix}`,
);
expect(actualComposeData.services?.web?.networks).toContain(
`backend-${suffix}`,
);
// Caso 2: Objeto con aliases
const apiNetworks = actualComposeData.services?.api?.networks as {
[key: string]: unknown;
};
expect(apiNetworks).toHaveProperty(`frontend-${suffix}`);
expect(apiNetworks[`frontend-${suffix}`]).toBeDefined();
expect(apiNetworks).not.toHaveProperty("frontend");
// Caso 2: Objeto con aliases
const apiNetworks = actualComposeData.services?.api?.networks as {
[key: string]: unknown;
};
expect(apiNetworks).toHaveProperty(`frontend-${suffix}`);
expect(apiNetworks[`frontend-${suffix}`]).toBeDefined();
expect(apiNetworks).not.toHaveProperty("frontend");
// Caso 3: Objeto con redes simples
const redisNetworks = actualComposeData.services?.redis?.networks;
expect(redisNetworks).toHaveProperty(`backend-${suffix}`);
expect(redisNetworks).not.toHaveProperty("backend");
// Caso 3: Objeto con redes simples
const redisNetworks = actualComposeData.services?.redis?.networks;
expect(redisNetworks).toHaveProperty(`backend-${suffix}`);
expect(redisNetworks).not.toHaveProperty("backend");
});
const composeFile7 = `
@@ -195,19 +195,19 @@ services:
- dokploy-network
`;
test("It shoudn't add suffix to dokploy-network in services", () => {
const composeData = parse(composeFile7) as ComposeSpecification;
test("It shouldn't add suffix to dokploy-network in services", () => {
const composeData = parse(composeFile7) as ComposeSpecification;
const suffix = generateRandomHash();
const suffix = generateRandomHash();
if (!composeData?.services) {
return;
}
const networks = addSuffixToServiceNetworks(composeData.services, suffix);
const service = networks.web;
if (!composeData?.services) {
return;
}
const networks = addSuffixToServiceNetworks(composeData.services, suffix);
const service = networks.web;
expect(service).toBeDefined();
expect(service?.networks).toContain("dokploy-network");
expect(service).toBeDefined();
expect(service?.networks).toContain("dokploy-network");
});
const composeFile8 = `
@@ -241,35 +241,35 @@ services:
dokploy-network:
aliases:
- apid
`;
test("It shoudn't add suffix to dokploy-network in services multiples cases", () => {
const composeData = parse(composeFile8) as ComposeSpecification;
test("It shouldn't add suffix to dokploy-network in services multiples cases", () => {
const composeData = parse(composeFile8) as ComposeSpecification;
const suffix = generateRandomHash();
const suffix = generateRandomHash();
if (!composeData?.services) {
return;
}
const networks = addSuffixToServiceNetworks(composeData.services, suffix);
const service = networks.web;
const api = networks.api;
const redis = networks.redis;
const db = networks.db;
if (!composeData?.services) {
return;
}
const networks = addSuffixToServiceNetworks(composeData.services, suffix);
const service = networks.web;
const api = networks.api;
const redis = networks.redis;
const db = networks.db;
const dbNetworks = db?.networks as {
[key: string]: unknown;
};
const dbNetworks = db?.networks as {
[key: string]: unknown;
};
const apiNetworks = api?.networks as {
[key: string]: unknown;
};
const apiNetworks = api?.networks as {
[key: string]: unknown;
};
expect(service).toBeDefined();
expect(service?.networks).toContain("dokploy-network");
expect(service).toBeDefined();
expect(service?.networks).toContain("dokploy-network");
expect(redis?.networks).toHaveProperty("dokploy-network");
expect(dbNetworks["dokploy-network"]).toBeDefined();
expect(apiNetworks["dokploy-network"]).toBeDefined();
expect(redis?.networks).toHaveProperty("dokploy-network");
expect(dbNetworks["dokploy-network"]).toBeDefined();
expect(apiNetworks["dokploy-network"]).toBeDefined();
});

View File

@@ -1,4 +1,4 @@
import { getEnviromentVariablesObject } from "@dokploy/server/index";
import { getEnvironmentVariablesObject } from "@dokploy/server/index";
import { describe, expect, it } from "vitest";
const projectEnv = `
@@ -15,29 +15,29 @@ DATABASE_NAME=dev_database
SECRET_KEY=env-secret-123
`;
describe("getEnviromentVariablesObject with environment variables (Stack compose)", () => {
it("resolves environment variables correctly for Stack compose", () => {
const serviceEnv = `
describe("getEnvironmentVariablesObject with environment variables (Stack compose)", () => {
it("resolves environment variables correctly for Stack compose", () => {
const serviceEnv = `
FOO=\${{environment.NODE_ENV}}
BAR=\${{environment.API_URL}}
BAZ=test
`;
const result = getEnviromentVariablesObject(
serviceEnv,
projectEnv,
environmentEnv,
);
const result = getEnvironmentVariablesObject(
serviceEnv,
projectEnv,
environmentEnv,
);
expect(result).toEqual({
FOO: "development",
BAR: "https://api.dev.example.com",
BAZ: "test",
});
});
expect(result).toEqual({
FOO: "development",
BAR: "https://api.dev.example.com",
BAZ: "test",
});
});
it("resolves both project and environment variables for Stack compose", () => {
const serviceEnv = `
it("resolves both project and environment variables for Stack compose", () => {
const serviceEnv = `
ENVIRONMENT=\${{project.ENVIRONMENT}}
NODE_ENV=\${{environment.NODE_ENV}}
API_URL=\${{environment.API_URL}}
@@ -45,140 +45,140 @@ DATABASE_URL=\${{project.DATABASE_URL}}
SERVICE_PORT=4000
`;
const result = getEnviromentVariablesObject(
serviceEnv,
projectEnv,
environmentEnv,
);
const result = getEnvironmentVariablesObject(
serviceEnv,
projectEnv,
environmentEnv,
);
expect(result).toEqual({
ENVIRONMENT: "staging",
NODE_ENV: "development",
API_URL: "https://api.dev.example.com",
DATABASE_URL: "postgres://postgres:postgres@localhost:5432/project_db",
SERVICE_PORT: "4000",
});
});
expect(result).toEqual({
ENVIRONMENT: "staging",
NODE_ENV: "development",
API_URL: "https://api.dev.example.com",
DATABASE_URL: "postgres://postgres:postgres@localhost:5432/project_db",
SERVICE_PORT: "4000",
});
});
it("handles multiple environment references in single value for Stack compose", () => {
const multiRefEnv = `
it("handles multiple environment references in single value for Stack compose", () => {
const multiRefEnv = `
HOST=localhost
PORT=5432
USERNAME=postgres
PASSWORD=secret123
`;
const serviceEnv = `
const serviceEnv = `
DATABASE_URL=postgresql://\${{environment.USERNAME}}:\${{environment.PASSWORD}}@\${{environment.HOST}}:\${{environment.PORT}}/mydb
`;
const result = getEnviromentVariablesObject(serviceEnv, "", multiRefEnv);
const result = getEnvironmentVariablesObject(serviceEnv, "", multiRefEnv);
expect(result).toEqual({
DATABASE_URL: "postgresql://postgres:secret123@localhost:5432/mydb",
});
});
expect(result).toEqual({
DATABASE_URL: "postgresql://postgres:secret123@localhost:5432/mydb",
});
});
it("throws error for undefined environment variables in Stack compose", () => {
const serviceWithUndefined = `
it("throws error for undefined environment variables in Stack compose", () => {
const serviceWithUndefined = `
UNDEFINED_VAR=\${{environment.UNDEFINED_VAR}}
`;
expect(() =>
getEnviromentVariablesObject(serviceWithUndefined, "", environmentEnv),
).toThrow("Invalid environment variable: environment.UNDEFINED_VAR");
});
expect(() =>
getEnvironmentVariablesObject(serviceWithUndefined, "", environmentEnv),
).toThrow("Invalid environment variable: environment.UNDEFINED_VAR");
});
it("allows service variables to override environment variables in Stack compose", () => {
const serviceOverrideEnv = `
it("allows service variables to override environment variables in Stack compose", () => {
const serviceOverrideEnv = `
NODE_ENV=production
API_URL=\${{environment.API_URL}}
`;
const result = getEnviromentVariablesObject(
serviceOverrideEnv,
"",
environmentEnv,
);
const result = getEnvironmentVariablesObject(
serviceOverrideEnv,
"",
environmentEnv,
);
expect(result).toEqual({
NODE_ENV: "production",
API_URL: "https://api.dev.example.com",
});
});
expect(result).toEqual({
NODE_ENV: "production",
API_URL: "https://api.dev.example.com",
});
});
it("resolves complex references with project, environment, and service variables for Stack compose", () => {
const complexServiceEnv = `
it("resolves complex references with project, environment, and service variables for Stack compose", () => {
const complexServiceEnv = `
FULL_DATABASE_URL=\${{project.DATABASE_URL}}/\${{environment.DATABASE_NAME}}
API_ENDPOINT=\${{environment.API_URL}}/\${{project.ENVIRONMENT}}/api
SERVICE_NAME=my-service
COMPLEX_VAR=\${{SERVICE_NAME}}-\${{environment.NODE_ENV}}-\${{project.ENVIRONMENT}}
`;
const result = getEnviromentVariablesObject(
complexServiceEnv,
projectEnv,
environmentEnv,
);
const result = getEnvironmentVariablesObject(
complexServiceEnv,
projectEnv,
environmentEnv,
);
expect(result).toEqual({
FULL_DATABASE_URL:
"postgres://postgres:postgres@localhost:5432/project_db/dev_database",
API_ENDPOINT: "https://api.dev.example.com/staging/api",
SERVICE_NAME: "my-service",
COMPLEX_VAR: "my-service-development-staging",
});
});
expect(result).toEqual({
FULL_DATABASE_URL:
"postgres://postgres:postgres@localhost:5432/project_db/dev_database",
API_ENDPOINT: "https://api.dev.example.com/staging/api",
SERVICE_NAME: "my-service",
COMPLEX_VAR: "my-service-development-staging",
});
});
it("maintains precedence: service > environment > project in Stack compose", () => {
const conflictingProjectEnv = `
it("maintains precedence: service > environment > project in Stack compose", () => {
const conflictingProjectEnv = `
NODE_ENV=production-project
API_URL=https://project.api.com
DATABASE_NAME=project_db
`;
const conflictingEnvironmentEnv = `
const conflictingEnvironmentEnv = `
NODE_ENV=development-environment
API_URL=https://environment.api.com
DATABASE_NAME=env_db
`;
const serviceWithConflicts = `
const serviceWithConflicts = `
NODE_ENV=service-override
PROJECT_ENV=\${{project.NODE_ENV}}
ENV_VAR=\${{environment.API_URL}}
DB_NAME=\${{environment.DATABASE_NAME}}
`;
const result = getEnviromentVariablesObject(
serviceWithConflicts,
conflictingProjectEnv,
conflictingEnvironmentEnv,
);
const result = getEnvironmentVariablesObject(
serviceWithConflicts,
conflictingProjectEnv,
conflictingEnvironmentEnv,
);
expect(result).toEqual({
NODE_ENV: "service-override",
PROJECT_ENV: "production-project",
ENV_VAR: "https://environment.api.com",
DB_NAME: "env_db",
});
});
expect(result).toEqual({
NODE_ENV: "service-override",
PROJECT_ENV: "production-project",
ENV_VAR: "https://environment.api.com",
DB_NAME: "env_db",
});
});
it("handles empty environment variables in Stack compose", () => {
const serviceWithEmpty = `
it("handles empty environment variables in Stack compose", () => {
const serviceWithEmpty = `
SERVICE_VAR=test
PROJECT_VAR=\${{project.ENVIRONMENT}}
`;
const result = getEnviromentVariablesObject(
serviceWithEmpty,
projectEnv,
"",
);
const result = getEnvironmentVariablesObject(
serviceWithEmpty,
projectEnv,
"",
);
expect(result).toEqual({
SERVICE_VAR: "test",
PROJECT_VAR: "staging",
});
});
expect(result).toEqual({
SERVICE_VAR: "test",
PROJECT_VAR: "staging",
});
});
});

View File

@@ -149,7 +149,7 @@ export const HandleRedirect = ({
const onDialogToggle = (open: boolean) => {
setIsOpen(open);
// commented for the moment because not reseting the form if accidentally closed the dialog can be considered as a feature instead of a bug
// commented for the moment because not resetting the form if accidentally closed the dialog can be considered as a feature instead of a bug
// setPresetSelected("");
// form.reset();
};

View File

@@ -91,7 +91,7 @@ export const ShowDockerLogs = ({ appName, serverId }: Props) => {
}, [option, services, containers]);
const isLoading = option === "native" ? containersLoading : servicesLoading;
const containersLenght =
const containersLength =
option === "native" ? containers?.length : services?.length;
return (
@@ -167,7 +167,7 @@ export const ShowDockerLogs = ({ appName, serverId }: Props) => {
</>
)}
<SelectLabel>Containers ({containersLenght})</SelectLabel>
<SelectLabel>Containers ({containersLength})</SelectLabel>
</SelectGroup>
</SelectContent>
</Select>

View File

@@ -77,7 +77,7 @@ export const ShowDockerLogsStack = ({ appName, serverId }: Props) => {
}, [option, services, containers]);
const isLoading = option === "native" ? containersLoading : servicesLoading;
const containersLenght =
const containersLength =
option === "native" ? containers?.length : services?.length;
return (
@@ -152,7 +152,7 @@ export const ShowDockerLogsStack = ({ appName, serverId }: Props) => {
</>
)}
<SelectLabel>Containers ({containersLenght})</SelectLabel>
<SelectLabel>Containers ({containersLength})</SelectLabel>
</SelectGroup>
</SelectContent>
</Select>

View File

@@ -225,7 +225,7 @@ export const RestoreBackup = ({
resolver: zodResolver(RestoreBackupSchema),
});
const destionationId = form.watch("destinationId");
const destinationId = form.watch("destinationId");
const currentDatabaseType = form.watch("databaseType");
const metadata = form.watch("metadata");
@@ -240,12 +240,12 @@ export const RestoreBackup = ({
const { data: files = [], isPending } = api.backup.listBackupFiles.useQuery(
{
destinationId: destionationId,
destinationId: destinationId,
search: debouncedSearchTerm,
serverId: serverId ?? "",
},
{
enabled: isOpen && !!destionationId,
enabled: isOpen && !!destinationId,
},
);

View File

@@ -103,7 +103,7 @@ export function TerminalLine({ log, noTimestamp, searchTerm }: LogLineProps) {
>
{" "}
<div className="flex items-start gap-x-2">
{/* Icon to expand the log item maybe implement a colapsible later */}
{/* Icon to expand the log item maybe implement a collapsible later */}
{/* <Square className="size-4 text-muted-foreground opacity-0 group-hover/logitem:opacity-100 transition-opacity" /> */}
{tooltip(color, rawTimestamp)}
{!noTimestamp && (

View File

@@ -35,7 +35,7 @@ import {
TableRow,
} from "@/components/ui/table";
import { api, type RouterOutputs } from "@/utils/api";
import { columns } from "./colums";
import { columns } from "./columns";
export type Container = NonNullable<
RouterOutputs["docker"]["getContainers"]
>[0];

View File

@@ -161,7 +161,7 @@ export const SetupServer = ({ serverId, asButton = false }: Props) => {
<ul>
<li>
1. Add the public SSH Key when you create a server in your
preffered provider (Hostinger, Digital Ocean, Hetzner,
preferred provider (Hostinger, Digital Ocean, Hetzner,
etc){" "}
</li>
<li>2. Add The SSH Key to Server Manually</li>

View File

@@ -48,7 +48,7 @@ import { ShowMonitoringModal } from "./show-monitoring-modal";
import { ShowSchedulesModal } from "./show-schedules-modal";
import { ShowSwarmOverviewModal } from "./show-swarm-overview-modal";
import { ShowTraefikFileSystemModal } from "./show-traefik-file-system-modal";
import { WelcomeSuscription } from "./welcome-stripe/welcome-suscription";
import { Welcomesubscription } from "./welcome-stripe/welcome-subscription";
export const ShowServers = () => {
const router = useRouter();
@@ -63,7 +63,7 @@ export const ShowServers = () => {
return (
<div className="w-full">
{query?.success && isCloud && <WelcomeSuscription />}
{query?.success && isCloud && <Welcomesubscription />}
<Card className="h-full p-2.5 rounded-xl max-w-5xl mx-auto">
<div className="rounded-xl bg-background shadow-md ">
<CardHeader className="">

View File

@@ -51,7 +51,7 @@ export const { useStepper, steps, Scoped } = defineStepper(
{ id: "complete", title: "Complete", description: "Checkout complete" },
);
export const WelcomeSuscription = () => {
export const Welcomesubscription = () => {
const [showConfetti, setShowConfetti] = useState(false);
const stepper = useStepper();
const [isOpen, setIsOpen] = useState(true);

View File

@@ -19,7 +19,7 @@ interface TreeDataItem {
type TreeProps = React.HTMLAttributes<HTMLDivElement> & {
data: TreeDataItem[] | TreeDataItem;
initialSlelectedItemId?: string;
initialSelectedItemId?: string;
onSelectChange?: (item: TreeDataItem | undefined) => void;
expandAll?: boolean;
folderIcon?: LucideIcon;
@@ -30,7 +30,7 @@ const Tree = React.forwardRef<HTMLDivElement, TreeProps>(
(
{
data,
initialSlelectedItemId,
initialSelectedItemId,
onSelectChange,
expandAll,
folderIcon,
@@ -42,7 +42,7 @@ const Tree = React.forwardRef<HTMLDivElement, TreeProps>(
) => {
const [selectedItemId, setSelectedItemId] = React.useState<
string | undefined
>(initialSlelectedItemId);
>(initialSelectedItemId);
const handleSelectChange = React.useCallback(
(item: TreeDataItem | undefined) => {
@@ -55,7 +55,7 @@ const Tree = React.forwardRef<HTMLDivElement, TreeProps>(
);
const expandedItemIds = React.useMemo(() => {
if (!initialSlelectedItemId) {
if (!initialSelectedItemId) {
return [] as string[];
}
@@ -81,9 +81,9 @@ const Tree = React.forwardRef<HTMLDivElement, TreeProps>(
}
}
walkTreeItems(data, initialSlelectedItemId);
walkTreeItems(data, initialSelectedItemId);
return ids;
}, [data, initialSlelectedItemId]);
}, [data, initialSelectedItemId]);
const { ref: refRoot } = useResizeObserver();

View File

@@ -196,7 +196,7 @@ export default async function handler(
return;
}
const commitedPaths = await extractCommitedPaths(
const committedPaths = await extractCommittedPaths(
req.body,
application.bitbucket,
application.bitbucketRepositorySlug ||
@@ -206,7 +206,7 @@ export default async function handler(
const shouldDeployPaths = shouldDeploy(
application.watchPaths,
commitedPaths,
committedPaths,
);
if (!shouldDeployPaths) {
@@ -538,7 +538,7 @@ export const getProviderByHeader = (headers: any) => {
return null;
};
export const extractCommitedPaths = async (
export const extractCommittedPaths = async (
body: any,
bitbucket: Bitbucket | null,
repository: string,
@@ -548,7 +548,7 @@ export const extractCommitedPaths = async (
const commitHashes = changes
.map((change: any) => change.new?.target?.hash)
.filter(Boolean);
const commitedPaths: string[] = [];
const committedPaths: string[] = [];
const username =
bitbucket?.bitbucketWorkspaceName || bitbucket?.bitbucketUsername || "";
for (const commit of commitHashes) {
@@ -559,7 +559,7 @@ export const extractCommitedPaths = async (
});
const data = await response.json();
for (const value of data.values) {
if (value?.new?.path) commitedPaths.push(value.new.path);
if (value?.new?.path) committedPaths.push(value.new.path);
}
} catch (error) {
console.error(
@@ -571,5 +571,5 @@ export const extractCommitedPaths = async (
}
}
return commitedPaths;
return committedPaths;
};

View File

@@ -8,7 +8,7 @@ import { myQueue } from "@/server/queues/queueSetup";
import { deploy } from "@/server/utils/deploy";
import {
extractBranchName,
extractCommitedPaths,
extractCommittedPaths,
extractCommitMessage,
extractHash,
getProviderByHeader,
@@ -97,7 +97,7 @@ export default async function handler(
return;
}
const commitedPaths = await extractCommitedPaths(
const committedPaths = await extractCommittedPaths(
req.body,
composeResult.bitbucket,
composeResult.bitbucketRepositorySlug ||
@@ -107,7 +107,7 @@ export default async function handler(
const shouldDeployPaths = shouldDeploy(
composeResult.watchPaths,
commitedPaths,
committedPaths,
);
if (!shouldDeployPaths) {

View File

@@ -174,27 +174,27 @@ export default async function handler(
case "invoice.payment_succeeded": {
const newInvoice = event.data.object as Stripe.Invoice;
const suscription = await stripe.subscriptions.retrieve(
const subscription = await stripe.subscriptions.retrieve(
newInvoice.subscription as string,
);
if (suscription.status !== "active") {
if (subscription.status !== "active") {
console.log(
`Skipping invoice.payment_succeeded for subscription ${suscription.id} with status ${suscription.status}`,
`Skipping invoice.payment_succeeded for subscription ${subscription.id} with status ${subscription.status}`,
);
break;
}
const serversQuantity = getSubscriptionServersQuantity(
suscription?.items?.data ?? [],
subscription?.items?.data ?? [],
);
await db
.update(user)
.set({ serversQuantity })
.where(eq(user.stripeCustomerId, suscription.customer as string));
.where(eq(user.stripeCustomerId, subscription.customer as string));
const admin = await findUserByStripeCustomerId(
suscription.customer as string,
subscription.customer as string,
);
if (!admin) {

View File

@@ -16,7 +16,7 @@ import { ShowImport } from "@/components/dashboard/application/advanced/import/s
import { ShowVolumes } from "@/components/dashboard/application/advanced/volumes/show-volumes";
import { ShowDeployments } from "@/components/dashboard/application/deployments/show-deployments";
import { ShowDomains } from "@/components/dashboard/application/domains/show-domains";
import { ShowEnvironment } from "@/components/dashboard/application/environment/show-enviroment";
import { ShowEnvironment } from "@/components/dashboard/application/environment/show-environment";
import { ShowPatches } from "@/components/dashboard/application/patches/show-patches";
import { ShowSchedules } from "@/components/dashboard/application/schedules/show-schedules";
import { ShowVolumeBackups } from "@/components/dashboard/application/volume-backups/show-volume-backups";

View File

@@ -10,7 +10,7 @@ import Link from "next/link";
import { useRouter } from "next/router";
import { type ReactElement, useState } from "react";
import superjson from "superjson";
import { ShowEnvironment } from "@/components/dashboard/application/environment/show-enviroment";
import { ShowEnvironment } from "@/components/dashboard/application/environment/show-environment";
import { ShowDockerLogs } from "@/components/dashboard/application/logs/show";
import { DeleteService } from "@/components/dashboard/compose/delete-service";
import { ShowBackups } from "@/components/dashboard/database/backups/show-backups";

View File

@@ -10,7 +10,7 @@ import Link from "next/link";
import { useRouter } from "next/router";
import { type ReactElement, useState } from "react";
import superjson from "superjson";
import { ShowEnvironment } from "@/components/dashboard/application/environment/show-enviroment";
import { ShowEnvironment } from "@/components/dashboard/application/environment/show-environment";
import { ShowDockerLogs } from "@/components/dashboard/application/logs/show";
import { DeleteService } from "@/components/dashboard/compose/delete-service";
import { ShowBackups } from "@/components/dashboard/database/backups/show-backups";

View File

@@ -10,7 +10,7 @@ import Link from "next/link";
import { useRouter } from "next/router";
import { type ReactElement, useState } from "react";
import superjson from "superjson";
import { ShowEnvironment } from "@/components/dashboard/application/environment/show-enviroment";
import { ShowEnvironment } from "@/components/dashboard/application/environment/show-environment";
import { ShowDockerLogs } from "@/components/dashboard/application/logs/show";
import { DeleteService } from "@/components/dashboard/compose/delete-service";
import { ShowBackups } from "@/components/dashboard/database/backups/show-backups";

View File

@@ -10,7 +10,7 @@ import Link from "next/link";
import { useRouter } from "next/router";
import { type ReactElement, useState } from "react";
import superjson from "superjson";
import { ShowEnvironment } from "@/components/dashboard/application/environment/show-enviroment";
import { ShowEnvironment } from "@/components/dashboard/application/environment/show-environment";
import { ShowDockerLogs } from "@/components/dashboard/application/logs/show";
import { DeleteService } from "@/components/dashboard/compose/delete-service";
import { ShowBackups } from "@/components/dashboard/database/backups/show-backups";

View File

@@ -10,7 +10,7 @@ import Link from "next/link";
import { useRouter } from "next/router";
import { type ReactElement, useState } from "react";
import superjson from "superjson";
import { ShowEnvironment } from "@/components/dashboard/application/environment/show-enviroment";
import { ShowEnvironment } from "@/components/dashboard/application/environment/show-environment";
import { ShowDockerLogs } from "@/components/dashboard/application/logs/show";
import { DeleteService } from "@/components/dashboard/compose/delete-service";
import { ShowBackups } from "@/components/dashboard/database/backups/show-backups";

View File

@@ -10,7 +10,7 @@ import Link from "next/link";
import { useRouter } from "next/router";
import { type ReactElement, useState } from "react";
import superjson from "superjson";
import { ShowEnvironment } from "@/components/dashboard/application/environment/show-enviroment";
import { ShowEnvironment } from "@/components/dashboard/application/environment/show-environment";
import { ShowDockerLogs } from "@/components/dashboard/application/logs/show";
import { DeleteService } from "@/components/dashboard/compose/delete-service";
import { ContainerFreeMonitoring } from "@/components/dashboard/monitoring/free/container/show-free-container-monitoring";

View File

@@ -1,5 +1,5 @@
import {
createDestintation,
createDestination,
execAsync,
execAsyncRemote,
findDestinationById,
@@ -25,7 +25,7 @@ export const destinationRouter = createTRPCRouter({
.input(apiCreateDestination)
.mutation(async ({ input, ctx }) => {
try {
const result = await createDestintation(
const result = await createDestination(
input,
ctx.session.activeOrganizationId,
);

View File

@@ -121,7 +121,7 @@ export * from "./utils/providers/raw";
export * from "./utils/schedules/index";
export * from "./utils/schedules/utils";
export * from "./utils/servers/remote-docker";
export * from "./utils/startup/cancell-deployments";
export * from "./utils/startup/cancel-deployments";
export * from "./utils/tracking/hubspot";
export * from "./utils/traefik/application";
export * from "./utils/traefik/domain";

View File

@@ -177,7 +177,7 @@ export const createDeployment = async (
status: "error",
logPath: "",
description: deployment.description || "",
errorMessage: `An error have occured: ${error instanceof Error ? error.message : error}`,
errorMessage: `An error have occurred: ${error instanceof Error ? error.message : error}`,
startedAt: new Date().toISOString(),
finishedAt: new Date().toISOString(),
})
@@ -257,7 +257,7 @@ export const createDeploymentPreview = async (
status: "error",
logPath: "",
description: deployment.description || "",
errorMessage: `An error have occured: ${error instanceof Error ? error.message : error}`,
errorMessage: `An error have occurred: ${error instanceof Error ? error.message : error}`,
startedAt: new Date().toISOString(),
finishedAt: new Date().toISOString(),
})
@@ -334,7 +334,7 @@ echo "Initializing deployment\n" >> ${logFilePath};
status: "error",
logPath: "",
description: deployment.description || "",
errorMessage: `An error have occured: ${error instanceof Error ? error.message : error}`,
errorMessage: `An error have occurred: ${error instanceof Error ? error.message : error}`,
startedAt: new Date().toISOString(),
finishedAt: new Date().toISOString(),
})
@@ -418,7 +418,7 @@ echo "Initializing backup\n" >> ${logFilePath};
status: "error",
logPath: "",
description: deployment.description || "",
errorMessage: `An error have occured: ${error instanceof Error ? error.message : error}`,
errorMessage: `An error have occurred: ${error instanceof Error ? error.message : error}`,
startedAt: new Date().toISOString(),
finishedAt: new Date().toISOString(),
})
@@ -493,7 +493,7 @@ export const createDeploymentSchedule = async (
status: "error",
logPath: "",
description: deployment.description || "",
errorMessage: `An error have occured: ${error instanceof Error ? error.message : error}`,
errorMessage: `An error have occurred: ${error instanceof Error ? error.message : error}`,
startedAt: new Date().toISOString(),
finishedAt: new Date().toISOString(),
})
@@ -578,7 +578,7 @@ export const createDeploymentVolumeBackup = async (
status: "error",
logPath: "",
description: deployment.description || "",
errorMessage: `An error have occured: ${error instanceof Error ? error.message : error}`,
errorMessage: `An error have occurred: ${error instanceof Error ? error.message : error}`,
startedAt: new Date().toISOString(),
finishedAt: new Date().toISOString(),
})

View File

@@ -9,7 +9,7 @@ import type { z } from "zod";
export type Destination = typeof destinations.$inferSelect;
export const createDestintation = async (
export const createDestination = async (
input: z.infer<typeof apiCreateDestination>,
organizationId: string,
) => {

View File

@@ -383,12 +383,12 @@ export const readPorts = async (
const seenPorts = new Set<string>();
for (const key in parsedResult) {
if (Object.hasOwn(parsedResult, key)) {
const containerPortMapppings = parsedResult[key];
const containerPortMappings = parsedResult[key];
const protocol = key.split("/")[1];
const targetPort = Number.parseInt(key.split("/")[0] ?? "0", 10);
// Take only the first mapping to avoid duplicates (IPv4 and IPv6)
const firstMapping = containerPortMapppings[0];
const firstMapping = containerPortMappings[0];
if (firstMapping) {
const publishedPort = Number.parseInt(firstMapping.HostPort, 10);
const portKey = `${targetPort}-${publishedPort}-${protocol}`;

View File

@@ -7,452 +7,452 @@ import { auth } from "../lib/auth";
export type User = typeof user.$inferSelect;
export const addNewProject = async (
userId: string,
projectId: string,
organizationId: string,
userId: string,
projectId: string,
organizationId: string,
) => {
const userR = await findMemberById(userId, organizationId);
const userR = await findMemberById(userId, organizationId);
await db
.update(member)
.set({
accessedProjects: [...userR.accessedProjects, projectId],
})
.where(
and(eq(member.id, userR.id), eq(member.organizationId, organizationId)),
);
await db
.update(member)
.set({
accessedProjects: [...userR.accessedProjects, projectId],
})
.where(
and(eq(member.id, userR.id), eq(member.organizationId, organizationId)),
);
};
export const addNewEnvironment = async (
userId: string,
environmentId: string,
organizationId: string,
userId: string,
environmentId: string,
organizationId: string,
) => {
const userR = await findMemberById(userId, organizationId);
const userR = await findMemberById(userId, organizationId);
await db
.update(member)
.set({
accessedEnvironments: [...userR.accessedEnvironments, environmentId],
})
.where(
and(eq(member.id, userR.id), eq(member.organizationId, organizationId)),
);
await db
.update(member)
.set({
accessedEnvironments: [...userR.accessedEnvironments, environmentId],
})
.where(
and(eq(member.id, userR.id), eq(member.organizationId, organizationId)),
);
};
export const addNewService = async (
userId: string,
serviceId: string,
organizationId: string,
userId: string,
serviceId: string,
organizationId: string,
) => {
const userR = await findMemberById(userId, organizationId);
await db
.update(member)
.set({
accessedServices: [...userR.accessedServices, serviceId],
})
.where(
and(eq(member.id, userR.id), eq(member.organizationId, organizationId)),
);
const userR = await findMemberById(userId, organizationId);
await db
.update(member)
.set({
accessedServices: [...userR.accessedServices, serviceId],
})
.where(
and(eq(member.id, userR.id), eq(member.organizationId, organizationId)),
);
};
export const canPerformCreationService = async (
userId: string,
projectId: string,
organizationId: string,
userId: string,
projectId: string,
organizationId: string,
) => {
const { accessedProjects, canCreateServices } = await findMemberById(
userId,
organizationId,
);
const haveAccessToProject = accessedProjects.includes(projectId);
const { accessedProjects, canCreateServices } = await findMemberById(
userId,
organizationId,
);
const haveAccessToProject = accessedProjects.includes(projectId);
if (canCreateServices && haveAccessToProject) {
return true;
}
if (canCreateServices && haveAccessToProject) {
return true;
}
return false;
return false;
};
export const canPerformAccessService = async (
userId: string,
serviceId: string,
organizationId: string,
userId: string,
serviceId: string,
organizationId: string,
) => {
const { accessedServices } = await findMemberById(userId, organizationId);
const haveAccessToService = accessedServices.includes(serviceId);
const { accessedServices } = await findMemberById(userId, organizationId);
const haveAccessToService = accessedServices.includes(serviceId);
if (haveAccessToService) {
return true;
}
if (haveAccessToService) {
return true;
}
return false;
return false;
};
export const canPeformDeleteService = async (
userId: string,
serviceId: string,
organizationId: string,
export const canPerformDeleteService = async (
userId: string,
serviceId: string,
organizationId: string,
) => {
const { accessedServices, canDeleteServices } = await findMemberById(
userId,
organizationId,
);
const haveAccessToService = accessedServices.includes(serviceId);
const { accessedServices, canDeleteServices } = await findMemberById(
userId,
organizationId,
);
const haveAccessToService = accessedServices.includes(serviceId);
if (canDeleteServices && haveAccessToService) {
return true;
}
if (canDeleteServices && haveAccessToService) {
return true;
}
return false;
return false;
};
export const canPerformCreationProject = async (
userId: string,
organizationId: string,
userId: string,
organizationId: string,
) => {
const { canCreateProjects } = await findMemberById(userId, organizationId);
const { canCreateProjects } = await findMemberById(userId, organizationId);
if (canCreateProjects) {
return true;
}
if (canCreateProjects) {
return true;
}
return false;
return false;
};
export const canPerformDeleteProject = async (
userId: string,
organizationId: string,
userId: string,
organizationId: string,
) => {
const { canDeleteProjects } = await findMemberById(userId, organizationId);
const { canDeleteProjects } = await findMemberById(userId, organizationId);
if (canDeleteProjects) {
return true;
}
if (canDeleteProjects) {
return true;
}
return false;
return false;
};
export const canPerformAccessProject = async (
userId: string,
projectId: string,
organizationId: string,
userId: string,
projectId: string,
organizationId: string,
) => {
const { accessedProjects } = await findMemberById(userId, organizationId);
const { accessedProjects } = await findMemberById(userId, organizationId);
const haveAccessToProject = accessedProjects.includes(projectId);
const haveAccessToProject = accessedProjects.includes(projectId);
if (haveAccessToProject) {
return true;
}
return false;
if (haveAccessToProject) {
return true;
}
return false;
};
export const canPerformAccessEnvironment = async (
userId: string,
environmentId: string,
organizationId: string,
userId: string,
environmentId: string,
organizationId: string,
) => {
const { accessedEnvironments } = await findMemberById(userId, organizationId);
const haveAccessToEnvironment = accessedEnvironments.includes(environmentId);
const { accessedEnvironments } = await findMemberById(userId, organizationId);
const haveAccessToEnvironment = accessedEnvironments.includes(environmentId);
if (haveAccessToEnvironment) {
return true;
}
if (haveAccessToEnvironment) {
return true;
}
return false;
return false;
};
export const canPerformDeleteEnvironment = async (
userId: string,
projectId: string,
organizationId: string,
userId: string,
projectId: string,
organizationId: string,
) => {
const { accessedProjects, canDeleteEnvironments } = await findMemberById(
userId,
organizationId,
);
const haveAccessToProject = accessedProjects.includes(projectId);
const { accessedProjects, canDeleteEnvironments } = await findMemberById(
userId,
organizationId,
);
const haveAccessToProject = accessedProjects.includes(projectId);
if (canDeleteEnvironments && haveAccessToProject) {
return true;
}
if (canDeleteEnvironments && haveAccessToProject) {
return true;
}
return false;
return false;
};
export const canAccessToTraefikFiles = async (
userId: string,
organizationId: string,
userId: string,
organizationId: string,
) => {
const { canAccessToTraefikFiles } = await findMemberById(
userId,
organizationId,
);
return canAccessToTraefikFiles;
const { canAccessToTraefikFiles } = await findMemberById(
userId,
organizationId,
);
return canAccessToTraefikFiles;
};
export const checkServiceAccess = async (
userId: string,
serviceId: string,
organizationId: string,
action = "access" as "access" | "create" | "delete",
userId: string,
serviceId: string,
organizationId: string,
action = "access" as "access" | "create" | "delete",
) => {
let hasPermission = false;
switch (action) {
case "create":
hasPermission = await canPerformCreationService(
userId,
serviceId,
organizationId,
);
break;
case "access":
hasPermission = await canPerformAccessService(
userId,
serviceId,
organizationId,
);
break;
case "delete":
hasPermission = await canPeformDeleteService(
userId,
serviceId,
organizationId,
);
break;
default:
hasPermission = false;
}
if (!hasPermission) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "Permission denied",
});
}
let hasPermission = false;
switch (action) {
case "create":
hasPermission = await canPerformCreationService(
userId,
serviceId,
organizationId,
);
break;
case "access":
hasPermission = await canPerformAccessService(
userId,
serviceId,
organizationId,
);
break;
case "delete":
hasPermission = await canPerformDeleteService(
userId,
serviceId,
organizationId,
);
break;
default:
hasPermission = false;
}
if (!hasPermission) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "Permission denied",
});
}
};
export const checkEnvironmentAccess = async (
userId: string,
environmentId: string,
organizationId: string,
action = "access" as const,
userId: string,
environmentId: string,
organizationId: string,
action = "access" as const,
) => {
let hasPermission = false;
switch (action) {
case "access":
hasPermission = await canPerformAccessEnvironment(
userId,
environmentId,
organizationId,
);
break;
default:
hasPermission = false;
}
if (!hasPermission) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "Permission denied",
});
}
let hasPermission = false;
switch (action) {
case "access":
hasPermission = await canPerformAccessEnvironment(
userId,
environmentId,
organizationId,
);
break;
default:
hasPermission = false;
}
if (!hasPermission) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "Permission denied",
});
}
};
export const checkEnvironmentDeletionPermission = async (
userId: string,
projectId: string,
organizationId: string,
userId: string,
projectId: string,
organizationId: string,
) => {
const member = await findMemberById(userId, organizationId);
const member = await findMemberById(userId, organizationId);
if (!member) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "User not found in organization",
});
}
if (!member) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "User not found in organization",
});
}
if (member.role === "owner" || member.role === "admin") {
return true;
}
if (member.role === "owner" || member.role === "admin") {
return true;
}
if (!member.canDeleteEnvironments) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You don't have permission to delete environments",
});
}
if (!member.canDeleteEnvironments) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You don't have permission to delete environments",
});
}
const hasProjectAccess = member.accessedProjects.includes(projectId);
if (!hasProjectAccess) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You don't have access to this project",
});
}
const hasProjectAccess = member.accessedProjects.includes(projectId);
if (!hasProjectAccess) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You don't have access to this project",
});
}
return true;
return true;
};
export const checkProjectAccess = async (
authId: string,
action: "create" | "delete" | "access",
organizationId: string,
projectId?: string,
authId: string,
action: "create" | "delete" | "access",
organizationId: string,
projectId?: string,
) => {
let hasPermission = false;
switch (action) {
case "access":
hasPermission = await canPerformAccessProject(
authId,
projectId as string,
organizationId,
);
break;
case "create":
hasPermission = await canPerformCreationProject(authId, organizationId);
break;
case "delete":
hasPermission = await canPerformDeleteProject(authId, organizationId);
break;
default:
hasPermission = false;
}
if (!hasPermission) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "Permission denied",
});
}
let hasPermission = false;
switch (action) {
case "access":
hasPermission = await canPerformAccessProject(
authId,
projectId as string,
organizationId,
);
break;
case "create":
hasPermission = await canPerformCreationProject(authId, organizationId);
break;
case "delete":
hasPermission = await canPerformDeleteProject(authId, organizationId);
break;
default:
hasPermission = false;
}
if (!hasPermission) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "Permission denied",
});
}
};
export const checkEnvironmentCreationPermission = async (
userId: string,
projectId: string,
organizationId: string,
userId: string,
projectId: string,
organizationId: string,
) => {
// Get user's member record
const member = await findMemberById(userId, organizationId);
// Get user's member record
const member = await findMemberById(userId, organizationId);
if (!member) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "User not found in organization",
});
}
if (!member) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "User not found in organization",
});
}
// Owners and admins can always create environments
if (member.role === "owner" || member.role === "admin") {
return true;
}
// Owners and admins can always create environments
if (member.role === "owner" || member.role === "admin") {
return true;
}
// Check if user has canCreateEnvironments permission
if (!member.canCreateEnvironments) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You don't have permission to create environments",
});
}
// Check if user has canCreateEnvironments permission
if (!member.canCreateEnvironments) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You don't have permission to create environments",
});
}
// Check if user has access to the project
const hasProjectAccess = member.accessedProjects.includes(projectId);
if (!hasProjectAccess) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You don't have access to this project",
});
}
// Check if user has access to the project
const hasProjectAccess = member.accessedProjects.includes(projectId);
if (!hasProjectAccess) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You don't have access to this project",
});
}
return true;
return true;
};
export const findMemberById = async (
userId: string,
organizationId: string,
userId: string,
organizationId: string,
) => {
const result = await db.query.member.findFirst({
where: and(
eq(member.userId, userId),
eq(member.organizationId, organizationId),
),
with: {
user: true,
},
});
const result = await db.query.member.findFirst({
where: and(
eq(member.userId, userId),
eq(member.organizationId, organizationId),
),
with: {
user: true,
},
});
if (!result) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "Permission denied",
});
}
return result;
if (!result) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "Permission denied",
});
}
return result;
};
export const updateUser = async (userId: string, userData: Partial<User>) => {
// Validate email if it's being updated
if (userData.email !== undefined) {
if (!userData.email || userData.email.trim() === "") {
throw new Error("Email is required and cannot be empty");
}
// Validate email if it's being updated
if (userData.email !== undefined) {
if (!userData.email || userData.email.trim() === "") {
throw new Error("Email is required and cannot be empty");
}
// Basic email format validation
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(userData.email)) {
throw new Error("Please enter a valid email address");
}
}
// Basic email format validation
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(userData.email)) {
throw new Error("Please enter a valid email address");
}
}
const userResult = await db
.update(user)
.set({
...userData,
})
.where(eq(user.id, userId))
.returning()
.then((res) => res[0]);
const userResult = await db
.update(user)
.set({
...userData,
})
.where(eq(user.id, userId))
.returning()
.then((res) => res[0]);
return userResult;
return userResult;
};
export const createApiKey = async (
userId: string,
input: {
name: string;
prefix?: string;
expiresIn?: number;
metadata: {
organizationId: string;
};
rateLimitEnabled?: boolean;
rateLimitTimeWindow?: number;
rateLimitMax?: number;
remaining?: number;
refillAmount?: number;
refillInterval?: number;
},
userId: string,
input: {
name: string;
prefix?: string;
expiresIn?: number;
metadata: {
organizationId: string;
};
rateLimitEnabled?: boolean;
rateLimitTimeWindow?: number;
rateLimitMax?: number;
remaining?: number;
refillAmount?: number;
refillInterval?: number;
},
) => {
const result = await auth.createApiKey({
body: {
name: input.name,
expiresIn: input.expiresIn,
prefix: input.prefix,
rateLimitEnabled: input.rateLimitEnabled,
rateLimitTimeWindow: input.rateLimitTimeWindow,
rateLimitMax: input.rateLimitMax,
remaining: input.remaining,
refillAmount: input.refillAmount,
refillInterval: input.refillInterval,
userId,
},
});
const result = await auth.createApiKey({
body: {
name: input.name,
expiresIn: input.expiresIn,
prefix: input.prefix,
rateLimitEnabled: input.rateLimitEnabled,
rateLimitTimeWindow: input.rateLimitTimeWindow,
rateLimitMax: input.rateLimitMax,
remaining: input.remaining,
refillAmount: input.refillAmount,
refillInterval: input.refillInterval,
userId,
},
});
if (input.metadata) {
await db
.update(apikey)
.set({ metadata: JSON.stringify(input.metadata) })
.where(eq(apikey.id, result.id));
}
if (input.metadata) {
await db
.update(apikey)
.set({ metadata: JSON.stringify(input.metadata) })
.where(eq(apikey.id, result.id));
}
return result;
return result;
};

View File

@@ -3,13 +3,13 @@ import { docker } from "../constants";
export const initializeSwarm = async () => {
const swarmInitialized = await dockerSwarmInitialized();
if (swarmInitialized) {
console.log("Swarm is already initilized");
console.log("Swarm is already initialized");
} else {
await docker.swarmInit({
AdvertiseAddr: "127.0.0.1",
ListenAddr: "0.0.0.0",
});
console.log("Swarm was initilized");
console.log("Swarm was initialized");
}
};
@@ -26,14 +26,14 @@ export const dockerSwarmInitialized = async () => {
export const initializeNetwork = async () => {
const networkInitialized = await dockerNetworkInitialized();
if (networkInitialized) {
console.log("Network is already initilized");
console.log("Network is already initialized");
} else {
docker.createNetwork({
Attachable: true,
Name: "dokploy-network",
Driver: "overlay",
});
console.log("Network was initilized");
console.log("Network was initialized");
}
};

View File

@@ -5,26 +5,26 @@ import boxen from "boxen";
import { quote } from "shell-quote";
import { writeDomainsToCompose } from "../docker/domain";
import {
encodeBase64,
getEnviromentVariablesObject,
prepareEnvironmentVariables,
encodeBase64,
getEnvironmentVariablesObject,
prepareEnvironmentVariables,
} from "../docker/utils";
export type ComposeNested = InferResultType<
"compose",
{ environment: { with: { project: true } }; mounts: true; domains: true }
"compose",
{ environment: { with: { project: true } }; mounts: true; domains: true }
>;
export const getBuildComposeCommand = async (compose: ComposeNested) => {
const { COMPOSE_PATH } = paths(!!compose.serverId);
const { sourceType, appName, mounts, composeType, domains } = compose;
const command = createCommand(compose);
const envCommand = getCreateEnvFileCommand(compose);
const projectPath = join(COMPOSE_PATH, compose.appName, "code");
const exportEnvCommand = getExportEnvCommand(compose);
const { COMPOSE_PATH } = paths(!!compose.serverId);
const { sourceType, appName, mounts, composeType, domains } = compose;
const command = createCommand(compose);
const envCommand = getCreateEnvFileCommand(compose);
const projectPath = join(COMPOSE_PATH, compose.appName, "code");
const exportEnvCommand = getExportEnvCommand(compose);
const newCompose = await writeDomainsToCompose(compose, domains);
const logContent = `
const newCompose = await writeDomainsToCompose(compose, domains);
const logContent = `
App Name: ${appName}
Build Compose 🐳
Detected: ${mounts.length} mounts 📂
@@ -32,31 +32,31 @@ Command: docker ${command}
Source Type: docker ${sourceType}
Compose Type: ${composeType}`;
const logBox = boxen(logContent, {
padding: {
left: 1,
right: 1,
bottom: 1,
},
width: 80,
borderStyle: "double",
});
const logBox = boxen(logContent, {
padding: {
left: 1,
right: 1,
bottom: 1,
},
width: 80,
borderStyle: "double",
});
const bashCommand = `
const bashCommand = `
set -e
{
echo "${logBox}";
${newCompose}
${envCommand}
cd "${projectPath}";
${compose.isolatedDeployment ? `docker network inspect ${compose.appName} >/dev/null 2>&1 || docker network create ${compose.composeType === "stack" ? "--driver overlay" : ""} --attachable ${compose.appName}` : ""}
env -i PATH="$PATH" ${exportEnvCommand} docker ${command.split(" ").join(" ")} 2>&1 || { echo "Error: ❌ Docker command failed"; exit 1; }
${compose.isolatedDeployment ? `docker network connect ${compose.appName} $(docker ps --filter "name=dokploy-traefik" -q) >/dev/null 2>&1` : ""}
echo "Docker Compose Deployed: ✅";
} || {
echo "Error: ❌ Script execution failed";
@@ -64,81 +64,81 @@ Compose Type: ${composeType} ✅`;
}
`;
return bashCommand;
return bashCommand;
};
const sanitizeCommand = (command: string) => {
const sanitizedCommand = command.trim();
const sanitizedCommand = command.trim();
const parts = sanitizedCommand.split(/\s+/);
const parts = sanitizedCommand.split(/\s+/);
const restCommand = parts.map((arg) => arg.replace(/^"(.*)"$/, "$1"));
const restCommand = parts.map((arg) => arg.replace(/^"(.*)"$/, "$1"));
return restCommand.join(" ");
return restCommand.join(" ");
};
export const createCommand = (compose: ComposeNested) => {
const { composeType, appName, sourceType } = compose;
if (compose.command) {
return `${sanitizeCommand(compose.command)}`;
}
const { composeType, appName, sourceType } = compose;
if (compose.command) {
return `${sanitizeCommand(compose.command)}`;
}
const path =
sourceType === "raw" ? "docker-compose.yml" : compose.composePath;
let command = "";
const path =
sourceType === "raw" ? "docker-compose.yml" : compose.composePath;
let command = "";
if (composeType === "docker-compose") {
command = `compose -p ${appName} -f ${path} up -d --build --remove-orphans`;
} else if (composeType === "stack") {
command = `stack deploy -c ${path} ${appName} --prune --with-registry-auth`;
}
if (composeType === "docker-compose") {
command = `compose -p ${appName} -f ${path} up -d --build --remove-orphans`;
} else if (composeType === "stack") {
command = `stack deploy -c ${path} ${appName} --prune --with-registry-auth`;
}
return command;
return command;
};
export const getCreateEnvFileCommand = (compose: ComposeNested) => {
const { COMPOSE_PATH } = paths(!!compose.serverId);
const { env, composePath, appName } = compose;
const composeFilePath =
join(COMPOSE_PATH, appName, "code", composePath) ||
join(COMPOSE_PATH, appName, "code", "docker-compose.yml");
const { COMPOSE_PATH } = paths(!!compose.serverId);
const { env, composePath, appName } = compose;
const composeFilePath =
join(COMPOSE_PATH, appName, "code", composePath) ||
join(COMPOSE_PATH, appName, "code", "docker-compose.yml");
const envFilePath = join(dirname(composeFilePath), ".env");
const envFilePath = join(dirname(composeFilePath), ".env");
let envContent = `APP_NAME=${appName}\n`;
envContent += env || "";
if (!envContent.includes("DOCKER_CONFIG")) {
envContent += "\nDOCKER_CONFIG=/root/.docker";
}
let envContent = `APP_NAME=${appName}\n`;
envContent += env || "";
if (!envContent.includes("DOCKER_CONFIG")) {
envContent += "\nDOCKER_CONFIG=/root/.docker";
}
if (compose.randomize) {
envContent += `\nCOMPOSE_PREFIX=${compose.suffix}`;
}
if (compose.randomize) {
envContent += `\nCOMPOSE_PREFIX=${compose.suffix}`;
}
const envFileContent = prepareEnvironmentVariables(
envContent,
compose.environment.project.env,
compose.environment.env,
).join("\n");
const envFileContent = prepareEnvironmentVariables(
envContent,
compose.environment.project.env,
compose.environment.env,
).join("\n");
const encodedContent = encodeBase64(envFileContent);
return `
const encodedContent = encodeBase64(envFileContent);
return `
touch ${envFilePath};
echo "${encodedContent}" | base64 -d > "${envFilePath}";
`;
};
const getExportEnvCommand = (compose: ComposeNested) => {
if (compose.composeType !== "stack") return "";
if (compose.composeType !== "stack") return "";
const envVars = getEnviromentVariablesObject(
compose.env,
compose.environment.project.env,
compose.environment.env,
);
const exports = Object.entries(envVars)
.map(([key, value]) => `${key}=${quote([value])}`)
.join(" ");
const envVars = getEnvironmentVariablesObject(
compose.env,
compose.environment.project.env,
compose.environment.env,
);
const exports = Object.entries(envVars)
.map(([key, value]) => `${key}=${quote([value])}`)
.join(" ");
return exports ? `${exports}` : "";
return exports ? `${exports}` : "";
};

View File

@@ -1,105 +1,105 @@
import {
getEnviromentVariablesObject,
prepareEnvironmentVariablesForShell,
getEnvironmentVariablesObject,
prepareEnvironmentVariablesForShell,
} from "@dokploy/server/utils/docker/utils";
import { quote } from "shell-quote";
import {
getBuildAppDirectory,
getDockerContextPath,
getBuildAppDirectory,
getDockerContextPath,
} from "../filesystem/directory";
import type { ApplicationNested } from ".";
import { createEnvFileCommand } from "./utils";
export const getDockerCommand = (application: ApplicationNested) => {
const {
appName,
env,
publishDirectory,
buildArgs,
buildSecrets,
dockerBuildStage,
cleanCache,
createEnvFile,
} = application;
const dockerFilePath = getBuildAppDirectory(application);
const {
appName,
env,
publishDirectory,
buildArgs,
buildSecrets,
dockerBuildStage,
cleanCache,
createEnvFile,
} = application;
const dockerFilePath = getBuildAppDirectory(application);
try {
const image = `${appName}`;
try {
const image = `${appName}`;
const defaultContextPath =
dockerFilePath.substring(0, dockerFilePath.lastIndexOf("/") + 1) || ".";
const defaultContextPath =
dockerFilePath.substring(0, dockerFilePath.lastIndexOf("/") + 1) || ".";
const dockerContextPath =
getDockerContextPath(application) || defaultContextPath;
const dockerContextPath =
getDockerContextPath(application) || defaultContextPath;
const commandArgs = ["build", "-t", image, "-f", dockerFilePath, "."];
const commandArgs = ["build", "-t", image, "-f", dockerFilePath, "."];
if (dockerBuildStage) {
commandArgs.push("--target", dockerBuildStage);
}
if (dockerBuildStage) {
commandArgs.push("--target", dockerBuildStage);
}
if (cleanCache) {
commandArgs.push("--no-cache");
}
if (cleanCache) {
commandArgs.push("--no-cache");
}
const args = prepareEnvironmentVariablesForShell(
buildArgs,
application.environment.project.env,
application.environment.env,
);
const args = prepareEnvironmentVariablesForShell(
buildArgs,
application.environment.project.env,
application.environment.env,
);
for (const arg of args) {
commandArgs.push("--build-arg", arg);
}
for (const arg of args) {
commandArgs.push("--build-arg", arg);
}
const secrets = getEnviromentVariablesObject(
buildSecrets,
application.environment.project.env,
application.environment.env,
);
const secrets = getEnvironmentVariablesObject(
buildSecrets,
application.environment.project.env,
application.environment.env,
);
const joinedSecrets = Object.entries(secrets)
.map(([key, value]) => `${key}=${quote([value])}`)
.join(" ");
const joinedSecrets = Object.entries(secrets)
.map(([key, value]) => `${key}=${quote([value])}`)
.join(" ");
/*
/*
Do not generate an environment file when publishDirectory is specified,
as it could be publicly exposed.
Also respect the createEnvFile flag.
*/
let command = "";
if (!publishDirectory && createEnvFile) {
command += createEnvFileCommand(
dockerFilePath,
env,
application.environment.project.env,
application.environment.env,
);
}
let command = "";
if (!publishDirectory && createEnvFile) {
command += createEnvFileCommand(
dockerFilePath,
env,
application.environment.project.env,
application.environment.env,
);
}
for (const key in secrets) {
// Although buildx is smart enough to know we may be referring to an environment variable name,
// we still make sure it doesn't fall back to `type=file`.
// See: https://docs.docker.com/reference/cli/docker/buildx/build/#secret
commandArgs.push("--secret", `type=env,id=${key}`);
}
for (const key in secrets) {
// Although buildx is smart enough to know we may be referring to an environment variable name,
// we still make sure it doesn't fall back to `type=file`.
// See: https://docs.docker.com/reference/cli/docker/buildx/build/#secret
commandArgs.push("--secret", `type=env,id=${key}`);
}
command += `
command += `
echo "Building ${appName}" ;
cd ${dockerContextPath} || {
cd ${dockerContextPath} || {
echo "❌ The path ${dockerContextPath} does not exist" ;
exit 1;
}
${joinedSecrets} docker ${commandArgs.join(" ")} || {
${joinedSecrets} docker ${commandArgs.join(" ")} || {
echo "❌ Docker build failed" ;
exit 1;
}
echo "✅ Docker build completed." ;
`;
return command;
} catch (error) {
throw error;
}
return command;
} catch (error) {
throw error;
}
};

View File

@@ -434,7 +434,7 @@ export const parseEnvironmentKeyValuePair = (
return [key, valueParts.join("=")];
};
export const getEnviromentVariablesObject = (
export const getEnvironmentVariablesObject = (
input: string | null,
projectEnv?: string | null,
environmentEnv?: string | null,