Compare commits

..

18 Commits

Author SHA1 Message Date
Mauricio Siu
5cd624c7ea Merge pull request #647 from Dokploy/canary
v0.10.10
2024-11-04 10:32:20 -06:00
Mauricio Siu
34b12a0315 chore(version): bump version 2024-11-04 10:20:55 -06:00
Mauricio Siu
e9e6064eb6 Merge pull request #646 from Dokploy/fix/notifications
fix(dokploy): filter notifications by admin
2024-11-04 10:17:17 -06:00
Mauricio Siu
6b7712e35f fix(dokploy): filter notifications by admin 2024-11-04 10:10:52 -06:00
Mauricio Siu
89c7e96df0 Merge pull request #643 from Dokploy/canary
v0.10.9
2024-11-02 17:18:34 -06:00
Mauricio Siu
8af5afbb6c chore(version): bump version 2024-11-02 17:11:46 -06:00
Mauricio Siu
ef9f16a3c3 refactor(dokploy): remove registry url 2024-11-02 15:37:49 -06:00
Mauricio Siu
d15b6387a3 refactor(dokploy): remove registryURL from registryTag 2024-11-02 15:28:14 -06:00
Mauricio Siu
2e9e39dcf5 Merge pull request #642 from Dokploy/fix/registry
fix(registry): add image tag resolution correctly when using cluster
2024-11-02 14:33:44 -06:00
Mauricio Siu
d901b02e92 refactor(dokploy): remove log 2024-11-02 14:18:00 -06:00
Mauricio Siu
766a25ccad fix(registry): add image tag resolution correctly when using cluster 2024-11-02 14:17:21 -06:00
Mauricio Siu
fae97b1817 Merge pull request #641 from Dokploy/639-cannot-fetch-projects-on-non-main-users-dashboard
refactor(dokploy): add missing project
2024-11-02 01:53:50 -06:00
Mauricio Siu
b2cc5e58a3 refactor(dokploy): add missing project 2024-11-02 01:28:13 -06:00
Mauricio Siu
1b56a6b400 refactor(dokploy): revert template content 2024-11-01 10:54:45 -06:00
Mauricio Siu
996d449f0f chore(version): bump version 2024-11-01 09:53:41 -06:00
Mauricio Siu
3b197f3624 fix(dokploy): add missing mount path in update volume 2024-11-01 09:53:21 -06:00
Mauricio Siu
0e136ffb8f Merge pull request #632 from Dokploy/canary
v10.7.0
2024-10-30 23:10:48 -06:00
Mauricio Siu
95e53169b1 chore(version): bump version 2024-10-30 23:07:05 -06:00
22 changed files with 154 additions and 84 deletions

View File

@@ -119,7 +119,7 @@ export const UpdateVolume = ({
} else if (typeForm === "file") {
form.reset({
content: data.content || "",
mountPath: data.mountPath,
mountPath: "/",
filePath: data.filePath || "",
type: "file",
});
@@ -296,15 +296,13 @@ export const UpdateVolume = ({
)}
</div>
<DialogFooter>
<DialogClose>
<Button
isLoading={isLoading}
form="hook-form-update-volume"
type="submit"
>
Update
</Button>
</DialogClose>
<Button
isLoading={isLoading}
// form="hook-form-update-volume"
type="submit"
>
Update
</Button>
</DialogFooter>
</form>
</Form>

View File

@@ -1,6 +1,6 @@
{
"name": "dokploy",
"version": "v0.10.6",
"version": "v0.10.10",
"private": true,
"license": "Apache-2.0",
"type": "module",

View File

@@ -132,16 +132,20 @@ export const projectRouter = createTRPCRouter({
if (accesedProjects.length === 0) {
return [];
}
const query = await db.query.projects.findMany({
where: sql`${projects.projectId} IN (${sql.join(
accesedProjects.map((projectId) => sql`${projectId}`),
sql`, `,
)})`,
where: and(
sql`${projects.projectId} IN (${sql.join(
accesedProjects.map((projectId) => sql`${projectId}`),
sql`, `,
)})`,
eq(projects.adminId, ctx.user.adminId),
),
with: {
applications: {
where: and(
buildServiceFilter(applications.applicationId, accesedServices),
eq(projects.adminId, ctx.user.adminId),
where: buildServiceFilter(
applications.applicationId,
accesedServices,
),
with: { domains: true },
},

View File

@@ -242,7 +242,7 @@ export const settingsRouter = createTRPCRouter({
await cleanUpUnusedImages(server.serverId);
await cleanUpDockerBuilder(server.serverId);
await cleanUpSystemPrune(server.serverId);
await sendDockerCleanupNotifications();
await sendDockerCleanupNotifications(server.adminId);
});
}
} else {
@@ -278,7 +278,7 @@ export const settingsRouter = createTRPCRouter({
await cleanUpUnusedImages();
await cleanUpDockerBuilder();
await cleanUpSystemPrune();
await sendDockerCleanupNotifications();
await sendDockerCleanupNotifications(admin.adminId);
});
} else {
const currentJob = scheduledJobs["docker-cleanup"];

View File

@@ -63,7 +63,6 @@ export const setupDockerContainerLogsWebSocketServer = (
}
stream
.on("close", () => {
console.log("Connection closed ✅ Container Logs");
client.end();
ws.close();
})
@@ -88,7 +87,6 @@ export const setupDockerContainerLogsWebSocketServer = (
privateKey: server.sshKey?.privateKey,
});
ws.on("close", () => {
console.log("Connection closed ✅, From Container Logs WS");
client.end();
});
} else {

View File

@@ -113,7 +113,6 @@ export const setupTerminalWebSocketServer = (
});
ws.on("close", () => {
console.log("Connection closed ✅");
stream.end();
});
},

View File

@@ -1,19 +1,12 @@
version: '3.3'
services:
stirling-pdf:
image: frooodle/s-pdf:0.30.1
ports:
- '8080'
volumes:
- training-data:/usr/share/tessdata #Required for extra OCR languages
- extra-configs:/configs
# - /location/of/customFiles:/customFiles/
# - /location/of/logs:/logs/
environment:
- DOCKER_ENABLE_SECURITY=false
- INSTALL_BOOK_AND_ADVANCED_HTML_OPS=false
- LANGS=en_GB
version: "3"
volumes:
training-data: {}
extra-configs: {}
services:
soketi:
image: quay.io/soketi/soketi:1.4-16-debian
container_name: soketi
environment:
SOKETI_DEBUG: "1"
SOKETI_HOST: "0.0.0.0"
SOKETI_PORT: "6001"
SOKETI_METRICS_SERVER_PORT: "9601"
restart: unless-stopped

View File

@@ -1,11 +1,10 @@
import { relations, sql } from "drizzle-orm";
import { boolean, pgEnum, pgTable, text, timestamp } from "drizzle-orm/pg-core";
import { relations } from "drizzle-orm";
import { pgEnum, pgTable, text } from "drizzle-orm/pg-core";
import { createInsertSchema } from "drizzle-zod";
import { nanoid } from "nanoid";
import { z } from "zod";
import { admins } from "./admin";
import { applications } from "./application";
import { auth } from "./auth";
/**
* This is an example of how to use the multi-project schema feature of Drizzle ORM. Use the same
* database instance for multiple projects.
@@ -48,7 +47,7 @@ const createSchema = createInsertSchema(registry, {
registryUrl: z.string(),
adminId: z.string().min(1),
registryId: z.string().min(1),
registryType: z.enum(["selfHosted", "cloud"]),
registryType: z.enum(["cloud"]),
imagePrefix: z.string().nullable().optional(),
});
@@ -59,7 +58,7 @@ export const apiCreateRegistry = createSchema
username: z.string().min(1),
password: z.string().min(1),
registryUrl: z.string(),
registryType: z.enum(["selfHosted", "cloud"]),
registryType: z.enum(["cloud"]),
imagePrefix: z.string().nullable().optional(),
})
.required()
@@ -72,7 +71,7 @@ export const apiTestRegistry = createSchema.pick({}).extend({
username: z.string().min(1),
password: z.string().min(1),
registryUrl: z.string(),
registryType: z.enum(["selfHosted", "cloud"]),
registryType: z.enum(["cloud"]),
imagePrefix: z.string().nullable().optional(),
serverId: z.string().optional(),
});

View File

@@ -193,6 +193,7 @@ export const deployApplication = async ({
applicationName: application.name,
applicationType: "application",
buildLink,
adminId: application.project.adminId,
});
} catch (error) {
await updateDeploymentStatus(deployment.deploymentId, "error");
@@ -204,6 +205,7 @@ export const deployApplication = async ({
// @ts-ignore
errorMessage: error?.message || "Error to build",
buildLink,
adminId: application.project.adminId,
});
console.log(
@@ -314,6 +316,7 @@ export const deployRemoteApplication = async ({
applicationName: application.name,
applicationType: "application",
buildLink,
adminId: application.project.adminId,
});
} catch (error) {
// @ts-ignore
@@ -336,6 +339,7 @@ export const deployRemoteApplication = async ({
// @ts-ignore
errorMessage: error?.message || "Error to build",
buildLink,
adminId: application.project.adminId,
});
console.log(

View File

@@ -235,6 +235,7 @@ export const deployCompose = async ({
applicationName: compose.name,
applicationType: "compose",
buildLink,
adminId: compose.project.adminId,
});
} catch (error) {
await updateDeploymentStatus(deployment.deploymentId, "error");
@@ -248,6 +249,7 @@ export const deployCompose = async ({
// @ts-ignore
errorMessage: error?.message || "Error to build",
buildLink,
adminId: compose.project.adminId,
});
throw error;
}
@@ -353,6 +355,7 @@ export const deployRemoteCompose = async ({
applicationName: compose.name,
applicationType: "compose",
buildLink,
adminId: compose.project.adminId,
});
} catch (error) {
// @ts-ignore
@@ -376,6 +379,7 @@ export const deployRemoteCompose = async ({
// @ts-ignore
errorMessage: error?.message || "Error to build",
buildLink,
adminId: compose.project.adminId,
});
throw error;
}

View File

@@ -101,7 +101,6 @@ const installRequirements = async (serverId: string, logPath: string) => {
}
stream
.on("close", () => {
writeStream.write("Connection closed ✅");
client.end();
resolve();
})

View File

@@ -43,7 +43,7 @@ export const generatePassword = (quantity = 16): string => {
Math.floor(Math.random() * characters.length),
);
}
return password;
return password.toLowerCase();
};
export const generateBase64 = (bytes = 32): string => {

View File

@@ -49,6 +49,7 @@ export const runMariadbBackup = async (
projectName: project.name,
databaseType: "mariadb",
type: "success",
adminId: project.adminId,
});
} catch (error) {
console.log(error);
@@ -59,6 +60,7 @@ export const runMariadbBackup = async (
type: "error",
// @ts-ignore
errorMessage: error?.message || "Error message not provided",
adminId: project.adminId,
});
throw error;
}

View File

@@ -46,6 +46,7 @@ export const runMongoBackup = async (mongo: Mongo, backup: BackupSchedule) => {
projectName: project.name,
databaseType: "mongodb",
type: "success",
adminId: project.adminId,
});
} catch (error) {
console.log(error);
@@ -56,6 +57,7 @@ export const runMongoBackup = async (mongo: Mongo, backup: BackupSchedule) => {
type: "error",
// @ts-ignore
errorMessage: error?.message || "Error message not provided",
adminId: project.adminId,
});
throw error;
}

View File

@@ -46,6 +46,7 @@ export const runMySqlBackup = async (mysql: MySql, backup: BackupSchedule) => {
projectName: project.name,
databaseType: "mysql",
type: "success",
adminId: project.adminId,
});
} catch (error) {
console.log(error);
@@ -56,6 +57,7 @@ export const runMySqlBackup = async (mysql: MySql, backup: BackupSchedule) => {
type: "error",
// @ts-ignore
errorMessage: error?.message || "Error message not provided",
adminId: project.adminId,
});
throw error;
}

View File

@@ -49,6 +49,7 @@ export const runPostgresBackup = async (
projectName: project.name,
databaseType: "postgres",
type: "success",
adminId: project.adminId,
});
} catch (error) {
await sendDatabaseBackupNotifications({
@@ -58,6 +59,7 @@ export const runPostgresBackup = async (
type: "error",
// @ts-ignore
errorMessage: error?.message || "Error message not provided",
adminId: project.adminId,
});
throw error;

View File

@@ -1,7 +1,8 @@
import { createWriteStream } from "node:fs";
import { join } from "node:path";
import type { InferResultType } from "@dokploy/server/types/with";
import type { CreateServiceOptions } from "dockerode";
import { uploadImage } from "../cluster/upload";
import { uploadImage, uploadImageRemoteCommand } from "../cluster/upload";
import {
calculateResources,
generateBindMounts,
@@ -69,19 +70,30 @@ export const getBuildCommand = (
application: ApplicationNested,
logPath: string,
) => {
const { buildType } = application;
let command = "";
const { buildType, registry } = application;
switch (buildType) {
case "nixpacks":
return getNixpacksCommand(application, logPath);
command = getNixpacksCommand(application, logPath);
break;
case "heroku_buildpacks":
return getHerokuCommand(application, logPath);
command = getHerokuCommand(application, logPath);
break;
case "paketo_buildpacks":
return getPaketoCommand(application, logPath);
command = getPaketoCommand(application, logPath);
break;
case "static":
return getStaticCommand(application, logPath);
command = getStaticCommand(application, logPath);
break;
case "dockerfile":
return getDockerCommand(application, logPath);
command = getDockerCommand(application, logPath);
break;
}
if (registry) {
command += uploadImageRemoteCommand(application, logPath);
}
return command;
};
export const mechanizeDockerContainer = async (
@@ -186,11 +198,11 @@ const getImageName = (application: ApplicationNested) => {
return dockerImage || "ERROR-NO-IMAGE-PROVIDED";
}
const registryUrl = registry?.registryUrl || "";
const imagePrefix = registry?.imagePrefix ? `${registry.imagePrefix}/` : "";
return registry
? `${registryUrl}/${imagePrefix}${appName}`
: `${appName}:latest`;
if (registry) {
return join(registry.imagePrefix || "", appName);
}
return `${appName}:latest`;
};
const getAuthConfig = (application: ApplicationNested) => {

View File

@@ -1,4 +1,5 @@
import type { WriteStream } from "node:fs";
import { join } from "node:path";
import type { ApplicationNested } from "../builders";
import { spawnAsync } from "../process/spawnAsync";
@@ -16,23 +17,14 @@ export const uploadImage = async (
const { appName } = application;
const imageName = `${appName}:latest`;
const finalURL =
registryType === "selfHosted"
? process.env.NODE_ENV === "development"
? "localhost:5000"
: registryUrl
: registryUrl;
const finalURL = registryUrl;
const registryTag = imagePrefix
? `${finalURL}/${imagePrefix}/${imageName}`
: `${finalURL}/${imageName}`;
const registryTag = join(imagePrefix || "", imageName);
try {
console.log(finalURL, registryTag);
writeStream.write(
`📦 [Enabled Registry] Uploading image to ${registry.registryType} | ${registryTag} | ${finalURL}\n`,
);
await spawnAsync(
"docker",
["login", finalURL, "-u", registry.username, "-p", registry.password],
@@ -59,7 +51,48 @@ export const uploadImage = async (
throw error;
}
};
// docker:
// endpoint: "unix:///var/run/docker.sock"
// exposedByDefault: false
// swarmMode: true
export const uploadImageRemoteCommand = (
application: ApplicationNested,
logPath: string,
) => {
const registry = application.registry;
if (!registry) {
throw new Error("Registry not found");
}
const { registryUrl, imagePrefix } = registry;
const { appName } = application;
const imageName = `${appName}:latest`;
const finalURL = registryUrl;
const registryTag = join(imagePrefix || "", imageName);
try {
const command = `
echo "📦 [Enabled Registry] Uploading image to '${registry.registryType}' | '${registryTag}'" >> ${logPath};
docker login ${finalURL} -u ${registry.username} -p ${registry.password} >> ${logPath} 2>> ${logPath} || {
echo "❌ DockerHub Failed" >> ${logPath};
exit 1;
}
echo "✅ DockerHub Login Success" >> ${logPath};
docker tag ${imageName} ${registryTag} >> ${logPath} 2>> ${logPath} || {
echo "❌ Error tagging image" >> ${logPath};
exit 1;
}
echo "✅ Image Tagged" >> ${logPath};
docker push ${registryTag} 2>> ${logPath} || {
echo "❌ Error pushing image" >> ${logPath};
exit 1;
}
echo "✅ Image Pushed" >> ${logPath};
`;
return command;
} catch (error) {
console.log(error);
throw error;
}
};

View File

@@ -2,7 +2,7 @@ import { db } from "@dokploy/server/db";
import { notifications } from "@dokploy/server/db/schema";
import BuildFailedEmail from "@dokploy/server/emails/emails/build-failed";
import { renderAsync } from "@react-email/components";
import { eq } from "drizzle-orm";
import { and, eq } from "drizzle-orm";
import {
sendDiscordNotification,
sendEmailNotification,
@@ -16,6 +16,7 @@ interface Props {
applicationType: string;
errorMessage: string;
buildLink: string;
adminId: string;
}
export const sendBuildErrorNotifications = async ({
@@ -24,10 +25,14 @@ export const sendBuildErrorNotifications = async ({
applicationType,
errorMessage,
buildLink,
adminId,
}: Props) => {
const date = new Date();
const notificationList = await db.query.notifications.findMany({
where: eq(notifications.appBuildError, true),
where: and(
eq(notifications.appBuildError, true),
eq(notifications.adminId, adminId),
),
with: {
email: true,
discord: true,

View File

@@ -2,7 +2,7 @@ import { db } from "@dokploy/server/db";
import { notifications } from "@dokploy/server/db/schema";
import BuildSuccessEmail from "@dokploy/server/emails/emails/build-success";
import { renderAsync } from "@react-email/components";
import { eq } from "drizzle-orm";
import { and, eq } from "drizzle-orm";
import {
sendDiscordNotification,
sendEmailNotification,
@@ -15,6 +15,7 @@ interface Props {
applicationName: string;
applicationType: string;
buildLink: string;
adminId: string;
}
export const sendBuildSuccessNotifications = async ({
@@ -22,10 +23,14 @@ export const sendBuildSuccessNotifications = async ({
applicationName,
applicationType,
buildLink,
adminId,
}: Props) => {
const date = new Date();
const notificationList = await db.query.notifications.findMany({
where: eq(notifications.appDeploy, true),
where: and(
eq(notifications.appDeploy, true),
eq(notifications.adminId, adminId),
),
with: {
email: true,
discord: true,

View File

@@ -2,7 +2,7 @@ import { db } from "@dokploy/server/db";
import { notifications } from "@dokploy/server/db/schema";
import DatabaseBackupEmail from "@dokploy/server/emails/emails/database-backup";
import { renderAsync } from "@react-email/components";
import { eq } from "drizzle-orm";
import { and, eq } from "drizzle-orm";
import {
sendDiscordNotification,
sendEmailNotification,
@@ -16,16 +16,21 @@ export const sendDatabaseBackupNotifications = async ({
databaseType,
type,
errorMessage,
adminId,
}: {
projectName: string;
applicationName: string;
databaseType: "postgres" | "mysql" | "mongodb" | "mariadb";
type: "error" | "success";
adminId: string;
errorMessage?: string;
}) => {
const date = new Date();
const notificationList = await db.query.notifications.findMany({
where: eq(notifications.databaseBackup, true),
where: and(
eq(notifications.databaseBackup, true),
eq(notifications.adminId, adminId),
),
with: {
email: true,
discord: true,

View File

@@ -2,7 +2,7 @@ import { db } from "@dokploy/server/db";
import { notifications } from "@dokploy/server/db/schema";
import DockerCleanupEmail from "@dokploy/server/emails/emails/docker-cleanup";
import { renderAsync } from "@react-email/components";
import { eq } from "drizzle-orm";
import { and, eq } from "drizzle-orm";
import {
sendDiscordNotification,
sendEmailNotification,
@@ -11,11 +11,15 @@ import {
} from "./utils";
export const sendDockerCleanupNotifications = async (
adminId: string,
message = "Docker cleanup for dokploy",
) => {
const date = new Date();
const notificationList = await db.query.notifications.findMany({
where: eq(notifications.dockerCleanup, true),
where: and(
eq(notifications.dockerCleanup, true),
eq(notifications.adminId, adminId),
),
with: {
email: true,
discord: true,