mirror of
https://github.com/Dokploy/dokploy.git
synced 2026-06-15 20:25:23 +02:00
Merge pull request #3846 from Dokploy/feat/add-more-endpoints-for-search
feat: add search functionality across multiple routers with member ac…
This commit is contained in:
@@ -7,6 +7,7 @@ import {
|
|||||||
findApplicationById,
|
findApplicationById,
|
||||||
findEnvironmentById,
|
findEnvironmentById,
|
||||||
findGitProviderById,
|
findGitProviderById,
|
||||||
|
findMemberById,
|
||||||
findProjectById,
|
findProjectById,
|
||||||
getApplicationStats,
|
getApplicationStats,
|
||||||
IS_CLOUD,
|
IS_CLOUD,
|
||||||
@@ -32,7 +33,7 @@ import {
|
|||||||
} from "@dokploy/server";
|
} from "@dokploy/server";
|
||||||
import { db } from "@dokploy/server/db";
|
import { db } from "@dokploy/server/db";
|
||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
import { eq } from "drizzle-orm";
|
import { and, desc, eq, ilike, or, sql } from "drizzle-orm";
|
||||||
import { nanoid } from "nanoid";
|
import { nanoid } from "nanoid";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { createTRPCRouter, protectedProcedure } from "@/server/api/trpc";
|
import { createTRPCRouter, protectedProcedure } from "@/server/api/trpc";
|
||||||
@@ -53,6 +54,8 @@ import {
|
|||||||
apiSaveGitProvider,
|
apiSaveGitProvider,
|
||||||
apiUpdateApplication,
|
apiUpdateApplication,
|
||||||
applications,
|
applications,
|
||||||
|
environments,
|
||||||
|
projects,
|
||||||
} from "@/server/db/schema";
|
} from "@/server/db/schema";
|
||||||
import { deploymentWorker } from "@/server/queues/deployments-queue";
|
import { deploymentWorker } from "@/server/queues/deployments-queue";
|
||||||
import type { DeploymentJob } from "@/server/queues/queue-types";
|
import type { DeploymentJob } from "@/server/queues/queue-types";
|
||||||
@@ -1002,4 +1005,138 @@ export const applicationRouter = createTRPCRouter({
|
|||||||
message: "Deployment cancellation only available in cloud version",
|
message: "Deployment cancellation only available in cloud version",
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
search: protectedProcedure
|
||||||
|
.input(
|
||||||
|
z.object({
|
||||||
|
q: z.string().optional(),
|
||||||
|
name: z.string().optional(),
|
||||||
|
appName: z.string().optional(),
|
||||||
|
description: z.string().optional(),
|
||||||
|
repository: z.string().optional(),
|
||||||
|
owner: z.string().optional(),
|
||||||
|
dockerImage: z.string().optional(),
|
||||||
|
projectId: z.string().optional(),
|
||||||
|
environmentId: z.string().optional(),
|
||||||
|
limit: z.number().min(1).max(100).default(20),
|
||||||
|
offset: z.number().min(0).default(0),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.query(async ({ ctx, input }) => {
|
||||||
|
const baseConditions = [
|
||||||
|
eq(projects.organizationId, ctx.session.activeOrganizationId),
|
||||||
|
];
|
||||||
|
|
||||||
|
if (input.projectId) {
|
||||||
|
baseConditions.push(eq(environments.projectId, input.projectId));
|
||||||
|
}
|
||||||
|
if (input.environmentId) {
|
||||||
|
baseConditions.push(
|
||||||
|
eq(applications.environmentId, input.environmentId),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.q?.trim()) {
|
||||||
|
const term = `%${input.q.trim()}%`;
|
||||||
|
baseConditions.push(
|
||||||
|
or(
|
||||||
|
ilike(applications.name, term),
|
||||||
|
ilike(applications.appName, term),
|
||||||
|
ilike(applications.description ?? "", term),
|
||||||
|
ilike(applications.repository ?? "", term),
|
||||||
|
ilike(applications.owner ?? "", term),
|
||||||
|
ilike(applications.dockerImage ?? "", term),
|
||||||
|
)!,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.name?.trim()) {
|
||||||
|
baseConditions.push(ilike(applications.name, `%${input.name.trim()}%`));
|
||||||
|
}
|
||||||
|
if (input.appName?.trim()) {
|
||||||
|
baseConditions.push(
|
||||||
|
ilike(applications.appName, `%${input.appName.trim()}%`),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (input.description?.trim()) {
|
||||||
|
baseConditions.push(
|
||||||
|
ilike(
|
||||||
|
applications.description ?? "",
|
||||||
|
`%${input.description.trim()}%`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (input.repository?.trim()) {
|
||||||
|
baseConditions.push(
|
||||||
|
ilike(applications.repository ?? "", `%${input.repository.trim()}%`),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (input.owner?.trim()) {
|
||||||
|
baseConditions.push(
|
||||||
|
ilike(applications.owner ?? "", `%${input.owner.trim()}%`),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (input.dockerImage?.trim()) {
|
||||||
|
baseConditions.push(
|
||||||
|
ilike(
|
||||||
|
applications.dockerImage ?? "",
|
||||||
|
`%${input.dockerImage.trim()}%`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx.user.role === "member") {
|
||||||
|
const { accessedServices } = await findMemberById(
|
||||||
|
ctx.user.id,
|
||||||
|
ctx.session.activeOrganizationId,
|
||||||
|
);
|
||||||
|
if (accessedServices.length === 0) return { items: [], total: 0 };
|
||||||
|
baseConditions.push(
|
||||||
|
sql`${applications.applicationId} IN (${sql.join(
|
||||||
|
accessedServices.map((id) => sql`${id}`),
|
||||||
|
sql`, `,
|
||||||
|
)})`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const where = and(...baseConditions);
|
||||||
|
|
||||||
|
const [items, countResult] = await Promise.all([
|
||||||
|
db
|
||||||
|
.select({
|
||||||
|
applicationId: applications.applicationId,
|
||||||
|
name: applications.name,
|
||||||
|
appName: applications.appName,
|
||||||
|
description: applications.description,
|
||||||
|
environmentId: applications.environmentId,
|
||||||
|
applicationStatus: applications.applicationStatus,
|
||||||
|
sourceType: applications.sourceType,
|
||||||
|
createdAt: applications.createdAt,
|
||||||
|
})
|
||||||
|
.from(applications)
|
||||||
|
.innerJoin(
|
||||||
|
environments,
|
||||||
|
eq(applications.environmentId, environments.environmentId),
|
||||||
|
)
|
||||||
|
.innerJoin(projects, eq(environments.projectId, projects.projectId))
|
||||||
|
.where(where)
|
||||||
|
.orderBy(desc(applications.createdAt))
|
||||||
|
.limit(input.limit)
|
||||||
|
.offset(input.offset),
|
||||||
|
db
|
||||||
|
.select({ count: sql<number>`count(*)::int` })
|
||||||
|
.from(applications)
|
||||||
|
.innerJoin(
|
||||||
|
environments,
|
||||||
|
eq(applications.environmentId, environments.environmentId),
|
||||||
|
)
|
||||||
|
.innerJoin(projects, eq(environments.projectId, projects.projectId))
|
||||||
|
.where(where),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
items,
|
||||||
|
total: countResult[0]?.count ?? 0,
|
||||||
|
};
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import {
|
|||||||
findDomainsByComposeId,
|
findDomainsByComposeId,
|
||||||
findEnvironmentById,
|
findEnvironmentById,
|
||||||
findGitProviderById,
|
findGitProviderById,
|
||||||
|
findMemberById,
|
||||||
findProjectById,
|
findProjectById,
|
||||||
findServerById,
|
findServerById,
|
||||||
getComposeContainer,
|
getComposeContainer,
|
||||||
@@ -41,7 +42,7 @@ import {
|
|||||||
} from "@dokploy/server/templates/github";
|
} from "@dokploy/server/templates/github";
|
||||||
import { processTemplate } from "@dokploy/server/templates/processors";
|
import { processTemplate } from "@dokploy/server/templates/processors";
|
||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
import { eq } from "drizzle-orm";
|
import { and, desc, eq, ilike, or, sql } from "drizzle-orm";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import { nanoid } from "nanoid";
|
import { nanoid } from "nanoid";
|
||||||
import { parse } from "toml";
|
import { parse } from "toml";
|
||||||
@@ -58,6 +59,8 @@ import {
|
|||||||
apiRedeployCompose,
|
apiRedeployCompose,
|
||||||
apiUpdateCompose,
|
apiUpdateCompose,
|
||||||
compose as composeTable,
|
compose as composeTable,
|
||||||
|
environments,
|
||||||
|
projects,
|
||||||
} from "@/server/db/schema";
|
} from "@/server/db/schema";
|
||||||
import { deploymentWorker } from "@/server/queues/deployments-queue";
|
import { deploymentWorker } from "@/server/queues/deployments-queue";
|
||||||
import type { DeploymentJob } from "@/server/queues/queue-types";
|
import type { DeploymentJob } from "@/server/queues/queue-types";
|
||||||
@@ -1054,4 +1057,114 @@ export const composeRouter = createTRPCRouter({
|
|||||||
message: "Deployment cancellation only available in cloud version",
|
message: "Deployment cancellation only available in cloud version",
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
search: protectedProcedure
|
||||||
|
.input(
|
||||||
|
z.object({
|
||||||
|
q: z.string().optional(),
|
||||||
|
name: z.string().optional(),
|
||||||
|
appName: z.string().optional(),
|
||||||
|
description: z.string().optional(),
|
||||||
|
projectId: z.string().optional(),
|
||||||
|
environmentId: z.string().optional(),
|
||||||
|
limit: z.number().min(1).max(100).default(20),
|
||||||
|
offset: z.number().min(0).default(0),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.query(async ({ ctx, input }) => {
|
||||||
|
const baseConditions = [
|
||||||
|
eq(projects.organizationId, ctx.session.activeOrganizationId),
|
||||||
|
];
|
||||||
|
|
||||||
|
if (input.projectId) {
|
||||||
|
baseConditions.push(eq(environments.projectId, input.projectId));
|
||||||
|
}
|
||||||
|
if (input.environmentId) {
|
||||||
|
baseConditions.push(
|
||||||
|
eq(composeTable.environmentId, input.environmentId),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.q?.trim()) {
|
||||||
|
const term = `%${input.q.trim()}%`;
|
||||||
|
baseConditions.push(
|
||||||
|
or(
|
||||||
|
ilike(composeTable.name, term),
|
||||||
|
ilike(composeTable.appName, term),
|
||||||
|
ilike(composeTable.description ?? "", term),
|
||||||
|
)!,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.name?.trim()) {
|
||||||
|
baseConditions.push(ilike(composeTable.name, `%${input.name.trim()}%`));
|
||||||
|
}
|
||||||
|
if (input.appName?.trim()) {
|
||||||
|
baseConditions.push(
|
||||||
|
ilike(composeTable.appName, `%${input.appName.trim()}%`),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (input.description?.trim()) {
|
||||||
|
baseConditions.push(
|
||||||
|
ilike(
|
||||||
|
composeTable.description ?? "",
|
||||||
|
`%${input.description.trim()}%`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx.user.role === "member") {
|
||||||
|
const { accessedServices } = await findMemberById(
|
||||||
|
ctx.user.id,
|
||||||
|
ctx.session.activeOrganizationId,
|
||||||
|
);
|
||||||
|
if (accessedServices.length === 0) return { items: [], total: 0 };
|
||||||
|
baseConditions.push(
|
||||||
|
sql`${composeTable.composeId} IN (${sql.join(
|
||||||
|
accessedServices.map((id) => sql`${id}`),
|
||||||
|
sql`, `,
|
||||||
|
)})`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const where = and(...baseConditions);
|
||||||
|
|
||||||
|
const [items, countResult] = await Promise.all([
|
||||||
|
db
|
||||||
|
.select({
|
||||||
|
composeId: composeTable.composeId,
|
||||||
|
name: composeTable.name,
|
||||||
|
appName: composeTable.appName,
|
||||||
|
description: composeTable.description,
|
||||||
|
environmentId: composeTable.environmentId,
|
||||||
|
composeStatus: composeTable.composeStatus,
|
||||||
|
sourceType: composeTable.sourceType,
|
||||||
|
createdAt: composeTable.createdAt,
|
||||||
|
})
|
||||||
|
.from(composeTable)
|
||||||
|
.innerJoin(
|
||||||
|
environments,
|
||||||
|
eq(composeTable.environmentId, environments.environmentId),
|
||||||
|
)
|
||||||
|
.innerJoin(projects, eq(environments.projectId, projects.projectId))
|
||||||
|
.where(where)
|
||||||
|
.orderBy(desc(composeTable.createdAt))
|
||||||
|
.limit(input.limit)
|
||||||
|
.offset(input.offset),
|
||||||
|
db
|
||||||
|
.select({ count: sql<number>`count(*)::int` })
|
||||||
|
.from(composeTable)
|
||||||
|
.innerJoin(
|
||||||
|
environments,
|
||||||
|
eq(composeTable.environmentId, environments.environmentId),
|
||||||
|
)
|
||||||
|
.innerJoin(projects, eq(environments.projectId, projects.projectId))
|
||||||
|
.where(where),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
items,
|
||||||
|
total: countResult[0]?.count ?? 0,
|
||||||
|
};
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -11,7 +11,9 @@ import {
|
|||||||
findMemberById,
|
findMemberById,
|
||||||
updateEnvironmentById,
|
updateEnvironmentById,
|
||||||
} from "@dokploy/server";
|
} from "@dokploy/server";
|
||||||
|
import { db } from "@dokploy/server/db";
|
||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
|
import { and, desc, eq, ilike, or, sql } from "drizzle-orm";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { createTRPCRouter, protectedProcedure } from "@/server/api/trpc";
|
import { createTRPCRouter, protectedProcedure } from "@/server/api/trpc";
|
||||||
import {
|
import {
|
||||||
@@ -21,6 +23,7 @@ import {
|
|||||||
apiRemoveEnvironment,
|
apiRemoveEnvironment,
|
||||||
apiUpdateEnvironment,
|
apiUpdateEnvironment,
|
||||||
} from "@/server/db/schema";
|
} from "@/server/db/schema";
|
||||||
|
import { environments, projects } from "@/server/db/schema";
|
||||||
|
|
||||||
// Helper function to filter services within an environment based on user permissions
|
// Helper function to filter services within an environment based on user permissions
|
||||||
const filterEnvironmentServices = (
|
const filterEnvironmentServices = (
|
||||||
@@ -358,4 +361,92 @@ export const environmentRouter = createTRPCRouter({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
search: protectedProcedure
|
||||||
|
.input(
|
||||||
|
z.object({
|
||||||
|
q: z.string().optional(),
|
||||||
|
name: z.string().optional(),
|
||||||
|
description: z.string().optional(),
|
||||||
|
projectId: z.string().optional(),
|
||||||
|
limit: z.number().min(1).max(100).default(20),
|
||||||
|
offset: z.number().min(0).default(0),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.query(async ({ ctx, input }) => {
|
||||||
|
const baseConditions = [
|
||||||
|
eq(projects.organizationId, ctx.session.activeOrganizationId),
|
||||||
|
];
|
||||||
|
|
||||||
|
if (input.projectId) {
|
||||||
|
baseConditions.push(eq(environments.projectId, input.projectId));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.q?.trim()) {
|
||||||
|
const term = `%${input.q.trim()}%`;
|
||||||
|
baseConditions.push(
|
||||||
|
or(
|
||||||
|
ilike(environments.name, term),
|
||||||
|
ilike(environments.description ?? "", term),
|
||||||
|
)!,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.name?.trim()) {
|
||||||
|
baseConditions.push(ilike(environments.name, `%${input.name.trim()}%`));
|
||||||
|
}
|
||||||
|
if (input.description?.trim()) {
|
||||||
|
baseConditions.push(
|
||||||
|
ilike(
|
||||||
|
environments.description ?? "",
|
||||||
|
`%${input.description.trim()}%`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx.user.role === "member") {
|
||||||
|
const { accessedEnvironments } = await findMemberById(
|
||||||
|
ctx.user.id,
|
||||||
|
ctx.session.activeOrganizationId,
|
||||||
|
);
|
||||||
|
if (accessedEnvironments.length === 0) return { items: [], total: 0 };
|
||||||
|
baseConditions.push(
|
||||||
|
sql`${environments.environmentId} IN (${sql.join(
|
||||||
|
accessedEnvironments.map((id) => sql`${id}`),
|
||||||
|
sql`, `,
|
||||||
|
)})`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const where = and(...baseConditions);
|
||||||
|
|
||||||
|
const [items, countResult] = await Promise.all([
|
||||||
|
db
|
||||||
|
.select({
|
||||||
|
environmentId: environments.environmentId,
|
||||||
|
name: environments.name,
|
||||||
|
description: environments.description,
|
||||||
|
createdAt: environments.createdAt,
|
||||||
|
env: environments.env,
|
||||||
|
projectId: environments.projectId,
|
||||||
|
isDefault: environments.isDefault,
|
||||||
|
})
|
||||||
|
.from(environments)
|
||||||
|
.innerJoin(projects, eq(environments.projectId, projects.projectId))
|
||||||
|
.where(where)
|
||||||
|
.orderBy(desc(environments.createdAt))
|
||||||
|
.limit(input.limit)
|
||||||
|
.offset(input.offset),
|
||||||
|
db
|
||||||
|
.select({ count: sql<number>`count(*)::int` })
|
||||||
|
.from(environments)
|
||||||
|
.innerJoin(projects, eq(environments.projectId, projects.projectId))
|
||||||
|
.where(where),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
items,
|
||||||
|
total: countResult[0]?.count ?? 0,
|
||||||
|
};
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
findBackupsByDbId,
|
findBackupsByDbId,
|
||||||
findEnvironmentById,
|
findEnvironmentById,
|
||||||
findMariadbById,
|
findMariadbById,
|
||||||
|
findMemberById,
|
||||||
findProjectById,
|
findProjectById,
|
||||||
IS_CLOUD,
|
IS_CLOUD,
|
||||||
rebuildDatabase,
|
rebuildDatabase,
|
||||||
@@ -22,7 +23,7 @@ import {
|
|||||||
import { db } from "@dokploy/server/db";
|
import { db } from "@dokploy/server/db";
|
||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
import { observable } from "@trpc/server/observable";
|
import { observable } from "@trpc/server/observable";
|
||||||
import { eq } from "drizzle-orm";
|
import { and, desc, eq, ilike, or, sql } from "drizzle-orm";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { createTRPCRouter, protectedProcedure } from "@/server/api/trpc";
|
import { createTRPCRouter, protectedProcedure } from "@/server/api/trpc";
|
||||||
import {
|
import {
|
||||||
@@ -37,6 +38,7 @@ import {
|
|||||||
apiUpdateMariaDB,
|
apiUpdateMariaDB,
|
||||||
mariadb as mariadbTable,
|
mariadb as mariadbTable,
|
||||||
} from "@/server/db/schema";
|
} from "@/server/db/schema";
|
||||||
|
import { environments, projects } from "@/server/db/schema";
|
||||||
import { cancelJobs } from "@/server/utils/backup";
|
import { cancelJobs } from "@/server/utils/backup";
|
||||||
export const mariadbRouter = createTRPCRouter({
|
export const mariadbRouter = createTRPCRouter({
|
||||||
create: protectedProcedure
|
create: protectedProcedure
|
||||||
@@ -446,4 +448,102 @@ export const mariadbRouter = createTRPCRouter({
|
|||||||
await rebuildDatabase(mariadb.mariadbId, "mariadb");
|
await rebuildDatabase(mariadb.mariadbId, "mariadb");
|
||||||
return true;
|
return true;
|
||||||
}),
|
}),
|
||||||
|
search: protectedProcedure
|
||||||
|
.input(
|
||||||
|
z.object({
|
||||||
|
q: z.string().optional(),
|
||||||
|
name: z.string().optional(),
|
||||||
|
appName: z.string().optional(),
|
||||||
|
description: z.string().optional(),
|
||||||
|
projectId: z.string().optional(),
|
||||||
|
environmentId: z.string().optional(),
|
||||||
|
limit: z.number().min(1).max(100).default(20),
|
||||||
|
offset: z.number().min(0).default(0),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.query(async ({ ctx, input }) => {
|
||||||
|
const baseConditions = [
|
||||||
|
eq(projects.organizationId, ctx.session.activeOrganizationId),
|
||||||
|
];
|
||||||
|
if (input.projectId) {
|
||||||
|
baseConditions.push(eq(environments.projectId, input.projectId));
|
||||||
|
}
|
||||||
|
if (input.environmentId) {
|
||||||
|
baseConditions.push(
|
||||||
|
eq(mariadbTable.environmentId, input.environmentId),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (input.q?.trim()) {
|
||||||
|
const term = `%${input.q.trim()}%`;
|
||||||
|
baseConditions.push(
|
||||||
|
or(
|
||||||
|
ilike(mariadbTable.name, term),
|
||||||
|
ilike(mariadbTable.appName, term),
|
||||||
|
ilike(mariadbTable.description ?? "", term),
|
||||||
|
)!,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (input.name?.trim()) {
|
||||||
|
baseConditions.push(ilike(mariadbTable.name, `%${input.name.trim()}%`));
|
||||||
|
}
|
||||||
|
if (input.appName?.trim()) {
|
||||||
|
baseConditions.push(
|
||||||
|
ilike(mariadbTable.appName, `%${input.appName.trim()}%`),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (input.description?.trim()) {
|
||||||
|
baseConditions.push(
|
||||||
|
ilike(
|
||||||
|
mariadbTable.description ?? "",
|
||||||
|
`%${input.description.trim()}%`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (ctx.user.role === "member") {
|
||||||
|
const { accessedServices } = await findMemberById(
|
||||||
|
ctx.user.id,
|
||||||
|
ctx.session.activeOrganizationId,
|
||||||
|
);
|
||||||
|
if (accessedServices.length === 0) return { items: [], total: 0 };
|
||||||
|
baseConditions.push(
|
||||||
|
sql`${mariadbTable.mariadbId} IN (${sql.join(
|
||||||
|
accessedServices.map((id) => sql`${id}`),
|
||||||
|
sql`, `,
|
||||||
|
)})`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const where = and(...baseConditions);
|
||||||
|
const [items, countResult] = await Promise.all([
|
||||||
|
db
|
||||||
|
.select({
|
||||||
|
mariadbId: mariadbTable.mariadbId,
|
||||||
|
name: mariadbTable.name,
|
||||||
|
appName: mariadbTable.appName,
|
||||||
|
description: mariadbTable.description,
|
||||||
|
environmentId: mariadbTable.environmentId,
|
||||||
|
applicationStatus: mariadbTable.applicationStatus,
|
||||||
|
createdAt: mariadbTable.createdAt,
|
||||||
|
})
|
||||||
|
.from(mariadbTable)
|
||||||
|
.innerJoin(
|
||||||
|
environments,
|
||||||
|
eq(mariadbTable.environmentId, environments.environmentId),
|
||||||
|
)
|
||||||
|
.innerJoin(projects, eq(environments.projectId, projects.projectId))
|
||||||
|
.where(where)
|
||||||
|
.orderBy(desc(mariadbTable.createdAt))
|
||||||
|
.limit(input.limit)
|
||||||
|
.offset(input.offset),
|
||||||
|
db
|
||||||
|
.select({ count: sql<number>`count(*)::int` })
|
||||||
|
.from(mariadbTable)
|
||||||
|
.innerJoin(
|
||||||
|
environments,
|
||||||
|
eq(mariadbTable.environmentId, environments.environmentId),
|
||||||
|
)
|
||||||
|
.innerJoin(projects, eq(environments.projectId, projects.projectId))
|
||||||
|
.where(where),
|
||||||
|
]);
|
||||||
|
return { items, total: countResult[0]?.count ?? 0 };
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
deployMongo,
|
deployMongo,
|
||||||
findBackupsByDbId,
|
findBackupsByDbId,
|
||||||
findEnvironmentById,
|
findEnvironmentById,
|
||||||
|
findMemberById,
|
||||||
findMongoById,
|
findMongoById,
|
||||||
findProjectById,
|
findProjectById,
|
||||||
IS_CLOUD,
|
IS_CLOUD,
|
||||||
@@ -21,7 +22,7 @@ import {
|
|||||||
} from "@dokploy/server";
|
} from "@dokploy/server";
|
||||||
import { db } from "@dokploy/server/db";
|
import { db } from "@dokploy/server/db";
|
||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
import { eq } from "drizzle-orm";
|
import { and, desc, eq, ilike, or, sql } from "drizzle-orm";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { createTRPCRouter, protectedProcedure } from "@/server/api/trpc";
|
import { createTRPCRouter, protectedProcedure } from "@/server/api/trpc";
|
||||||
import {
|
import {
|
||||||
@@ -36,6 +37,7 @@ import {
|
|||||||
apiUpdateMongo,
|
apiUpdateMongo,
|
||||||
mongo as mongoTable,
|
mongo as mongoTable,
|
||||||
} from "@/server/db/schema";
|
} from "@/server/db/schema";
|
||||||
|
import { environments, projects } from "@/server/db/schema";
|
||||||
import { cancelJobs } from "@/server/utils/backup";
|
import { cancelJobs } from "@/server/utils/backup";
|
||||||
export const mongoRouter = createTRPCRouter({
|
export const mongoRouter = createTRPCRouter({
|
||||||
create: protectedProcedure
|
create: protectedProcedure
|
||||||
@@ -476,4 +478,97 @@ export const mongoRouter = createTRPCRouter({
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}),
|
}),
|
||||||
|
search: protectedProcedure
|
||||||
|
.input(
|
||||||
|
z.object({
|
||||||
|
q: z.string().optional(),
|
||||||
|
name: z.string().optional(),
|
||||||
|
appName: z.string().optional(),
|
||||||
|
description: z.string().optional(),
|
||||||
|
projectId: z.string().optional(),
|
||||||
|
environmentId: z.string().optional(),
|
||||||
|
limit: z.number().min(1).max(100).default(20),
|
||||||
|
offset: z.number().min(0).default(0),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.query(async ({ ctx, input }) => {
|
||||||
|
const baseConditions = [
|
||||||
|
eq(projects.organizationId, ctx.session.activeOrganizationId),
|
||||||
|
];
|
||||||
|
if (input.projectId) {
|
||||||
|
baseConditions.push(eq(environments.projectId, input.projectId));
|
||||||
|
}
|
||||||
|
if (input.environmentId) {
|
||||||
|
baseConditions.push(eq(mongoTable.environmentId, input.environmentId));
|
||||||
|
}
|
||||||
|
if (input.q?.trim()) {
|
||||||
|
const term = `%${input.q.trim()}%`;
|
||||||
|
baseConditions.push(
|
||||||
|
or(
|
||||||
|
ilike(mongoTable.name, term),
|
||||||
|
ilike(mongoTable.appName, term),
|
||||||
|
ilike(mongoTable.description ?? "", term),
|
||||||
|
)!,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (input.name?.trim()) {
|
||||||
|
baseConditions.push(ilike(mongoTable.name, `%${input.name.trim()}%`));
|
||||||
|
}
|
||||||
|
if (input.appName?.trim()) {
|
||||||
|
baseConditions.push(
|
||||||
|
ilike(mongoTable.appName, `%${input.appName.trim()}%`),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (input.description?.trim()) {
|
||||||
|
baseConditions.push(
|
||||||
|
ilike(mongoTable.description ?? "", `%${input.description.trim()}%`),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (ctx.user.role === "member") {
|
||||||
|
const { accessedServices } = await findMemberById(
|
||||||
|
ctx.user.id,
|
||||||
|
ctx.session.activeOrganizationId,
|
||||||
|
);
|
||||||
|
if (accessedServices.length === 0) return { items: [], total: 0 };
|
||||||
|
baseConditions.push(
|
||||||
|
sql`${mongoTable.mongoId} IN (${sql.join(
|
||||||
|
accessedServices.map((id) => sql`${id}`),
|
||||||
|
sql`, `,
|
||||||
|
)})`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const where = and(...baseConditions);
|
||||||
|
const [items, countResult] = await Promise.all([
|
||||||
|
db
|
||||||
|
.select({
|
||||||
|
mongoId: mongoTable.mongoId,
|
||||||
|
name: mongoTable.name,
|
||||||
|
appName: mongoTable.appName,
|
||||||
|
description: mongoTable.description,
|
||||||
|
environmentId: mongoTable.environmentId,
|
||||||
|
applicationStatus: mongoTable.applicationStatus,
|
||||||
|
createdAt: mongoTable.createdAt,
|
||||||
|
})
|
||||||
|
.from(mongoTable)
|
||||||
|
.innerJoin(
|
||||||
|
environments,
|
||||||
|
eq(mongoTable.environmentId, environments.environmentId),
|
||||||
|
)
|
||||||
|
.innerJoin(projects, eq(environments.projectId, projects.projectId))
|
||||||
|
.where(where)
|
||||||
|
.orderBy(desc(mongoTable.createdAt))
|
||||||
|
.limit(input.limit)
|
||||||
|
.offset(input.offset),
|
||||||
|
db
|
||||||
|
.select({ count: sql<number>`count(*)::int` })
|
||||||
|
.from(mongoTable)
|
||||||
|
.innerJoin(
|
||||||
|
environments,
|
||||||
|
eq(mongoTable.environmentId, environments.environmentId),
|
||||||
|
)
|
||||||
|
.innerJoin(projects, eq(environments.projectId, projects.projectId))
|
||||||
|
.where(where),
|
||||||
|
]);
|
||||||
|
return { items, total: countResult[0]?.count ?? 0 };
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
deployMySql,
|
deployMySql,
|
||||||
findBackupsByDbId,
|
findBackupsByDbId,
|
||||||
findEnvironmentById,
|
findEnvironmentById,
|
||||||
|
findMemberById,
|
||||||
findMySqlById,
|
findMySqlById,
|
||||||
findProjectById,
|
findProjectById,
|
||||||
IS_CLOUD,
|
IS_CLOUD,
|
||||||
@@ -21,7 +22,7 @@ import {
|
|||||||
} from "@dokploy/server";
|
} from "@dokploy/server";
|
||||||
import { db } from "@dokploy/server/db";
|
import { db } from "@dokploy/server/db";
|
||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
import { eq } from "drizzle-orm";
|
import { and, desc, eq, ilike, or, sql } from "drizzle-orm";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { createTRPCRouter, protectedProcedure } from "@/server/api/trpc";
|
import { createTRPCRouter, protectedProcedure } from "@/server/api/trpc";
|
||||||
import {
|
import {
|
||||||
@@ -34,7 +35,9 @@ import {
|
|||||||
apiSaveEnvironmentVariablesMySql,
|
apiSaveEnvironmentVariablesMySql,
|
||||||
apiSaveExternalPortMySql,
|
apiSaveExternalPortMySql,
|
||||||
apiUpdateMySql,
|
apiUpdateMySql,
|
||||||
|
environments,
|
||||||
mysql as mysqlTable,
|
mysql as mysqlTable,
|
||||||
|
projects,
|
||||||
} from "@/server/db/schema";
|
} from "@/server/db/schema";
|
||||||
import { cancelJobs } from "@/server/utils/backup";
|
import { cancelJobs } from "@/server/utils/backup";
|
||||||
|
|
||||||
@@ -471,4 +474,97 @@ export const mysqlRouter = createTRPCRouter({
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}),
|
}),
|
||||||
|
search: protectedProcedure
|
||||||
|
.input(
|
||||||
|
z.object({
|
||||||
|
q: z.string().optional(),
|
||||||
|
name: z.string().optional(),
|
||||||
|
appName: z.string().optional(),
|
||||||
|
description: z.string().optional(),
|
||||||
|
projectId: z.string().optional(),
|
||||||
|
environmentId: z.string().optional(),
|
||||||
|
limit: z.number().min(1).max(100).default(20),
|
||||||
|
offset: z.number().min(0).default(0),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.query(async ({ ctx, input }) => {
|
||||||
|
const baseConditions = [
|
||||||
|
eq(projects.organizationId, ctx.session.activeOrganizationId),
|
||||||
|
];
|
||||||
|
if (input.projectId) {
|
||||||
|
baseConditions.push(eq(environments.projectId, input.projectId));
|
||||||
|
}
|
||||||
|
if (input.environmentId) {
|
||||||
|
baseConditions.push(eq(mysqlTable.environmentId, input.environmentId));
|
||||||
|
}
|
||||||
|
if (input.q?.trim()) {
|
||||||
|
const term = `%${input.q.trim()}%`;
|
||||||
|
baseConditions.push(
|
||||||
|
or(
|
||||||
|
ilike(mysqlTable.name, term),
|
||||||
|
ilike(mysqlTable.appName, term),
|
||||||
|
ilike(mysqlTable.description ?? "", term),
|
||||||
|
)!,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (input.name?.trim()) {
|
||||||
|
baseConditions.push(ilike(mysqlTable.name, `%${input.name.trim()}%`));
|
||||||
|
}
|
||||||
|
if (input.appName?.trim()) {
|
||||||
|
baseConditions.push(
|
||||||
|
ilike(mysqlTable.appName, `%${input.appName.trim()}%`),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (input.description?.trim()) {
|
||||||
|
baseConditions.push(
|
||||||
|
ilike(mysqlTable.description ?? "", `%${input.description.trim()}%`),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (ctx.user.role === "member") {
|
||||||
|
const { accessedServices } = await findMemberById(
|
||||||
|
ctx.user.id,
|
||||||
|
ctx.session.activeOrganizationId,
|
||||||
|
);
|
||||||
|
if (accessedServices.length === 0) return { items: [], total: 0 };
|
||||||
|
baseConditions.push(
|
||||||
|
sql`${mysqlTable.mysqlId} IN (${sql.join(
|
||||||
|
accessedServices.map((id) => sql`${id}`),
|
||||||
|
sql`, `,
|
||||||
|
)})`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const where = and(...baseConditions);
|
||||||
|
const [items, countResult] = await Promise.all([
|
||||||
|
db
|
||||||
|
.select({
|
||||||
|
mysqlId: mysqlTable.mysqlId,
|
||||||
|
name: mysqlTable.name,
|
||||||
|
appName: mysqlTable.appName,
|
||||||
|
description: mysqlTable.description,
|
||||||
|
environmentId: mysqlTable.environmentId,
|
||||||
|
applicationStatus: mysqlTable.applicationStatus,
|
||||||
|
createdAt: mysqlTable.createdAt,
|
||||||
|
})
|
||||||
|
.from(mysqlTable)
|
||||||
|
.innerJoin(
|
||||||
|
environments,
|
||||||
|
eq(mysqlTable.environmentId, environments.environmentId),
|
||||||
|
)
|
||||||
|
.innerJoin(projects, eq(environments.projectId, projects.projectId))
|
||||||
|
.where(where)
|
||||||
|
.orderBy(desc(mysqlTable.createdAt))
|
||||||
|
.limit(input.limit)
|
||||||
|
.offset(input.offset),
|
||||||
|
db
|
||||||
|
.select({ count: sql<number>`count(*)::int` })
|
||||||
|
.from(mysqlTable)
|
||||||
|
.innerJoin(
|
||||||
|
environments,
|
||||||
|
eq(mysqlTable.environmentId, environments.environmentId),
|
||||||
|
)
|
||||||
|
.innerJoin(projects, eq(environments.projectId, projects.projectId))
|
||||||
|
.where(where),
|
||||||
|
]);
|
||||||
|
return { items, total: countResult[0]?.count ?? 0 };
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
deployPostgres,
|
deployPostgres,
|
||||||
findBackupsByDbId,
|
findBackupsByDbId,
|
||||||
findEnvironmentById,
|
findEnvironmentById,
|
||||||
|
findMemberById,
|
||||||
findPostgresById,
|
findPostgresById,
|
||||||
findProjectById,
|
findProjectById,
|
||||||
getMountPath,
|
getMountPath,
|
||||||
@@ -22,7 +23,7 @@ import {
|
|||||||
} from "@dokploy/server";
|
} from "@dokploy/server";
|
||||||
import { db } from "@dokploy/server/db";
|
import { db } from "@dokploy/server/db";
|
||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
import { eq } from "drizzle-orm";
|
import { and, desc, eq, ilike, or, sql } from "drizzle-orm";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { createTRPCRouter, protectedProcedure } from "@/server/api/trpc";
|
import { createTRPCRouter, protectedProcedure } from "@/server/api/trpc";
|
||||||
import {
|
import {
|
||||||
@@ -37,6 +38,7 @@ import {
|
|||||||
apiUpdatePostgres,
|
apiUpdatePostgres,
|
||||||
postgres as postgresTable,
|
postgres as postgresTable,
|
||||||
} from "@/server/db/schema";
|
} from "@/server/db/schema";
|
||||||
|
import { environments, projects } from "@/server/db/schema";
|
||||||
import { cancelJobs } from "@/server/utils/backup";
|
import { cancelJobs } from "@/server/utils/backup";
|
||||||
|
|
||||||
export const postgresRouter = createTRPCRouter({
|
export const postgresRouter = createTRPCRouter({
|
||||||
@@ -483,4 +485,104 @@ export const postgresRouter = createTRPCRouter({
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}),
|
}),
|
||||||
|
search: protectedProcedure
|
||||||
|
.input(
|
||||||
|
z.object({
|
||||||
|
q: z.string().optional(),
|
||||||
|
name: z.string().optional(),
|
||||||
|
appName: z.string().optional(),
|
||||||
|
description: z.string().optional(),
|
||||||
|
projectId: z.string().optional(),
|
||||||
|
environmentId: z.string().optional(),
|
||||||
|
limit: z.number().min(1).max(100).default(20),
|
||||||
|
offset: z.number().min(0).default(0),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.query(async ({ ctx, input }) => {
|
||||||
|
const baseConditions = [
|
||||||
|
eq(projects.organizationId, ctx.session.activeOrganizationId),
|
||||||
|
];
|
||||||
|
if (input.projectId) {
|
||||||
|
baseConditions.push(eq(environments.projectId, input.projectId));
|
||||||
|
}
|
||||||
|
if (input.environmentId) {
|
||||||
|
baseConditions.push(
|
||||||
|
eq(postgresTable.environmentId, input.environmentId),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (input.q?.trim()) {
|
||||||
|
const term = `%${input.q.trim()}%`;
|
||||||
|
baseConditions.push(
|
||||||
|
or(
|
||||||
|
ilike(postgresTable.name, term),
|
||||||
|
ilike(postgresTable.appName, term),
|
||||||
|
ilike(postgresTable.description ?? "", term),
|
||||||
|
)!,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (input.name?.trim()) {
|
||||||
|
baseConditions.push(
|
||||||
|
ilike(postgresTable.name, `%${input.name.trim()}%`),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (input.appName?.trim()) {
|
||||||
|
baseConditions.push(
|
||||||
|
ilike(postgresTable.appName, `%${input.appName.trim()}%`),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (input.description?.trim()) {
|
||||||
|
baseConditions.push(
|
||||||
|
ilike(
|
||||||
|
postgresTable.description ?? "",
|
||||||
|
`%${input.description.trim()}%`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (ctx.user.role === "member") {
|
||||||
|
const { accessedServices } = await findMemberById(
|
||||||
|
ctx.user.id,
|
||||||
|
ctx.session.activeOrganizationId,
|
||||||
|
);
|
||||||
|
if (accessedServices.length === 0) return { items: [], total: 0 };
|
||||||
|
baseConditions.push(
|
||||||
|
sql`${postgresTable.postgresId} IN (${sql.join(
|
||||||
|
accessedServices.map((id) => sql`${id}`),
|
||||||
|
sql`, `,
|
||||||
|
)})`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const where = and(...baseConditions);
|
||||||
|
const [items, countResult] = await Promise.all([
|
||||||
|
db
|
||||||
|
.select({
|
||||||
|
postgresId: postgresTable.postgresId,
|
||||||
|
name: postgresTable.name,
|
||||||
|
appName: postgresTable.appName,
|
||||||
|
description: postgresTable.description,
|
||||||
|
environmentId: postgresTable.environmentId,
|
||||||
|
applicationStatus: postgresTable.applicationStatus,
|
||||||
|
createdAt: postgresTable.createdAt,
|
||||||
|
})
|
||||||
|
.from(postgresTable)
|
||||||
|
.innerJoin(
|
||||||
|
environments,
|
||||||
|
eq(postgresTable.environmentId, environments.environmentId),
|
||||||
|
)
|
||||||
|
.innerJoin(projects, eq(environments.projectId, projects.projectId))
|
||||||
|
.where(where)
|
||||||
|
.orderBy(desc(postgresTable.createdAt))
|
||||||
|
.limit(input.limit)
|
||||||
|
.offset(input.offset),
|
||||||
|
db
|
||||||
|
.select({ count: sql<number>`count(*)::int` })
|
||||||
|
.from(postgresTable)
|
||||||
|
.innerJoin(
|
||||||
|
environments,
|
||||||
|
eq(postgresTable.environmentId, environments.environmentId),
|
||||||
|
)
|
||||||
|
.innerJoin(projects, eq(environments.projectId, projects.projectId))
|
||||||
|
.where(where),
|
||||||
|
]);
|
||||||
|
return { items, total: countResult[0]?.count ?? 0 };
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ import {
|
|||||||
} from "@dokploy/server";
|
} from "@dokploy/server";
|
||||||
import { db } from "@dokploy/server/db";
|
import { db } from "@dokploy/server/db";
|
||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
import { and, desc, eq, sql } from "drizzle-orm";
|
import { and, desc, eq, ilike, or, sql } from "drizzle-orm";
|
||||||
import type { AnyPgColumn } from "drizzle-orm/pg-core";
|
import type { AnyPgColumn } from "drizzle-orm/pg-core";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { createTRPCRouter, protectedProcedure } from "@/server/api/trpc";
|
import { createTRPCRouter, protectedProcedure } from "@/server/api/trpc";
|
||||||
@@ -277,6 +277,83 @@ export const projectRouter = createTRPCRouter({
|
|||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
search: protectedProcedure
|
||||||
|
.input(
|
||||||
|
z.object({
|
||||||
|
q: z.string().optional(),
|
||||||
|
name: z.string().optional(),
|
||||||
|
description: z.string().optional(),
|
||||||
|
limit: z.number().min(1).max(100).default(20),
|
||||||
|
offset: z.number().min(0).default(0),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.query(async ({ ctx, input }) => {
|
||||||
|
const baseConditions = [
|
||||||
|
eq(projects.organizationId, ctx.session.activeOrganizationId),
|
||||||
|
];
|
||||||
|
|
||||||
|
if (input.q?.trim()) {
|
||||||
|
const term = `%${input.q.trim()}%`;
|
||||||
|
baseConditions.push(
|
||||||
|
or(
|
||||||
|
ilike(projects.name, term),
|
||||||
|
ilike(projects.description ?? "", term),
|
||||||
|
)!,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.name?.trim()) {
|
||||||
|
baseConditions.push(ilike(projects.name, `%${input.name.trim()}%`));
|
||||||
|
}
|
||||||
|
if (input.description?.trim()) {
|
||||||
|
baseConditions.push(
|
||||||
|
ilike(projects.description ?? "", `%${input.description.trim()}%`),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx.user.role === "member") {
|
||||||
|
const { accessedProjects } = await findMemberById(
|
||||||
|
ctx.user.id,
|
||||||
|
ctx.session.activeOrganizationId,
|
||||||
|
);
|
||||||
|
if (accessedProjects.length === 0) return { items: [], total: 0 };
|
||||||
|
baseConditions.push(
|
||||||
|
sql`${projects.projectId} IN (${sql.join(
|
||||||
|
accessedProjects.map((id) => sql`${id}`),
|
||||||
|
sql`, `,
|
||||||
|
)})`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const where = and(...baseConditions);
|
||||||
|
|
||||||
|
const [items, countResult] = await Promise.all([
|
||||||
|
db.query.projects.findMany({
|
||||||
|
where,
|
||||||
|
limit: input.limit,
|
||||||
|
offset: input.offset,
|
||||||
|
orderBy: desc(projects.createdAt),
|
||||||
|
columns: {
|
||||||
|
projectId: true,
|
||||||
|
name: true,
|
||||||
|
description: true,
|
||||||
|
createdAt: true,
|
||||||
|
organizationId: true,
|
||||||
|
env: true,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
db
|
||||||
|
.select({ count: sql<number>`count(*)::int` })
|
||||||
|
.from(projects)
|
||||||
|
.where(where),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
items,
|
||||||
|
total: countResult[0]?.count ?? 0,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
|
||||||
remove: protectedProcedure
|
remove: protectedProcedure
|
||||||
.input(apiRemoveProject)
|
.input(apiRemoveProject)
|
||||||
.mutation(async ({ input, ctx }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
createRedis,
|
createRedis,
|
||||||
deployRedis,
|
deployRedis,
|
||||||
findEnvironmentById,
|
findEnvironmentById,
|
||||||
|
findMemberById,
|
||||||
findProjectById,
|
findProjectById,
|
||||||
findRedisById,
|
findRedisById,
|
||||||
IS_CLOUD,
|
IS_CLOUD,
|
||||||
@@ -20,7 +21,7 @@ import {
|
|||||||
} from "@dokploy/server";
|
} from "@dokploy/server";
|
||||||
import { db } from "@dokploy/server/db";
|
import { db } from "@dokploy/server/db";
|
||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
import { eq } from "drizzle-orm";
|
import { and, desc, eq, ilike, or, sql } from "drizzle-orm";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { createTRPCRouter, protectedProcedure } from "@/server/api/trpc";
|
import { createTRPCRouter, protectedProcedure } from "@/server/api/trpc";
|
||||||
import {
|
import {
|
||||||
@@ -35,6 +36,7 @@ import {
|
|||||||
apiUpdateRedis,
|
apiUpdateRedis,
|
||||||
redis as redisTable,
|
redis as redisTable,
|
||||||
} from "@/server/db/schema";
|
} from "@/server/db/schema";
|
||||||
|
import { environments, projects } from "@/server/db/schema";
|
||||||
export const redisRouter = createTRPCRouter({
|
export const redisRouter = createTRPCRouter({
|
||||||
create: protectedProcedure
|
create: protectedProcedure
|
||||||
.input(apiCreateRedis)
|
.input(apiCreateRedis)
|
||||||
@@ -450,4 +452,97 @@ export const redisRouter = createTRPCRouter({
|
|||||||
await rebuildDatabase(redis.redisId, "redis");
|
await rebuildDatabase(redis.redisId, "redis");
|
||||||
return true;
|
return true;
|
||||||
}),
|
}),
|
||||||
|
search: protectedProcedure
|
||||||
|
.input(
|
||||||
|
z.object({
|
||||||
|
q: z.string().optional(),
|
||||||
|
name: z.string().optional(),
|
||||||
|
appName: z.string().optional(),
|
||||||
|
description: z.string().optional(),
|
||||||
|
projectId: z.string().optional(),
|
||||||
|
environmentId: z.string().optional(),
|
||||||
|
limit: z.number().min(1).max(100).default(20),
|
||||||
|
offset: z.number().min(0).default(0),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.query(async ({ ctx, input }) => {
|
||||||
|
const baseConditions = [
|
||||||
|
eq(projects.organizationId, ctx.session.activeOrganizationId),
|
||||||
|
];
|
||||||
|
if (input.projectId) {
|
||||||
|
baseConditions.push(eq(environments.projectId, input.projectId));
|
||||||
|
}
|
||||||
|
if (input.environmentId) {
|
||||||
|
baseConditions.push(eq(redisTable.environmentId, input.environmentId));
|
||||||
|
}
|
||||||
|
if (input.q?.trim()) {
|
||||||
|
const term = `%${input.q.trim()}%`;
|
||||||
|
baseConditions.push(
|
||||||
|
or(
|
||||||
|
ilike(redisTable.name, term),
|
||||||
|
ilike(redisTable.appName, term),
|
||||||
|
ilike(redisTable.description ?? "", term),
|
||||||
|
)!,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (input.name?.trim()) {
|
||||||
|
baseConditions.push(ilike(redisTable.name, `%${input.name.trim()}%`));
|
||||||
|
}
|
||||||
|
if (input.appName?.trim()) {
|
||||||
|
baseConditions.push(
|
||||||
|
ilike(redisTable.appName, `%${input.appName.trim()}%`),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (input.description?.trim()) {
|
||||||
|
baseConditions.push(
|
||||||
|
ilike(redisTable.description ?? "", `%${input.description.trim()}%`),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (ctx.user.role === "member") {
|
||||||
|
const { accessedServices } = await findMemberById(
|
||||||
|
ctx.user.id,
|
||||||
|
ctx.session.activeOrganizationId,
|
||||||
|
);
|
||||||
|
if (accessedServices.length === 0) return { items: [], total: 0 };
|
||||||
|
baseConditions.push(
|
||||||
|
sql`${redisTable.redisId} IN (${sql.join(
|
||||||
|
accessedServices.map((id) => sql`${id}`),
|
||||||
|
sql`, `,
|
||||||
|
)})`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const where = and(...baseConditions);
|
||||||
|
const [items, countResult] = await Promise.all([
|
||||||
|
db
|
||||||
|
.select({
|
||||||
|
redisId: redisTable.redisId,
|
||||||
|
name: redisTable.name,
|
||||||
|
appName: redisTable.appName,
|
||||||
|
description: redisTable.description,
|
||||||
|
environmentId: redisTable.environmentId,
|
||||||
|
applicationStatus: redisTable.applicationStatus,
|
||||||
|
createdAt: redisTable.createdAt,
|
||||||
|
})
|
||||||
|
.from(redisTable)
|
||||||
|
.innerJoin(
|
||||||
|
environments,
|
||||||
|
eq(redisTable.environmentId, environments.environmentId),
|
||||||
|
)
|
||||||
|
.innerJoin(projects, eq(environments.projectId, projects.projectId))
|
||||||
|
.where(where)
|
||||||
|
.orderBy(desc(redisTable.createdAt))
|
||||||
|
.limit(input.limit)
|
||||||
|
.offset(input.offset),
|
||||||
|
db
|
||||||
|
.select({ count: sql<number>`count(*)::int` })
|
||||||
|
.from(redisTable)
|
||||||
|
.innerJoin(
|
||||||
|
environments,
|
||||||
|
eq(redisTable.environmentId, environments.environmentId),
|
||||||
|
)
|
||||||
|
.innerJoin(projects, eq(environments.projectId, projects.projectId))
|
||||||
|
.where(where),
|
||||||
|
]);
|
||||||
|
return { items, total: countResult[0]?.count ?? 0 };
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user