Merge branch 'canary' into 319-ability-to-roll-back-service-depoyments

This commit is contained in:
Mauricio Siu
2025-06-21 21:18:05 -06:00
51 changed files with 12957 additions and 167 deletions

View File

@@ -8,6 +8,7 @@ import { bitbucket } from "./bitbucket";
import { gitea } from "./gitea";
import { github } from "./github";
import { gitlab } from "./gitlab";
import { users_temp } from "./user";
export const gitProviderType = pgEnum("gitProviderType", [
"github",
@@ -29,6 +30,9 @@ export const gitProvider = pgTable("git_provider", {
organizationId: text("organizationId")
.notNull()
.references(() => organization.id, { onDelete: "cascade" }),
userId: text("userId")
.notNull()
.references(() => users_temp.id, { onDelete: "cascade" }),
});
export const gitProviderRelations = relations(gitProvider, ({ one }) => ({
@@ -52,6 +56,10 @@ export const gitProviderRelations = relations(gitProvider, ({ one }) => ({
fields: [gitProvider.organizationId],
references: [organization.id],
}),
user: one(users_temp, {
fields: [gitProvider.userId],
references: [users_temp.id],
}),
}));
const createSchema = createInsertSchema(gitProvider);

View File

@@ -56,7 +56,7 @@ export const users_temp = pgTable("user_temp", {
letsEncryptEmail: text("letsEncryptEmail"),
sshPrivateKey: text("sshPrivateKey"),
enableDockerCleanup: boolean("enableDockerCleanup").notNull().default(false),
logCleanupCron: text("logCleanupCron"),
logCleanupCron: text("logCleanupCron").default("0 0 * * *"),
role: text("role").notNull().default("user"),
// Metrics
enablePaidFeatures: boolean("enablePaidFeatures").notNull().default(false),

View File

@@ -18,9 +18,6 @@ const { handler, api } = betterAuth({
provider: "pg",
schema: schema,
}),
logger: {
disabled: process.env.NODE_ENV === "production",
},
appName: "Dokploy",
socialProviders: {
github: {

View File

@@ -13,6 +13,7 @@ export type Bitbucket = typeof bitbucket.$inferSelect;
export const createBitbucket = async (
input: typeof apiCreateBitbucket._type,
organizationId: string,
userId: string,
) => {
return await db.transaction(async (tx) => {
const newGitProvider = await tx
@@ -21,6 +22,7 @@ export const createBitbucket = async (
providerType: "bitbucket",
organizationId: organizationId,
name: input.name,
userId: userId,
})
.returning()
.then((response) => response[0]);

View File

@@ -0,0 +1,634 @@
// CDN Provider Interface
export interface CDNProvider {
name: string;
displayName: string;
checkIp: (ip: string) => boolean;
warningMessage: string;
}
const isIPInCIDR = (ip: string, cidr: string): boolean => {
const [network, prefixLength] = cidr.split("/");
if (!network || !prefixLength) return false;
const prefix = Number.parseInt(prefixLength, 10);
// Convert IP addresses to 32-bit integers
const ipToInt = (ipStr: string): number => {
return (
ipStr
.split(".")
.reduce((acc, octet) => (acc << 8) + Number.parseInt(octet, 10), 0) >>>
0
);
};
const ipInt = ipToInt(ip);
const networkInt = ipToInt(network);
const mask = (0xffffffff << (32 - prefix)) >>> 0;
return (ipInt & mask) === (networkInt & mask);
};
// Cloudflare IP ranges
// https://www.cloudflare.com/ips-v4
const CLOUDFLARE_IP_RANGES = [
"173.245.48.0/20",
"103.21.244.0/22",
"103.22.200.0/22",
"103.31.4.0/22",
"141.101.64.0/18",
"108.162.192.0/18",
"190.93.240.0/20",
"188.114.96.0/20",
"197.234.240.0/22",
"198.41.128.0/17",
"162.158.0.0/15",
"104.16.0.0/13",
"104.24.0.0/14",
"172.64.0.0/13",
"131.0.72.0/22",
];
// Fastly IP ranges
// https://api.fastly.com/public-ip-list
const FASTLY_IP_RANGES = [
"23.235.32.0/20",
"43.249.72.0/22",
"103.244.50.0/24",
"103.245.222.0/23",
"103.245.224.0/24",
"104.156.80.0/20",
"140.248.64.0/18",
"140.248.128.0/17",
"146.75.0.0/17",
"151.101.0.0/16",
"157.52.64.0/18",
"167.82.0.0/17",
"167.82.128.0/20",
"167.82.160.0/20",
"167.82.224.0/20",
"172.111.64.0/18",
"185.31.16.0/22",
"199.27.72.0/21",
"199.232.0.0/16",
];
// Bunny CDN IP addresses
// https://bunnycdn.com/api/system/edgeserverlist
const BUNNY_CDN_IPS = new Set([
"89.187.188.227",
"89.187.188.228",
"139.180.134.196",
"89.38.96.158",
"89.187.162.249",
"89.187.162.242",
"185.102.217.65",
"185.93.1.243",
"156.146.40.49",
"185.59.220.199",
"185.59.220.198",
"195.181.166.158",
"185.180.12.68",
"138.199.24.209",
"138.199.24.211",
"79.127.216.111",
"79.127.216.112",
"89.187.169.47",
"138.199.24.218",
"185.40.106.117",
"200.25.45.4",
"200.25.57.5",
"193.162.131.1",
"200.25.11.8",
"200.25.53.5",
"200.25.13.98",
"41.242.2.18",
"200.25.62.5",
"200.25.38.69",
"200.25.42.70",
"200.25.36.166",
"195.206.229.106",
"194.242.11.186",
"185.164.35.8",
"94.20.154.22",
"185.93.1.244",
"156.59.145.154",
"143.244.49.177",
"138.199.46.66",
"138.199.37.227",
"138.199.37.231",
"138.199.37.230",
"138.199.37.229",
"138.199.46.69",
"138.199.46.68",
"138.199.46.67",
"185.93.1.246",
"138.199.37.232",
"195.181.163.196",
"107.182.163.162",
"195.181.163.195",
"84.17.46.53",
"212.102.40.114",
"84.17.46.54",
"138.199.40.58",
"143.244.38.134",
"143.244.38.136",
"185.152.64.17",
"84.17.59.115",
"89.187.165.194",
"138.199.15.193",
"89.35.237.170",
"37.19.216.130",
"185.93.1.247",
"185.93.3.244",
"143.244.49.179",
"143.244.49.180",
"138.199.9.104",
"185.152.66.243",
"143.244.49.178",
"169.150.221.147",
"200.25.18.73",
"84.17.63.178",
"200.25.32.131",
"37.19.207.34",
"192.189.65.146",
"143.244.45.177",
"185.93.1.249",
"185.93.1.250",
"169.150.215.115",
"209.177.87.197",
"156.146.56.162",
"156.146.56.161",
"185.93.2.246",
"185.93.2.245",
"212.102.40.113",
"185.93.2.244",
"143.244.50.82",
"143.244.50.83",
"156.146.56.163",
"129.227.217.178",
"129.227.217.179",
"200.25.69.94",
"128.1.52.179",
"200.25.16.103",
"15.235.54.226",
"102.67.138.155",
"156.146.43.65",
"195.181.163.203",
"195.181.163.202",
"156.146.56.169",
"156.146.56.170",
"156.146.56.166",
"156.146.56.171",
"169.150.207.210",
"156.146.56.167",
"143.244.50.84",
"143.244.50.85",
"143.244.50.86",
"143.244.50.87",
"156.146.56.168",
"169.150.207.211",
"212.102.50.59",
"146.185.248.15",
"143.244.50.90",
"143.244.50.91",
"143.244.50.88",
"143.244.50.209",
"143.244.50.213",
"143.244.50.214",
"143.244.49.183",
"143.244.50.89",
"143.244.50.210",
"143.244.50.211",
"143.244.50.212",
"5.42.206.66",
"94.46.27.186",
"169.150.207.213",
"169.150.207.214",
"169.150.207.215",
"169.150.207.212",
"169.150.219.114",
"169.150.202.210",
"169.150.242.193",
"185.93.1.251",
"169.150.207.216",
"169.150.207.217",
"169.150.238.19",
"102.219.126.20",
"156.59.66.182",
"122.10.251.130",
"185.24.11.18",
"138.199.36.7",
"138.199.36.8",
"138.199.36.9",
"138.199.36.10",
"138.199.36.11",
"138.199.37.225",
"84.17.46.49",
"84.17.37.217",
"169.150.225.35",
"169.150.225.36",
"169.150.225.37",
"169.150.225.38",
"169.150.225.39",
"169.150.225.34",
"169.150.236.97",
"169.150.236.98",
"169.150.236.99",
"169.150.236.100",
"93.189.63.149",
"143.244.56.49",
"143.244.56.50",
"143.244.56.51",
"169.150.247.40",
"169.150.247.33",
"169.150.247.34",
"169.150.247.35",
"169.150.247.36",
"169.150.247.37",
"169.150.247.38",
"169.150.247.39",
"38.142.94.218",
"87.249.137.52",
"38.104.169.186",
"66.181.163.74",
"84.17.38.227",
"84.17.38.228",
"84.17.38.229",
"84.17.38.230",
"84.17.38.231",
"84.17.38.232",
"169.150.225.41",
"169.150.225.42",
"169.150.249.162",
"169.150.249.163",
"169.150.249.164",
"169.150.249.165",
"169.150.249.166",
"169.150.249.167",
"169.150.249.168",
"169.150.249.169",
"185.131.64.124",
"103.112.0.22",
"37.236.234.2",
"169.150.252.209",
"212.102.46.118",
"192.169.120.162",
"93.180.217.214",
"37.19.203.178",
"107.155.47.146",
"193.201.190.174",
"156.59.95.218",
"213.170.143.139",
"129.227.186.154",
"195.238.127.98",
"200.25.22.6",
"204.16.244.92",
"200.25.70.101",
"200.25.66.100",
"139.180.209.182",
"103.108.231.41",
"103.108.229.5",
"103.216.220.9",
"169.150.225.40",
"212.102.50.49",
"212.102.50.52",
"109.61.83.242",
"109.61.83.243",
"212.102.50.50",
"169.150.225.43",
"45.125.247.57",
"103.235.199.170",
"128.1.35.170",
"38.32.110.58",
"169.150.220.228",
"169.150.220.229",
"169.150.220.230",
"169.150.220.231",
"138.199.4.179",
"207.211.214.145",
"109.61.86.193",
"103.214.20.95",
"178.175.134.51",
"138.199.4.178",
"172.255.253.140",
"185.24.11.19",
"109.61.83.244",
"109.61.83.245",
"84.17.38.250",
"84.17.38.251",
"146.59.69.202",
"146.70.80.218",
"200.25.80.74",
"79.127.213.214",
"79.127.213.215",
"79.127.213.216",
"79.127.213.217",
"195.69.140.112",
"109.61.83.247",
"109.61.83.246",
"185.93.2.248",
"109.61.83.249",
"109.61.83.250",
"109.61.83.251",
"46.199.75.115",
"141.164.35.160",
"109.61.83.97",
"109.61.83.98",
"109.61.83.99",
"129.227.179.18",
"185.180.14.250",
"152.89.160.26",
"5.189.202.62",
"98.98.242.142",
"156.59.92.126",
"84.17.59.117",
"79.127.216.66",
"79.127.204.113",
"79.127.237.132",
"169.150.236.104",
"169.150.236.105",
"37.27.135.61",
"158.51.123.205",
"156.146.43.70",
"156.146.43.71",
"156.146.43.72",
"180.149.231.175",
"185.93.2.243",
"143.244.56.52",
"143.244.56.53",
"143.244.56.54",
"143.244.56.55",
"143.244.56.56",
"143.244.56.57",
"143.244.56.58",
"144.76.236.44",
"88.198.57.50",
"78.46.69.199",
"136.243.16.49",
"138.201.86.122",
"136.243.42.90",
"88.99.95.221",
"178.63.2.112",
"5.9.98.45",
"136.243.42.10",
"169.150.236.106",
"169.150.236.107",
"185.93.1.242",
"185.93.1.245",
"143.244.60.193",
"195.181.163.194",
"79.127.188.193",
"79.127.188.196",
"79.127.188.194",
"79.127.188.195",
"104.166.144.106",
"156.59.126.78",
"185.135.85.154",
"38.54.5.37",
"38.54.3.92",
"185.165.170.74",
"207.121.80.118",
"207.121.46.228",
"207.121.46.236",
"207.121.46.244",
"207.121.46.252",
"216.202.235.164",
"207.121.46.220",
"207.121.75.132",
"207.121.80.12",
"207.121.80.172",
"207.121.90.60",
"207.121.90.68",
"207.121.97.204",
"207.121.90.252",
"207.121.97.236",
"207.121.99.12",
"138.199.24.219",
"185.93.2.251",
"138.199.46.65",
"207.121.41.196",
"207.121.99.20",
"207.121.99.36",
"207.121.99.44",
"207.121.99.52",
"207.121.99.60",
"207.121.23.68",
"207.121.23.124",
"207.121.23.244",
"207.121.23.180",
"207.121.23.188",
"207.121.23.196",
"207.121.23.204",
"207.121.24.52",
"207.121.24.60",
"207.121.24.68",
"207.121.24.76",
"207.121.24.92",
"207.121.24.100",
"207.121.24.108",
"207.121.24.116",
"154.95.86.76",
"5.9.99.73",
"78.46.92.118",
"144.76.65.213",
"78.46.156.89",
"88.198.9.155",
"144.76.79.22",
"103.1.215.93",
"103.137.12.33",
"103.107.196.31",
"116.90.72.155",
"103.137.14.5",
"116.90.75.65",
"37.19.207.37",
"208.83.234.224",
"116.202.155.146",
"116.202.193.178",
"116.202.224.168",
"188.40.126.227",
"88.99.26.189",
"168.119.39.238",
"88.99.26.97",
"168.119.12.188",
"176.9.139.55",
"142.132.223.79",
"142.132.223.80",
"142.132.223.81",
"46.4.116.17",
"46.4.119.81",
"167.235.114.167",
"159.69.68.171",
"178.63.21.52",
"46.4.120.152",
"116.202.80.247",
"5.9.71.119",
"195.201.11.156",
"78.46.123.17",
"46.4.113.143",
"136.243.2.236",
"195.201.81.217",
"148.251.42.123",
"94.130.68.122",
"88.198.22.103",
"46.4.102.90",
"157.90.180.205",
"162.55.135.11",
"195.201.109.59",
"148.251.41.244",
"116.202.235.16",
"128.140.70.141",
"78.46.74.86",
"78.46.74.85",
"178.63.41.242",
"178.63.41.247",
"178.63.41.234",
"104.237.53.74",
"104.237.54.154",
"104.237.51.58",
"64.185.235.90",
"64.185.234.114",
"64.185.232.194",
"64.185.232.178",
"64.185.232.82",
"103.60.15.169",
"103.60.15.170",
"103.60.15.171",
"103.60.15.172",
"103.60.15.173",
"103.60.15.162",
"103.60.15.163",
"103.60.15.164",
"103.60.15.165",
"103.60.15.166",
"103.60.15.167",
"103.60.15.168",
"109.248.43.116",
"109.248.43.117",
"109.248.43.162",
"109.248.43.163",
"109.248.43.164",
"109.248.43.165",
"49.12.71.27",
"49.12.0.158",
"78.47.94.156",
"109.248.43.159",
"109.248.43.160",
"109.248.43.208",
"109.248.43.179",
"109.248.43.232",
"109.248.43.231",
"109.248.43.241",
"109.248.43.236",
"109.248.43.240",
"109.248.43.103",
"116.202.118.194",
"116.202.80.29",
"159.69.57.80",
"139.180.129.216",
"139.99.174.7",
"89.187.169.18",
"89.187.179.7",
"143.244.62.213",
"185.93.3.246",
"195.181.163.198",
"185.152.64.19",
"84.17.37.211",
"212.102.50.54",
"212.102.46.115",
"143.244.38.135",
"169.150.238.21",
"169.150.207.51",
"169.150.207.49",
"84.17.38.226",
"84.17.38.225",
"37.19.222.248",
"37.19.222.249",
"169.150.247.139",
"169.150.247.177",
"169.150.247.178",
"169.150.213.49",
"212.102.46.119",
"84.17.38.234",
"84.17.38.233",
"169.150.247.179",
"169.150.247.180",
"169.150.247.181",
"169.150.247.182",
"169.150.247.183",
"169.150.247.138",
"169.150.247.184",
"169.150.247.185",
"156.146.58.83",
"212.102.43.88",
"89.187.169.26",
"109.61.89.57",
"109.61.89.58",
"109.61.83.241",
"84.17.38.243",
"84.17.38.244",
"84.17.38.246",
"84.17.38.247",
"84.17.38.245",
"143.244.38.129",
"84.17.38.248",
"89.187.176.34",
"185.152.64.23",
"79.127.213.209",
"79.127.213.210",
"84.17.37.209",
"156.146.43.68",
"185.93.3.243",
"79.127.219.198",
"138.199.33.57",
"79.127.242.89",
"138.199.4.136",
"169.150.220.235",
"138.199.4.129",
"138.199.4.177",
"37.19.222.34",
"46.151.193.85",
"212.104.158.17",
"212.104.158.18",
"212.104.158.19",
"212.104.158.20",
"212.104.158.21",
"212.104.158.22",
"212.104.158.24",
"212.104.158.26",
"79.127.237.134",
"89.187.184.177",
"89.187.184.179",
"89.187.184.173",
"89.187.184.178",
"89.187.184.176",
]);
const CDN_PROVIDERS: CDNProvider[] = [
{
name: "cloudflare",
displayName: "Cloudflare",
checkIp: (ip: string) =>
CLOUDFLARE_IP_RANGES.some((range) => isIPInCIDR(ip, range)),
warningMessage:
"Domain is behind Cloudflare - actual IP is masked by Cloudflare proxy",
},
{
name: "bunnycdn",
displayName: "Bunny CDN",
checkIp: (ip: string) => BUNNY_CDN_IPS.has(ip),
warningMessage:
"Domain is behind Bunny CDN - actual IP is masked by CDN proxy",
},
{
name: "fastly",
displayName: "Fastly",
checkIp: (ip: string) =>
FASTLY_IP_RANGES.some((range) => isIPInCIDR(ip, range)),
warningMessage:
"Domain is behind Fastly - actual IP is masked by CDN proxy",
},
];
export const detectCDNProvider = (ip: string): CDNProvider | null => {
return CDN_PROVIDERS.find((provider) => provider.checkIp(ip)) || null;
};

View File

@@ -8,6 +8,7 @@ import { eq } from "drizzle-orm";
import { type apiCreateDomain, domains } from "../db/schema";
import { findUserById } from "./admin";
import { findApplicationById } from "./application";
import { detectCDNProvider } from "./cdn";
import { findServerById } from "./server";
export type Domain = typeof domains.$inferSelect;
@@ -142,28 +143,6 @@ export const getDomainHost = (domain: Domain) => {
const resolveDns = promisify(dns.resolve4);
// Cloudflare IP ranges (simplified - these are some common ones)
const CLOUDFLARE_IPS = [
"172.67.",
"104.21.",
"104.16.",
"104.17.",
"104.18.",
"104.19.",
"104.20.",
"104.22.",
"104.23.",
"104.24.",
"104.25.",
"104.26.",
"104.27.",
"104.28.",
];
const isCloudflareIp = (ip: string) => {
return CLOUDFLARE_IPS.some((range) => ip.startsWith(range));
};
export const validateDomain = async (
domain: string,
expectedIp?: string,
@@ -172,6 +151,7 @@ export const validateDomain = async (
resolvedIp?: string;
error?: string;
isCloudflare?: boolean;
cdnProvider?: string;
}> => {
try {
// Remove protocol and path if present
@@ -182,17 +162,18 @@ export const validateDomain = async (
const resolvedIps = ips.map((ip) => ip.toString());
// Check if it's a Cloudflare IP
const behindCloudflare = ips.some((ip) => isCloudflareIp(ip));
// Check if any IP belongs to a CDN provider
const cdnProvider = ips
.map((ip) => detectCDNProvider(ip))
.find((provider) => provider !== null);
// If behind Cloudflare, we consider it valid but inform the user
if (behindCloudflare) {
// If behind a CDN, we consider it valid but inform the user
if (cdnProvider) {
return {
isValid: true,
resolvedIp: resolvedIps.join(", "),
isCloudflare: true,
error:
"Domain is behind Cloudflare - actual IP is masked by Cloudflare proxy",
cdnProvider: cdnProvider.displayName,
error: cdnProvider.warningMessage,
};
}

View File

@@ -12,6 +12,7 @@ export type Gitea = typeof gitea.$inferSelect;
export const createGitea = async (
input: typeof apiCreateGitea._type,
organizationId: string,
userId: string,
) => {
return await db.transaction(async (tx) => {
const newGitProvider = await tx
@@ -20,6 +21,7 @@ export const createGitea = async (
providerType: "gitea",
organizationId: organizationId,
name: input.name,
userId: userId,
})
.returning()
.then((response) => response[0]);

View File

@@ -13,6 +13,7 @@ export type Github = typeof github.$inferSelect;
export const createGithub = async (
input: typeof apiCreateGithub._type,
organizationId: string,
userId: string,
) => {
return await db.transaction(async (tx) => {
const newGitProvider = await tx
@@ -21,6 +22,7 @@ export const createGithub = async (
providerType: "github",
organizationId: organizationId,
name: input.name,
userId: userId,
})
.returning()
.then((response) => response[0]);

View File

@@ -12,6 +12,7 @@ export type Gitlab = typeof gitlab.$inferSelect;
export const createGitlab = async (
input: typeof apiCreateGitlab._type,
organizationId: string,
userId: string,
) => {
return await db.transaction(async (tx) => {
const newGitProvider = await tx
@@ -20,6 +21,7 @@ export const createGitlab = async (
providerType: "gitlab",
organizationId: organizationId,
name: input.name,
userId: userId,
})
.returning()
.then((response) => response[0]);

View File

@@ -37,7 +37,8 @@ export const startLogCleanup = async (
}
return true;
} catch (_) {
} catch (error) {
console.error("Error starting log cleanup:", error);
return false;
}
};

View File

@@ -88,6 +88,7 @@ export const initCronJobs = async () => {
}
if (admin?.user.logCleanupCron) {
console.log("Starting log requests cleanup", admin.user.logCleanupCron);
await startLogCleanup(admin.user.logCleanupCron);
}
};

View File

@@ -73,7 +73,7 @@ export const cloneBitbucketRepository = async (
});
writeStream.write(`\nCloned ${repoclone} to ${outputPath}: ✅\n`);
} catch (error) {
writeStream.write(`ERROR Clonning: ${error}: ❌`);
writeStream.write(`ERROR Cloning: ${error}: ❌`);
throw error;
} finally {
writeStream.end();

View File

@@ -155,7 +155,7 @@ export const getGiteaCloneCommand = async (
const cloneCommand = `
rm -rf ${outputPath};
mkdir -p ${outputPath};
if ! git clone --branch ${giteaBranch} --depth 1 ${enableSubmodules ? "--recurse-submodules" : ""} ${cloneUrl} ${outputPath} >> ${logPath} 2>&1; then
echo "❌ [ERROR] Failed to clone the repository ${repoClone}" >> ${logPath};
exit 1;
@@ -232,7 +232,7 @@ export const cloneGiteaRepository = async (
);
writeStream.write(`\nCloned ${repoClone}: ✅\n`);
} catch (error) {
writeStream.write(`ERROR Clonning: ${error}: ❌`);
writeStream.write(`ERROR Cloning: ${error}: ❌`);
throw error;
} finally {
writeStream.end();

View File

@@ -149,7 +149,7 @@ export const cloneGithubRepository = async ({
});
writeStream.write(`\nCloned ${repoclone}: ✅\n`);
} catch (error) {
writeStream.write(`ERROR Clonning: ${error}: ❌`);
writeStream.write(`ERROR Cloning: ${error}: ❌`);
throw error;
} finally {
writeStream.end();

View File

@@ -132,7 +132,7 @@ export const cloneGitlabRepository = async (
const cloneUrl = `https://oauth2:${gitlab?.accessToken}@${repoclone}`;
try {
writeStream.write(`\nClonning Repo ${repoclone} to ${outputPath}: ✅\n`);
writeStream.write(`\nCloning Repo ${repoclone} to ${outputPath}: ✅\n`);
const cloneArgs = [
"clone",
"--branch",
@@ -152,7 +152,7 @@ export const cloneGitlabRepository = async (
});
writeStream.write(`\nCloned ${repoclone}: ✅\n`);
} catch (error) {
writeStream.write(`ERROR Clonning: ${error}: ❌`);
writeStream.write(`ERROR Cloning: ${error}: ❌`);
throw error;
} finally {
writeStream.end();