mirror of
https://github.com/Dokploy/dokploy.git
synced 2026-06-19 22:25:22 +02:00
refactor: update project and API components to utilize environment context for organization authorization checks and enhance service retrieval methods
This commit is contained in:
@@ -187,23 +187,28 @@ export const ShowProjects = () => {
|
||||
)}
|
||||
<div className="w-full grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 2xl:grid-cols-4 3xl:grid-cols-5 flex-wrap gap-5">
|
||||
{filteredProjects?.map((project) => {
|
||||
// const emptyServices =
|
||||
// project?.environments.length === 0 &&
|
||||
// project?.mongo.length === 0 &&
|
||||
// project?.environments.mysql.length === 0 &&
|
||||
// project?.environments.postgres.length === 0 &&
|
||||
// project?.environments.redis.length === 0 &&
|
||||
// project?.applications.length === 0 &&
|
||||
// project?.compose.length === 0;
|
||||
const emptyServices =
|
||||
project?.environments.map((env) => env.applications.length === 0 &&
|
||||
env.mariadb.length === 0 &&
|
||||
env.mongo.length === 0 &&
|
||||
env.mysql.length === 0 &&
|
||||
env.postgres.length === 0 &&
|
||||
env.redis.length === 0 &&
|
||||
env.applications.length === 0 &&
|
||||
env.compose.length === 0).every(Boolean);
|
||||
|
||||
// const totalServices =
|
||||
// project?.mariadb.length +
|
||||
// project?.mongo.length +
|
||||
// project?.mysql.length +
|
||||
// project?.postgres.length +
|
||||
// project?.redis.length +
|
||||
// project?.applications.length +
|
||||
// project?.compose.length;
|
||||
const totalServices =
|
||||
project?.environments.map((env) => env.mariadb.length +
|
||||
env.mongo.length +
|
||||
env.mysql.length +
|
||||
env.postgres.length +
|
||||
env.redis.length +
|
||||
env.applications.length +
|
||||
env.compose.length ).reduce((acc, curr) => acc + curr, 0);
|
||||
|
||||
const haveServicesWithDomains =
|
||||
project?.environments.map((env) => env.applications.length > 0 ||
|
||||
env.compose.length > 0).some(Boolean);
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -214,8 +219,7 @@ export const ShowProjects = () => {
|
||||
href={`/dashboard/project/${project.projectId}`}
|
||||
>
|
||||
<Card className="group relative w-full h-full bg-transparent transition-colors hover:bg-border">
|
||||
{/* {project.applications.length > 0 ||
|
||||
project.compose.length > 0 ? (
|
||||
{haveServicesWithDomains ? (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
@@ -230,44 +234,47 @@ export const ShowProjects = () => {
|
||||
className="w-[200px] space-y-2 overflow-y-auto max-h-[400px]"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
{project.applications.length > 0 && (
|
||||
{project.environments.some((env) => env.applications.length > 0) && (
|
||||
<DropdownMenuGroup>
|
||||
<DropdownMenuLabel>
|
||||
Applications
|
||||
</DropdownMenuLabel>
|
||||
{project.applications.map((app) => (
|
||||
<div key={app.applicationId}>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuGroup>
|
||||
<DropdownMenuLabel className="font-normal capitalize text-xs flex items-center justify-between">
|
||||
{app.name}
|
||||
<StatusTooltip
|
||||
status={app.applicationStatus}
|
||||
/>
|
||||
</DropdownMenuLabel>
|
||||
{project.environments.map((env) =>
|
||||
env.applications.map((app) => (
|
||||
<div key={app.applicationId}>
|
||||
<DropdownMenuSeparator />
|
||||
{app.domains.map((domain) => (
|
||||
<DropdownMenuItem
|
||||
key={domain.domainId}
|
||||
asChild
|
||||
>
|
||||
<Link
|
||||
className="space-x-4 text-xs cursor-pointer justify-between"
|
||||
target="_blank"
|
||||
href={`${domain.https ? "https" : "http"}://${domain.host}${domain.path}`}
|
||||
<DropdownMenuGroup>
|
||||
<DropdownMenuLabel className="font-normal capitalize text-xs flex items-center justify-between">
|
||||
{app.name}
|
||||
<StatusTooltip
|
||||
status={app.applicationStatus}
|
||||
/>
|
||||
</DropdownMenuLabel>
|
||||
<DropdownMenuSeparator />
|
||||
{app.domains.map((domain) => (
|
||||
<DropdownMenuItem
|
||||
key={domain.domainId}
|
||||
asChild
|
||||
>
|
||||
<span className="truncate">
|
||||
{domain.host}
|
||||
</span>
|
||||
<ExternalLinkIcon className="size-4 shrink-0" />
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
))}
|
||||
</DropdownMenuGroup>
|
||||
</div>
|
||||
))}
|
||||
<Link
|
||||
className="space-x-4 text-xs cursor-pointer justify-between"
|
||||
target="_blank"
|
||||
href={`${domain.https ? "https" : "http"}://${domain.host}${domain.path}`}
|
||||
>
|
||||
<span className="truncate">
|
||||
{domain.host}
|
||||
</span>
|
||||
<ExternalLinkIcon className="size-4 shrink-0" />
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
))}
|
||||
</DropdownMenuGroup>
|
||||
</div>
|
||||
))
|
||||
)}
|
||||
</DropdownMenuGroup>
|
||||
)}
|
||||
{/*
|
||||
{project.compose.length > 0 && (
|
||||
<DropdownMenuGroup>
|
||||
<DropdownMenuLabel>
|
||||
@@ -305,10 +312,10 @@ export const ShowProjects = () => {
|
||||
</div>
|
||||
))}
|
||||
</DropdownMenuGroup>
|
||||
)}
|
||||
)} */}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
) : null} */}
|
||||
) : null}
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center justify-between gap-2">
|
||||
<span className="flex flex-col gap-1.5">
|
||||
@@ -379,7 +386,7 @@ export const ShowProjects = () => {
|
||||
Are you sure to delete this
|
||||
project?
|
||||
</AlertDialogTitle>
|
||||
{/* {!emptyServices ? (
|
||||
{!emptyServices ? (
|
||||
<div className="flex flex-row gap-4 rounded-lg bg-yellow-50 p-2 dark:bg-yellow-950">
|
||||
<AlertTriangle className="text-yellow-600 dark:text-yellow-400" />
|
||||
<span className="text-sm text-yellow-600 dark:text-yellow-400">
|
||||
@@ -393,14 +400,14 @@ export const ShowProjects = () => {
|
||||
This action cannot be
|
||||
undone
|
||||
</AlertDialogDescription>
|
||||
)} */}
|
||||
)}
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel>
|
||||
Cancel
|
||||
</AlertDialogCancel>
|
||||
<AlertDialogAction
|
||||
// disabled={!emptyServices}
|
||||
disabled={!emptyServices}
|
||||
onClick={async () => {
|
||||
await mutateAsync({
|
||||
projectId:
|
||||
@@ -438,12 +445,12 @@ export const ShowProjects = () => {
|
||||
<DateTooltip date={project.createdAt}>
|
||||
Created
|
||||
</DateTooltip>
|
||||
{/* <span>
|
||||
<span>
|
||||
{totalServices}{" "}
|
||||
{totalServices === 1
|
||||
? "service"
|
||||
: "services"}
|
||||
</span> */}
|
||||
</span>
|
||||
</div>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
|
||||
@@ -34,7 +34,7 @@ export const domainRouter = createTRPCRouter({
|
||||
if (input.domainType === "compose" && input.composeId) {
|
||||
const compose = await findComposeById(input.composeId);
|
||||
if (
|
||||
compose.project.organizationId !== ctx.session.activeOrganizationId
|
||||
compose.environment.project.organizationId !== ctx.session.activeOrganizationId
|
||||
) {
|
||||
throw new TRPCError({
|
||||
code: "UNAUTHORIZED",
|
||||
@@ -44,7 +44,7 @@ export const domainRouter = createTRPCRouter({
|
||||
} else if (input.domainType === "application" && input.applicationId) {
|
||||
const application = await findApplicationById(input.applicationId);
|
||||
if (
|
||||
application.project.organizationId !==
|
||||
application.environment.project.organizationId !==
|
||||
ctx.session.activeOrganizationId
|
||||
) {
|
||||
throw new TRPCError({
|
||||
@@ -70,7 +70,7 @@ export const domainRouter = createTRPCRouter({
|
||||
.query(async ({ input, ctx }) => {
|
||||
const application = await findApplicationById(input.applicationId);
|
||||
if (
|
||||
application.project.organizationId !== ctx.session.activeOrganizationId
|
||||
application.environment.project.organizationId !== ctx.session.activeOrganizationId
|
||||
) {
|
||||
throw new TRPCError({
|
||||
code: "UNAUTHORIZED",
|
||||
@@ -83,7 +83,7 @@ export const domainRouter = createTRPCRouter({
|
||||
.input(apiFindCompose)
|
||||
.query(async ({ input, ctx }) => {
|
||||
const compose = await findComposeById(input.composeId);
|
||||
if (compose.project.organizationId !== ctx.session.activeOrganizationId) {
|
||||
if (compose.environment.project.organizationId !== ctx.session.activeOrganizationId) {
|
||||
throw new TRPCError({
|
||||
code: "UNAUTHORIZED",
|
||||
message: "You are not authorized to access this compose",
|
||||
@@ -122,7 +122,7 @@ export const domainRouter = createTRPCRouter({
|
||||
if (currentDomain.applicationId) {
|
||||
const newApp = await findApplicationById(currentDomain.applicationId);
|
||||
if (
|
||||
newApp.project.organizationId !== ctx.session.activeOrganizationId
|
||||
newApp.environment.project.organizationId !== ctx.session.activeOrganizationId
|
||||
) {
|
||||
throw new TRPCError({
|
||||
code: "UNAUTHORIZED",
|
||||
@@ -132,7 +132,7 @@ export const domainRouter = createTRPCRouter({
|
||||
} else if (currentDomain.composeId) {
|
||||
const newCompose = await findComposeById(currentDomain.composeId);
|
||||
if (
|
||||
newCompose.project.organizationId !== ctx.session.activeOrganizationId
|
||||
newCompose.environment.project.organizationId !== ctx.session.activeOrganizationId
|
||||
) {
|
||||
throw new TRPCError({
|
||||
code: "UNAUTHORIZED",
|
||||
@@ -144,7 +144,7 @@ export const domainRouter = createTRPCRouter({
|
||||
currentDomain.previewDeploymentId,
|
||||
);
|
||||
if (
|
||||
newPreviewDeployment.application.project.organizationId !==
|
||||
newPreviewDeployment.application.environment.project.organizationId !==
|
||||
ctx.session.activeOrganizationId
|
||||
) {
|
||||
throw new TRPCError({
|
||||
@@ -175,7 +175,7 @@ export const domainRouter = createTRPCRouter({
|
||||
if (domain.applicationId) {
|
||||
const application = await findApplicationById(domain.applicationId);
|
||||
if (
|
||||
application.project.organizationId !== ctx.session.activeOrganizationId
|
||||
application.environment.project.organizationId !== ctx.session.activeOrganizationId
|
||||
) {
|
||||
throw new TRPCError({
|
||||
code: "UNAUTHORIZED",
|
||||
@@ -184,7 +184,7 @@ export const domainRouter = createTRPCRouter({
|
||||
}
|
||||
} else if (domain.composeId) {
|
||||
const compose = await findComposeById(domain.composeId);
|
||||
if (compose.project.organizationId !== ctx.session.activeOrganizationId) {
|
||||
if (compose.environment.project.organizationId !== ctx.session.activeOrganizationId) {
|
||||
throw new TRPCError({
|
||||
code: "UNAUTHORIZED",
|
||||
message: "You are not authorized to access this compose",
|
||||
@@ -200,7 +200,7 @@ export const domainRouter = createTRPCRouter({
|
||||
if (domain.applicationId) {
|
||||
const application = await findApplicationById(domain.applicationId);
|
||||
if (
|
||||
application.project.organizationId !==
|
||||
application.environment.project.organizationId !==
|
||||
ctx.session.activeOrganizationId
|
||||
) {
|
||||
throw new TRPCError({
|
||||
@@ -211,7 +211,7 @@ export const domainRouter = createTRPCRouter({
|
||||
} else if (domain.composeId) {
|
||||
const compose = await findComposeById(domain.composeId);
|
||||
if (
|
||||
compose.project.organizationId !== ctx.session.activeOrganizationId
|
||||
compose.environment.project.organizationId !== ctx.session.activeOrganizationId
|
||||
) {
|
||||
throw new TRPCError({
|
||||
code: "UNAUTHORIZED",
|
||||
|
||||
@@ -15,7 +15,7 @@ export const previewDeploymentRouter = createTRPCRouter({
|
||||
.query(async ({ input, ctx }) => {
|
||||
const application = await findApplicationById(input.applicationId);
|
||||
if (
|
||||
application.project.organizationId !== ctx.session.activeOrganizationId
|
||||
application.environment.project.organizationId !== ctx.session.activeOrganizationId
|
||||
) {
|
||||
throw new TRPCError({
|
||||
code: "UNAUTHORIZED",
|
||||
@@ -31,7 +31,7 @@ export const previewDeploymentRouter = createTRPCRouter({
|
||||
input.previewDeploymentId,
|
||||
);
|
||||
if (
|
||||
previewDeployment.application.project.organizationId !==
|
||||
previewDeployment.application.environment.project.organizationId !==
|
||||
ctx.session.activeOrganizationId
|
||||
) {
|
||||
throw new TRPCError({
|
||||
@@ -49,7 +49,7 @@ export const previewDeploymentRouter = createTRPCRouter({
|
||||
input.previewDeploymentId,
|
||||
);
|
||||
if (
|
||||
previewDeployment.application.project.organizationId !==
|
||||
previewDeployment.application.environment.project.organizationId !==
|
||||
ctx.session.activeOrganizationId
|
||||
) {
|
||||
throw new TRPCError({
|
||||
|
||||
@@ -31,7 +31,11 @@ export const findPreviewDeploymentById = async (
|
||||
application: {
|
||||
with: {
|
||||
server: true,
|
||||
project: true,
|
||||
environment: {
|
||||
with: {
|
||||
project: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -45,36 +49,7 @@ export const findPreviewDeploymentById = async (
|
||||
return application;
|
||||
};
|
||||
|
||||
export const findApplicationByPreview = async (applicationId: string) => {
|
||||
const application = await db.query.applications.findFirst({
|
||||
with: {
|
||||
previewDeployments: {
|
||||
where: eq(previewDeployments.applicationId, applicationId),
|
||||
},
|
||||
project: true,
|
||||
domains: true,
|
||||
deployments: true,
|
||||
mounts: true,
|
||||
redirects: true,
|
||||
security: true,
|
||||
ports: true,
|
||||
registry: true,
|
||||
gitlab: true,
|
||||
github: true,
|
||||
bitbucket: true,
|
||||
gitea: true,
|
||||
server: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!application) {
|
||||
throw new TRPCError({
|
||||
code: "NOT_FOUND",
|
||||
message: "Application not found",
|
||||
});
|
||||
}
|
||||
return application;
|
||||
};
|
||||
|
||||
export const removePreviewDeployment = async (previewDeploymentId: string) => {
|
||||
try {
|
||||
@@ -163,7 +138,7 @@ export const createPreviewDeployment = async (
|
||||
const appName = `preview-${application.appName}-${generatePassword(6)}`;
|
||||
|
||||
const org = await db.query.organization.findFirst({
|
||||
where: eq(organization.id, application.project.organizationId),
|
||||
where: eq(organization.id, application.environment.project.organizationId),
|
||||
});
|
||||
const generateDomain = await generateWildcardDomain(
|
||||
application.previewWildcard || "*.traefik.me",
|
||||
|
||||
Reference in New Issue
Block a user