Merge branch 'canary' into feat-lark-webhook

This commit is contained in:
Mauricio Siu
2025-10-25 12:09:04 -06:00
99 changed files with 34859 additions and 468 deletions

View File

@@ -473,6 +473,7 @@ export const deployPreviewApplication = async ({
application.appName = previewDeployment.appName;
application.env = `${application.previewEnv}\nDOKPLOY_DEPLOY_URL=${previewDeployment?.domain?.host}`;
application.buildArgs = application.previewBuildArgs;
application.buildSecrets = application.previewBuildSecrets;
if (application.sourceType === "github") {
await cloneGithubRepository({
@@ -580,6 +581,7 @@ export const deployRemotePreviewApplication = async ({
application.appName = previewDeployment.appName;
application.env = `${application.previewEnv}\nDOKPLOY_DEPLOY_URL=${previewDeployment?.domain?.host}`;
application.buildArgs = application.previewBuildArgs;
application.buildSecrets = application.previewBuildSecrets;
if (application.serverId) {
let command = "set -e;";

View File

@@ -603,6 +603,21 @@ const BUNNY_CDN_IPS = new Set([
"89.187.184.176",
]);
// Arvancloud IP ranges
// https://www.arvancloud.ir/fa/ips.txt
const ARVANCLOUD_IP_RANGES = [
"185.143.232.0/22",
"188.229.116.16/29",
"94.101.182.0/27",
"2.144.3.128/28",
"89.45.48.64/28",
"37.32.16.0/27",
"37.32.17.0/27",
"37.32.18.0/27",
"37.32.19.0/27",
"185.215.232.0/22",
];
const CDN_PROVIDERS: CDNProvider[] = [
{
name: "cloudflare",
@@ -627,6 +642,14 @@ const CDN_PROVIDERS: CDNProvider[] = [
warningMessage:
"Domain is behind Fastly - actual IP is masked by CDN proxy",
},
{
name: "arvancloud",
displayName: "Arvancloud",
checkIp: (ip: string) =>
ARVANCLOUD_IP_RANGES.some((range) => isIPInCIDR(ip, range)),
warningMessage:
"Domain is behind Arvancloud - actual IP is masked by CDN proxy",
},
];
export const detectCDNProvider = (ip: string): CDNProvider | null => {

View File

@@ -227,7 +227,7 @@ export const deployCompose = async ({
const buildLink = `${await getDokployUrl()}/dashboard/project/${
compose.environment.projectId
}/services/compose/${compose.composeId}?tab=deployments`;
}/environment/${compose.environmentId}/services/compose/${compose.composeId}?tab=deployments`;
const deployment = await createDeploymentCompose({
composeId: composeId,
title: titleLog,
@@ -335,7 +335,7 @@ export const deployRemoteCompose = async ({
const buildLink = `${await getDokployUrl()}/dashboard/project/${
compose.environment.projectId
}/services/compose/${compose.composeId}?tab=deployments`;
}/environment/${compose.environmentId}/services/compose/${compose.composeId}?tab=deployments`;
const deployment = await createDeploymentCompose({
composeId: composeId,
title: titleLog,

View File

@@ -163,6 +163,24 @@ export const canPerformAccessEnvironment = async (
return false;
};
export const canPerformDeleteEnvironment = async (
userId: string,
projectId: string,
organizationId: string,
) => {
const { accessedProjects, canDeleteEnvironments } = await findMemberById(
userId,
organizationId,
);
const haveAccessToProject = accessedProjects.includes(projectId);
if (canDeleteEnvironments && haveAccessToProject) {
return true;
}
return false;
};
export const canAccessToTraefikFiles = async (
userId: string,
organizationId: string,
@@ -240,6 +258,42 @@ export const checkEnvironmentAccess = async (
}
};
export const checkEnvironmentDeletionPermission = async (
userId: string,
projectId: string,
organizationId: string,
) => {
const member = await findMemberById(userId, organizationId);
if (!member) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "User not found in organization",
});
}
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",
});
}
const hasProjectAccess = member.accessedProjects.includes(projectId);
if (!hasProjectAccess) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You don't have access to this project",
});
}
return true;
};
export const checkProjectAccess = async (
authId: string,
action: "create" | "delete" | "access",
@@ -272,6 +326,46 @@ export const checkProjectAccess = async (
}
};
export const checkEnvironmentCreationPermission = async (
userId: string,
projectId: string,
organizationId: string,
) => {
// Get user's member record
const member = await findMemberById(userId, organizationId);
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;
}
// 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",
});
}
return true;
};
export const findMemberById = async (
userId: string,
organizationId: string,