mirror of
https://github.com/dokploy/dokploy.git
synced 2026-06-14 03:19:49 +00:00
chore: update @dokploy/trpc-openapi to version 0.0.19 and enhance API documentation
- Updated the version of @dokploy/trpc-openapi from 0.0.18 to 0.0.19 in pnpm-lock.yaml and package.json. - Added OpenAPI metadata to various procedures across multiple routers, improving API documentation and clarity for developers. - Enhanced descriptions and summaries for several API endpoints, ensuring better understanding of their functionality. These changes improve the overall API usability and documentation quality.
This commit is contained in:
@@ -58,7 +58,7 @@
|
||||
"@codemirror/search": "^6.6.0",
|
||||
"@codemirror/view": "^6.39.15",
|
||||
"@dokploy/server": "workspace:*",
|
||||
"@dokploy/trpc-openapi": "0.0.18",
|
||||
"@dokploy/trpc-openapi": "0.0.19",
|
||||
"@faker-js/faker": "^8.4.1",
|
||||
"@hookform/resolvers": "^5.2.2",
|
||||
"@octokit/auth-app": "^6.1.3",
|
||||
|
||||
@@ -10,6 +10,12 @@ import { adminProcedure, createTRPCRouter } from "../trpc";
|
||||
|
||||
export const adminRouter = createTRPCRouter({
|
||||
setupMonitoring: adminProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Update web server monitoring settings",
|
||||
description: "Update the monitoring configuration for the web server including refresh rates, thresholds, and container services. Restarts the monitoring system and returns the updated settings. Disabled on cloud.",
|
||||
},
|
||||
})
|
||||
.input(apiUpdateWebServerMonitoring)
|
||||
.mutation(async ({ input }) => {
|
||||
try {
|
||||
|
||||
@@ -41,12 +41,24 @@ import { generatePassword } from "@/templates/utils";
|
||||
|
||||
export const aiRouter = createTRPCRouter({
|
||||
one: adminProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get AI settings by ID",
|
||||
description: "Returns a single AI provider configuration by its ID.",
|
||||
},
|
||||
})
|
||||
.input(z.object({ aiId: z.string() }))
|
||||
.query(async ({ input }) => {
|
||||
return await getAiSettingById(input.aiId);
|
||||
}),
|
||||
|
||||
getModels: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "List available AI models",
|
||||
description: "Fetches the list of models from the given AI provider URL. Supports OpenAI-compatible, Ollama, Gemini, Perplexity, ZAI, and MiniMax providers.",
|
||||
},
|
||||
})
|
||||
.input(z.object({ apiUrl: z.string().min(1), apiKey: z.string() }))
|
||||
.query(async ({ input }) => {
|
||||
try {
|
||||
@@ -174,33 +186,75 @@ export const aiRouter = createTRPCRouter({
|
||||
});
|
||||
}
|
||||
}),
|
||||
create: adminProcedure.input(apiCreateAi).mutation(async ({ ctx, input }) => {
|
||||
create: adminProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Create AI provider",
|
||||
description: "Saves a new AI provider configuration (API URL, key, model) for the current organization.",
|
||||
},
|
||||
})
|
||||
.input(apiCreateAi)
|
||||
.mutation(async ({ ctx, input }) => {
|
||||
return await saveAiSettings(ctx.session.activeOrganizationId, input);
|
||||
}),
|
||||
|
||||
update: adminProcedure.input(apiUpdateAi).mutation(async ({ ctx, input }) => {
|
||||
update: adminProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Update AI provider",
|
||||
description: "Updates an existing AI provider configuration for the current organization.",
|
||||
},
|
||||
})
|
||||
.input(apiUpdateAi)
|
||||
.mutation(async ({ ctx, input }) => {
|
||||
return await saveAiSettings(ctx.session.activeOrganizationId, input);
|
||||
}),
|
||||
|
||||
getAll: adminProcedure.query(async ({ ctx }) => {
|
||||
getAll: adminProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "List all AI providers",
|
||||
description: "Returns all AI provider configurations for the current organization.",
|
||||
},
|
||||
})
|
||||
.query(async ({ ctx }) => {
|
||||
return await getAiSettingsByOrganizationId(
|
||||
ctx.session.activeOrganizationId,
|
||||
);
|
||||
}),
|
||||
|
||||
get: adminProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get AI provider",
|
||||
description: "Returns a single AI provider configuration by its ID.",
|
||||
},
|
||||
})
|
||||
.input(z.object({ aiId: z.string() }))
|
||||
.query(async ({ input }) => {
|
||||
return await getAiSettingById(input.aiId);
|
||||
}),
|
||||
|
||||
delete: adminProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Delete AI provider",
|
||||
description: "Removes an AI provider configuration by its ID.",
|
||||
},
|
||||
})
|
||||
.input(z.object({ aiId: z.string() }))
|
||||
.mutation(async ({ input }) => {
|
||||
return await deleteAiSettings(input.aiId);
|
||||
}),
|
||||
|
||||
getEnabledProviders: protectedProcedure.query(async ({ ctx }) => {
|
||||
getEnabledProviders: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "List enabled AI providers",
|
||||
description: "Returns a lightweight list of enabled AI providers (ID, name, model) for the current organization, suitable for dropdown selectors.",
|
||||
},
|
||||
})
|
||||
.query(async ({ ctx }) => {
|
||||
const settings = await getAiSettingsByOrganizationId(
|
||||
ctx.session.activeOrganizationId,
|
||||
);
|
||||
@@ -210,6 +264,12 @@ export const aiRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
analyzeLogs: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Analyze logs with AI",
|
||||
description: "Sends build or runtime logs to the specified AI provider for analysis. Returns a summary of issues found, root causes, and suggested fixes.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
aiId: z.string().min(1),
|
||||
@@ -268,6 +328,12 @@ ${input.logs}`,
|
||||
}),
|
||||
|
||||
testConnection: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Test AI provider connection",
|
||||
description: "Sends a minimal prompt to the specified AI provider and model to verify the API URL, key, and model are valid and reachable.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
apiUrl: z.string().min(1),
|
||||
@@ -302,6 +368,12 @@ ${input.logs}`,
|
||||
}),
|
||||
|
||||
suggest: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Suggest deployment variants",
|
||||
description: "Uses AI to generate deployment configuration suggestions (docker-compose variants) based on the user's input prompt.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
aiId: z.string(),
|
||||
@@ -323,6 +395,12 @@ ${input.logs}`,
|
||||
}
|
||||
}),
|
||||
deploy: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Deploy AI suggestion",
|
||||
description: "Deploys an AI-generated suggestion by creating a compose service with its docker-compose file, environment variables, domains, and config file mounts.",
|
||||
},
|
||||
})
|
||||
.input(deploySuggestionSchema)
|
||||
.mutation(async ({ ctx, input }) => {
|
||||
const environment = await findEnvironmentById(input.environmentId);
|
||||
|
||||
@@ -79,6 +79,12 @@ import { cancelDeployment, deploy } from "@/server/utils/deploy";
|
||||
|
||||
export const applicationRouter = createTRPCRouter({
|
||||
create: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Create an application",
|
||||
description: "Creates a new application in the specified project environment. Supports GitHub, GitLab, Bitbucket, Git, Docker image, and drop sources.",
|
||||
},
|
||||
})
|
||||
.input(apiCreateApplication)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -134,6 +140,12 @@ export const applicationRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
one: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get an application",
|
||||
description: "Retrieves detailed information about an application by its ID, including git provider access status and deployment configuration.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneApplication)
|
||||
.query(async ({ input, ctx }) => {
|
||||
await checkServiceAccess(ctx, input.applicationId, "read");
|
||||
@@ -189,6 +201,12 @@ export const applicationRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
reload: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Reload an application",
|
||||
description: "Restarts the Docker container for the application by mechanizing it. Resets the application status to idle, then to done on success or error on failure.",
|
||||
},
|
||||
})
|
||||
.input(apiReloadApplication)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.applicationId, {
|
||||
@@ -218,6 +236,12 @@ export const applicationRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
delete: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Delete an application",
|
||||
description: "Permanently deletes an application and cleans up all associated resources including Docker services, Traefik configuration, deployments, middlewares, and source code.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneApplication)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServiceAccess(ctx, input.applicationId, "delete");
|
||||
@@ -279,6 +303,12 @@ export const applicationRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
stop: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Stop an application",
|
||||
description: "Stops the running Docker service for the application and sets its status to idle.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneApplication)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.applicationId, {
|
||||
@@ -301,6 +331,12 @@ export const applicationRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
start: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Start an application",
|
||||
description: "Starts the Docker service for the application and sets its status to done.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneApplication)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.applicationId, {
|
||||
@@ -323,6 +359,12 @@ export const applicationRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
redeploy: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Redeploy an application",
|
||||
description: "Triggers a rebuild and redeployment of the application. Queues a deployment job or executes it directly for cloud servers.",
|
||||
},
|
||||
})
|
||||
.input(apiRedeployApplication)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.applicationId, {
|
||||
@@ -367,6 +409,12 @@ export const applicationRouter = createTRPCRouter({
|
||||
});
|
||||
}),
|
||||
saveEnvironment: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Save environment variables",
|
||||
description: "Updates the environment variables, build arguments, and build secrets for an application.",
|
||||
},
|
||||
})
|
||||
.input(apiSaveEnvironmentVariables)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.applicationId, {
|
||||
@@ -388,6 +436,12 @@ export const applicationRouter = createTRPCRouter({
|
||||
return true;
|
||||
}),
|
||||
saveBuildType: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Save build type configuration",
|
||||
description: "Updates the build type and related settings for an application, including Dockerfile path, build context, publish directory, and build stage.",
|
||||
},
|
||||
})
|
||||
.input(apiSaveBuildType)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.applicationId, {
|
||||
@@ -413,6 +467,12 @@ export const applicationRouter = createTRPCRouter({
|
||||
return true;
|
||||
}),
|
||||
saveGithubProvider: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Save GitHub provider",
|
||||
description: "Configures the application to use a GitHub repository as its source, setting the repository, branch, owner, and build path.",
|
||||
},
|
||||
})
|
||||
.input(apiSaveGithubProvider)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.applicationId, {
|
||||
@@ -440,6 +500,12 @@ export const applicationRouter = createTRPCRouter({
|
||||
return true;
|
||||
}),
|
||||
saveGitlabProvider: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Save GitLab provider",
|
||||
description: "Configures the application to use a GitLab repository as its source, setting the repository, branch, owner, build path, and project ID.",
|
||||
},
|
||||
})
|
||||
.input(apiSaveGitlabProvider)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.applicationId, {
|
||||
@@ -468,6 +534,12 @@ export const applicationRouter = createTRPCRouter({
|
||||
return true;
|
||||
}),
|
||||
saveBitbucketProvider: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Save Bitbucket provider",
|
||||
description: "Configures the application to use a Bitbucket repository as its source, setting the repository, branch, owner, and build path.",
|
||||
},
|
||||
})
|
||||
.input(apiSaveBitbucketProvider)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.applicationId, {
|
||||
@@ -495,6 +567,12 @@ export const applicationRouter = createTRPCRouter({
|
||||
return true;
|
||||
}),
|
||||
saveGiteaProvider: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Save Gitea provider",
|
||||
description: "Configures the application to use a Gitea repository as its source, setting the repository, branch, owner, and build path.",
|
||||
},
|
||||
})
|
||||
.input(apiSaveGiteaProvider)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.applicationId, {
|
||||
@@ -521,6 +599,12 @@ export const applicationRouter = createTRPCRouter({
|
||||
return true;
|
||||
}),
|
||||
saveDockerProvider: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Save Docker provider",
|
||||
description: "Configures the application to use a Docker image as its source, setting the image name, registry URL, and optional credentials.",
|
||||
},
|
||||
})
|
||||
.input(apiSaveDockerProvider)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.applicationId, {
|
||||
@@ -544,6 +628,12 @@ export const applicationRouter = createTRPCRouter({
|
||||
return true;
|
||||
}),
|
||||
saveGitProvider: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Save Git provider",
|
||||
description: "Configures the application to use a custom Git repository URL as its source, with optional SSH key authentication.",
|
||||
},
|
||||
})
|
||||
.input(apiSaveGitProvider)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.applicationId, {
|
||||
@@ -569,6 +659,12 @@ export const applicationRouter = createTRPCRouter({
|
||||
return true;
|
||||
}),
|
||||
disconnectGitProvider: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Disconnect git provider",
|
||||
description: "Removes all git provider configuration from the application, resetting source type to default and clearing repository, branch, and owner fields for all providers.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneApplication)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.applicationId, {
|
||||
@@ -622,6 +718,12 @@ export const applicationRouter = createTRPCRouter({
|
||||
return true;
|
||||
}),
|
||||
markRunning: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Mark application as running",
|
||||
description: "Sets the application status to running. Used to indicate that a deployment is in progress.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneApplication)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.applicationId, {
|
||||
@@ -637,6 +739,12 @@ export const applicationRouter = createTRPCRouter({
|
||||
});
|
||||
}),
|
||||
update: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Update an application",
|
||||
description: "Updates the general configuration of an application such as name, description, memory limits, CPU limits, and other settings.",
|
||||
},
|
||||
})
|
||||
.input(apiUpdateApplication)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.applicationId, {
|
||||
@@ -673,6 +781,12 @@ export const applicationRouter = createTRPCRouter({
|
||||
return true;
|
||||
}),
|
||||
refreshToken: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Refresh deploy token",
|
||||
description: "Regenerates the webhook refresh token for the application, invalidating the previous token used for triggering deployments.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneApplication)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.applicationId, {
|
||||
@@ -691,6 +805,12 @@ export const applicationRouter = createTRPCRouter({
|
||||
return true;
|
||||
}),
|
||||
deploy: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Deploy an application",
|
||||
description: "Triggers a new deployment for the application. Queues a deployment job or executes it directly for cloud servers.",
|
||||
},
|
||||
})
|
||||
.input(apiDeployApplication)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.applicationId, {
|
||||
@@ -735,6 +855,12 @@ export const applicationRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
cleanQueues: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Clean deployment queues",
|
||||
description: "Removes all pending deployment jobs from the queue for the specified application.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneApplication)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.applicationId, {
|
||||
@@ -743,6 +869,12 @@ export const applicationRouter = createTRPCRouter({
|
||||
await cleanQueuesByApplication(input.applicationId);
|
||||
}),
|
||||
clearDeployments: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Clear old deployments",
|
||||
description: "Removes old deployment logs and artifacts for the application to free up disk space.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneApplication)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.applicationId, {
|
||||
@@ -759,6 +891,12 @@ export const applicationRouter = createTRPCRouter({
|
||||
return true;
|
||||
}),
|
||||
killBuild: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Kill active build",
|
||||
description: "Forcefully terminates the currently running Docker build process for the application.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneApplication)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.applicationId, {
|
||||
@@ -774,6 +912,12 @@ export const applicationRouter = createTRPCRouter({
|
||||
});
|
||||
}),
|
||||
readTraefikConfig: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Read Traefik configuration",
|
||||
description: "Reads the current Traefik reverse proxy configuration file for the application. Supports both local and remote server configurations.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneApplication)
|
||||
.query(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.applicationId, {
|
||||
@@ -793,6 +937,12 @@ export const applicationRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
dropDeployment: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Deploy from zip upload",
|
||||
description: "Deploys an application from an uploaded zip file. Unzips the file into the application directory and triggers a deployment.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
zfd.formData({
|
||||
applicationId: z.string(),
|
||||
@@ -849,6 +999,12 @@ export const applicationRouter = createTRPCRouter({
|
||||
return true;
|
||||
}),
|
||||
updateTraefikConfig: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Update Traefik configuration",
|
||||
description: "Writes a new Traefik reverse proxy configuration for the application. Supports both local and remote server configurations.",
|
||||
},
|
||||
})
|
||||
.input(z.object({ applicationId: z.string(), traefikConfig: z.string() }))
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.applicationId, {
|
||||
@@ -873,6 +1029,12 @@ export const applicationRouter = createTRPCRouter({
|
||||
return true;
|
||||
}),
|
||||
readAppMonitoring: withPermission("monitoring", "read")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Read application monitoring stats",
|
||||
description: "Retrieves CPU and memory monitoring statistics for the application. Only available in self-hosted mode.",
|
||||
},
|
||||
})
|
||||
.input(apiFindMonitoringStats)
|
||||
.query(async ({ input }) => {
|
||||
if (IS_CLOUD) {
|
||||
@@ -886,6 +1048,12 @@ export const applicationRouter = createTRPCRouter({
|
||||
return stats;
|
||||
}),
|
||||
move: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Move application to another environment",
|
||||
description: "Moves an application to a different environment within the same project or to another project's environment.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
applicationId: z.string(),
|
||||
@@ -922,6 +1090,12 @@ export const applicationRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
cancelDeployment: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Cancel a deployment",
|
||||
description: "Cancels an in-progress deployment for the application and resets its status to idle. Only available in cloud version.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneApplication)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.applicationId, {
|
||||
@@ -972,6 +1146,12 @@ export const applicationRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
search: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Search applications",
|
||||
description: "Searches applications by name, appName, description, repository, owner, or Docker image with pagination. Respects service-level access control.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
q: z.string().optional(),
|
||||
@@ -1104,6 +1284,12 @@ export const applicationRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
readLogs: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Read application logs",
|
||||
description: "Retrieves Docker container logs for the application with configurable tail length, time range, and optional text search filtering.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
apiFindOneApplication.extend({
|
||||
tail: z.number().int().min(1).max(10000).default(100),
|
||||
|
||||
@@ -78,6 +78,12 @@ interface RcloneFile {
|
||||
|
||||
export const backupRouter = createTRPCRouter({
|
||||
create: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Create a backup",
|
||||
description: "Creates a new backup configuration for a database or compose service. If enabled, automatically schedules the backup according to the provided cron expression.",
|
||||
},
|
||||
})
|
||||
.input(apiCreateBackup)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -152,6 +158,12 @@ export const backupRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
one: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get a backup",
|
||||
description: "Returns the details of a specific backup configuration by its ID.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneBackup)
|
||||
.query(async ({ input, ctx }) => {
|
||||
const backup = await findBackupById(input.backupId);
|
||||
@@ -172,6 +184,12 @@ export const backupRouter = createTRPCRouter({
|
||||
return backup;
|
||||
}),
|
||||
update: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Update a backup",
|
||||
description: "Updates an existing backup configuration. Reschedules or removes the backup job depending on the enabled state.",
|
||||
},
|
||||
})
|
||||
.input(apiUpdateBackup)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -229,6 +247,12 @@ export const backupRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
remove: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Delete a backup",
|
||||
description: "Permanently removes a backup configuration and unschedules any associated backup job.",
|
||||
},
|
||||
})
|
||||
.input(apiRemoveBackup)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -272,6 +296,12 @@ export const backupRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
manualBackupPostgres: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Run a PostgreSQL backup manually",
|
||||
description: "Immediately executes a PostgreSQL backup using the specified backup configuration. Cleans up old backups according to retention settings.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneBackup)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -303,6 +333,12 @@ export const backupRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
manualBackupMySql: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Run a MySQL backup manually",
|
||||
description: "Immediately executes a MySQL backup using the specified backup configuration. Cleans up old backups according to retention settings.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneBackup)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -330,6 +366,12 @@ export const backupRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
manualBackupMariadb: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Run a MariaDB backup manually",
|
||||
description: "Immediately executes a MariaDB backup using the specified backup configuration. Cleans up old backups according to retention settings.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneBackup)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -357,6 +399,12 @@ export const backupRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
manualBackupCompose: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Run a Compose backup manually",
|
||||
description: "Immediately executes a Compose service backup using the specified backup configuration. Cleans up old backups according to retention settings.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneBackup)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -384,6 +432,12 @@ export const backupRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
manualBackupMongo: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Run a MongoDB backup manually",
|
||||
description: "Immediately executes a MongoDB backup using the specified backup configuration. Cleans up old backups according to retention settings.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneBackup)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -411,6 +465,12 @@ export const backupRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
manualBackupLibsql: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Run a LibSQL backup manually",
|
||||
description: "Immediately executes a LibSQL backup using the specified backup configuration. Cleans up old backups according to retention settings.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneBackup)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -438,6 +498,12 @@ export const backupRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
manualBackupWebServer: withPermission("backup", "create")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Run a web server backup manually",
|
||||
description: "Immediately executes a web server backup using the specified backup configuration. Cleans up old backups according to retention settings.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneBackup)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const backup = await findBackupById(input.backupId);
|
||||
@@ -451,6 +517,12 @@ export const backupRouter = createTRPCRouter({
|
||||
return true;
|
||||
}),
|
||||
listBackupFiles: withPermission("backup", "read")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "List backup files in S3",
|
||||
description: "Lists backup files stored in the S3 destination bucket. Supports searching by path prefix and returns up to 100 results.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
destinationId: z.string(),
|
||||
|
||||
@@ -25,6 +25,12 @@ import {
|
||||
|
||||
export const bitbucketRouter = createTRPCRouter({
|
||||
create: withPermission("gitProviders", "create")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Create Bitbucket provider",
|
||||
description: "Creates a new Bitbucket provider configuration linked to the active organization. Requires gitProviders create permission.",
|
||||
},
|
||||
})
|
||||
.input(apiCreateBitbucket)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -50,11 +56,24 @@ export const bitbucketRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
one: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get Bitbucket provider",
|
||||
description: "Returns a single Bitbucket provider configuration by its ID.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneBitbucket)
|
||||
.query(async ({ input }) => {
|
||||
return await findBitbucketById(input.bitbucketId);
|
||||
}),
|
||||
bitbucketProviders: protectedProcedure.query(async ({ ctx }) => {
|
||||
bitbucketProviders: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "List Bitbucket providers",
|
||||
description: "Returns all Bitbucket providers accessible to the current user within the active organization.",
|
||||
},
|
||||
})
|
||||
.query(async ({ ctx }) => {
|
||||
const accessibleIds = await getAccessibleGitProviderIds(ctx.session);
|
||||
|
||||
let result = await db.query.bitbucket.findMany({
|
||||
@@ -77,16 +96,34 @@ export const bitbucketRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
getBitbucketRepositories: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "List Bitbucket repositories",
|
||||
description: "Fetches the list of repositories accessible by the Bitbucket provider. Calls the Bitbucket API using the provider's credentials.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneBitbucket)
|
||||
.query(async ({ input }) => {
|
||||
return await getBitbucketRepositories(input.bitbucketId);
|
||||
}),
|
||||
getBitbucketBranches: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "List Bitbucket branches",
|
||||
description: "Fetches the list of branches for a specific Bitbucket repository. Calls the Bitbucket API using the provider's credentials.",
|
||||
},
|
||||
})
|
||||
.input(apiFindBitbucketBranches)
|
||||
.query(async ({ input }) => {
|
||||
return await getBitbucketBranches(input);
|
||||
}),
|
||||
testConnection: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Test Bitbucket connection",
|
||||
description: "Tests the connection to a Bitbucket provider by attempting to fetch its repositories. Returns the number of repositories found or throws an error on failure.",
|
||||
},
|
||||
})
|
||||
.input(apiBitbucketTestConnection)
|
||||
.mutation(async ({ input }) => {
|
||||
try {
|
||||
@@ -101,6 +138,12 @@ export const bitbucketRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
update: withPermission("gitProviders", "create")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Update Bitbucket provider",
|
||||
description: "Updates a Bitbucket provider configuration. Requires gitProviders create permission.",
|
||||
},
|
||||
})
|
||||
.input(apiUpdateBitbucket)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const result = await updateBitbucket(input.bitbucketId, {
|
||||
|
||||
@@ -19,6 +19,12 @@ import {
|
||||
|
||||
export const certificateRouter = createTRPCRouter({
|
||||
create: withPermission("certificate", "create")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Create a certificate",
|
||||
description: "Creates a new SSL/TLS certificate. In cloud mode, a server must be specified. Logs an audit entry upon creation.",
|
||||
},
|
||||
})
|
||||
.input(apiCreateCertificate)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
if (IS_CLOUD && !input.serverId) {
|
||||
@@ -41,6 +47,12 @@ export const certificateRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
one: withPermission("certificate", "read")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get a certificate",
|
||||
description: "Returns a single certificate by its ID. Verifies that the certificate belongs to the current organization.",
|
||||
},
|
||||
})
|
||||
.input(apiFindCertificate)
|
||||
.query(async ({ input, ctx }) => {
|
||||
const certificates = await findCertificateById(input.certificateId);
|
||||
@@ -53,6 +65,12 @@ export const certificateRouter = createTRPCRouter({
|
||||
return certificates;
|
||||
}),
|
||||
remove: withPermission("certificate", "delete")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Delete a certificate",
|
||||
description: "Deletes a certificate by its ID after verifying organization ownership. Logs an audit entry before removal.",
|
||||
},
|
||||
})
|
||||
.input(apiFindCertificate)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const certificates = await findCertificateById(input.certificateId);
|
||||
@@ -71,7 +89,14 @@ export const certificateRouter = createTRPCRouter({
|
||||
await removeCertificateById(input.certificateId);
|
||||
return true;
|
||||
}),
|
||||
all: withPermission("certificate", "read").query(async ({ ctx }) => {
|
||||
all: withPermission("certificate", "read")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "List all certificates",
|
||||
description: "Returns all certificates belonging to the current organization, including their associated server information.",
|
||||
},
|
||||
})
|
||||
.query(async ({ ctx }) => {
|
||||
return await db.query.certificates.findMany({
|
||||
where: eq(certificates.organizationId, ctx.session.activeOrganizationId),
|
||||
with: {
|
||||
@@ -80,6 +105,12 @@ export const certificateRouter = createTRPCRouter({
|
||||
});
|
||||
}),
|
||||
update: withPermission("certificate", "update")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Update a certificate",
|
||||
description: "Updates the name, certificate data, and private key of an existing certificate. Verifies organization ownership before applying changes.",
|
||||
},
|
||||
})
|
||||
.input(apiUpdateCertificate)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const certificate = await findCertificateById(input.certificateId);
|
||||
|
||||
@@ -13,6 +13,12 @@ import { createTRPCRouter, withPermission } from "../trpc";
|
||||
|
||||
export const clusterRouter = createTRPCRouter({
|
||||
getNodes: withPermission("server", "read")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get cluster nodes",
|
||||
description: "Retrieves all nodes in the Docker Swarm cluster. Optionally targets a remote server.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
serverId: z.string().optional(),
|
||||
@@ -25,6 +31,12 @@ export const clusterRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
removeWorker: withPermission("server", "delete")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Remove a worker node",
|
||||
description: "Drains and forcefully removes a worker node from the Docker Swarm cluster. An audit log entry is created for the removal.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
nodeId: z.string(),
|
||||
@@ -60,6 +72,12 @@ export const clusterRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
addWorker: withPermission("server", "create")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get worker join command",
|
||||
description: "Returns the Docker Swarm join command and token for adding a new worker node to the cluster, along with the Docker version.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
serverId: z.string().optional(),
|
||||
@@ -83,6 +101,12 @@ export const clusterRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
addManager: withPermission("server", "create")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get manager join command",
|
||||
description: "Returns the Docker Swarm join command and token for adding a new manager node to the cluster, along with the Docker version.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
serverId: z.string().optional(),
|
||||
|
||||
@@ -83,6 +83,12 @@ import { audit } from "../utils/audit";
|
||||
|
||||
export const composeRouter = createTRPCRouter({
|
||||
create: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Create a compose service",
|
||||
description: "Creates a new Docker Compose service in the specified project environment with the given configuration.",
|
||||
},
|
||||
})
|
||||
.input(apiCreateCompose)
|
||||
.mutation(async ({ ctx, input }) => {
|
||||
try {
|
||||
@@ -133,6 +139,12 @@ export const composeRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
one: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get a compose service",
|
||||
description: "Retrieves detailed information about a compose service by its ID, including git provider access status and deployment configuration.",
|
||||
},
|
||||
})
|
||||
.input(apiFindCompose)
|
||||
.query(async ({ input, ctx }) => {
|
||||
await checkServiceAccess(ctx, input.composeId, "read");
|
||||
@@ -189,6 +201,12 @@ export const composeRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
update: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Update a compose service",
|
||||
description: "Updates the configuration of a compose service such as name, description, compose file content, and other settings.",
|
||||
},
|
||||
})
|
||||
.input(apiUpdateCompose)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.composeId, {
|
||||
@@ -204,6 +222,12 @@ export const composeRouter = createTRPCRouter({
|
||||
return updated;
|
||||
}),
|
||||
saveEnvironment: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Save compose environment variables",
|
||||
description: "Updates the environment variables for a compose service.",
|
||||
},
|
||||
})
|
||||
.input(apiSaveEnvironmentVariablesCompose)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.composeId, {
|
||||
@@ -229,6 +253,12 @@ export const composeRouter = createTRPCRouter({
|
||||
return true;
|
||||
}),
|
||||
delete: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Delete a compose service",
|
||||
description: "Permanently deletes a compose service and cleans up associated Docker resources, deployments, and directories. Optionally deletes associated volumes.",
|
||||
},
|
||||
})
|
||||
.input(apiDeleteCompose)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServiceAccess(ctx, input.composeId, "delete");
|
||||
@@ -279,6 +309,12 @@ export const composeRouter = createTRPCRouter({
|
||||
return composeResult;
|
||||
}),
|
||||
cleanQueues: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Clean deployment queues",
|
||||
description: "Removes all pending deployment jobs from the queue for the specified compose service.",
|
||||
},
|
||||
})
|
||||
.input(apiFindCompose)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.composeId, {
|
||||
@@ -288,6 +324,12 @@ export const composeRouter = createTRPCRouter({
|
||||
return { success: true, message: "Queues cleaned successfully" };
|
||||
}),
|
||||
clearDeployments: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Clear old deployments",
|
||||
description: "Removes old deployment logs and artifacts for the compose service to free up disk space.",
|
||||
},
|
||||
})
|
||||
.input(apiFindCompose)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.composeId, {
|
||||
@@ -304,6 +346,12 @@ export const composeRouter = createTRPCRouter({
|
||||
return true;
|
||||
}),
|
||||
killBuild: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Kill active build",
|
||||
description: "Forcefully terminates the currently running Docker build process for the compose service.",
|
||||
},
|
||||
})
|
||||
.input(apiFindCompose)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.composeId, {
|
||||
@@ -314,6 +362,12 @@ export const composeRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
loadServices: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Load compose services",
|
||||
description: "Parses the compose file and returns the list of services defined in it, with their current container status.",
|
||||
},
|
||||
})
|
||||
.input(apiFetchServices)
|
||||
.query(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.composeId, {
|
||||
@@ -322,6 +376,12 @@ export const composeRouter = createTRPCRouter({
|
||||
return await loadServices(input.composeId, input.type);
|
||||
}),
|
||||
loadMountsByService: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Load mounts by service",
|
||||
description: "Retrieves the Docker volume mounts for a specific service within a compose stack by inspecting the running container.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
composeId: z.string().min(1),
|
||||
@@ -340,6 +400,12 @@ export const composeRouter = createTRPCRouter({
|
||||
return mounts;
|
||||
}),
|
||||
fetchSourceType: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Fetch and clone source",
|
||||
description: "Clones the compose repository from the configured git provider and returns the source type. Executes the clone command locally or on a remote server.",
|
||||
},
|
||||
})
|
||||
.input(apiFindCompose)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -365,6 +431,12 @@ export const composeRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
randomizeCompose: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Randomize compose file",
|
||||
description: "Adds a random suffix to service names and volumes in the compose file to avoid naming conflicts between deployments.",
|
||||
},
|
||||
})
|
||||
.input(apiRandomizeCompose)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.composeId, {
|
||||
@@ -381,6 +453,12 @@ export const composeRouter = createTRPCRouter({
|
||||
return result;
|
||||
}),
|
||||
isolatedDeployment: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Randomize for isolated deployment",
|
||||
description: "Randomizes the compose file for isolated deployment mode, ensuring unique service and volume names to support parallel deployments.",
|
||||
},
|
||||
})
|
||||
.input(apiRandomizeCompose)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.composeId, {
|
||||
@@ -400,6 +478,12 @@ export const composeRouter = createTRPCRouter({
|
||||
return result;
|
||||
}),
|
||||
getConvertedCompose: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get converted compose file",
|
||||
description: "Returns the compose file with domains injected as Traefik labels, converted to YAML format ready for deployment.",
|
||||
},
|
||||
})
|
||||
.input(apiFindCompose)
|
||||
.query(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.composeId, {
|
||||
@@ -414,6 +498,12 @@ export const composeRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
deploy: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Deploy a compose service",
|
||||
description: "Triggers a new deployment for the compose service. Queues a deployment job or executes it directly for cloud servers.",
|
||||
},
|
||||
})
|
||||
.input(apiDeployCompose)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.composeId, {
|
||||
@@ -464,6 +554,12 @@ export const composeRouter = createTRPCRouter({
|
||||
};
|
||||
}),
|
||||
redeploy: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Redeploy a compose service",
|
||||
description: "Triggers a rebuild and redeployment of the compose service. Queues a deployment job or executes it directly for cloud servers.",
|
||||
},
|
||||
})
|
||||
.input(apiRedeployCompose)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.composeId, {
|
||||
@@ -512,6 +608,12 @@ export const composeRouter = createTRPCRouter({
|
||||
};
|
||||
}),
|
||||
stop: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Stop a compose service",
|
||||
description: "Stops all running containers for the compose service using docker compose stop.",
|
||||
},
|
||||
})
|
||||
.input(apiFindCompose)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.composeId, {
|
||||
@@ -528,6 +630,12 @@ export const composeRouter = createTRPCRouter({
|
||||
return true;
|
||||
}),
|
||||
start: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Start a compose service",
|
||||
description: "Starts all containers for the compose service using docker compose start.",
|
||||
},
|
||||
})
|
||||
.input(apiFindCompose)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.composeId, {
|
||||
@@ -544,6 +652,12 @@ export const composeRouter = createTRPCRouter({
|
||||
return true;
|
||||
}),
|
||||
getDefaultCommand: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get default compose command",
|
||||
description: "Generates and returns the default docker compose command that would be used to deploy the service.",
|
||||
},
|
||||
})
|
||||
.input(apiFindCompose)
|
||||
.query(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.composeId, {
|
||||
@@ -554,6 +668,12 @@ export const composeRouter = createTRPCRouter({
|
||||
return `docker ${command}`;
|
||||
}),
|
||||
refreshToken: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Refresh deploy token",
|
||||
description: "Regenerates the webhook refresh token for the compose service, invalidating the previous token used for triggering deployments.",
|
||||
},
|
||||
})
|
||||
.input(apiFindCompose)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.composeId, {
|
||||
@@ -572,6 +692,12 @@ export const composeRouter = createTRPCRouter({
|
||||
return true;
|
||||
}),
|
||||
deployTemplate: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Deploy a template",
|
||||
description: "Creates a new compose service from a template by fetching its files, processing variables, creating mounts and domains, and setting up the compose configuration.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
environmentId: z.string(),
|
||||
@@ -680,6 +806,12 @@ export const composeRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
templates: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "List available templates",
|
||||
description: "Fetches the list of available compose templates from the GitHub templates repository.",
|
||||
},
|
||||
})
|
||||
.input(z.object({ baseUrl: z.string().optional() }))
|
||||
.query(async ({ input }) => {
|
||||
try {
|
||||
@@ -698,6 +830,12 @@ export const composeRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
getTags: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get template tags",
|
||||
description: "Fetches all unique tags from the available compose templates for filtering purposes.",
|
||||
},
|
||||
})
|
||||
.input(z.object({ baseUrl: z.string().optional() }))
|
||||
.query(async ({ input }) => {
|
||||
const githubTemplates = await fetchTemplatesList(input.baseUrl);
|
||||
@@ -707,6 +845,12 @@ export const composeRouter = createTRPCRouter({
|
||||
return uniqueTags;
|
||||
}),
|
||||
disconnectGitProvider: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Disconnect git provider",
|
||||
description: "Removes all git provider configuration from the compose service, resetting source type to default and clearing repository, branch, and owner fields for all providers.",
|
||||
},
|
||||
})
|
||||
.input(apiFindCompose)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.composeId, {
|
||||
@@ -759,6 +903,12 @@ export const composeRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
move: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Move compose to another environment",
|
||||
description: "Moves a compose service to a different environment within the same project or to another project's environment.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
composeId: z.string(),
|
||||
@@ -796,6 +946,12 @@ export const composeRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
processTemplate: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Process a template",
|
||||
description: "Processes a base64-encoded template configuration, resolving variables and generating the compose file and environment settings without applying them.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
base64: z.string(),
|
||||
@@ -860,6 +1016,12 @@ export const composeRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
import: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Import a template",
|
||||
description: "Imports a base64-encoded template into an existing compose service, replacing its compose file, environment variables, mounts, and domains with the template's configuration.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
base64: z.string(),
|
||||
@@ -972,6 +1134,12 @@ export const composeRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
cancelDeployment: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Cancel a deployment",
|
||||
description: "Cancels an in-progress deployment for the compose service and resets its status to idle. Only available in cloud version.",
|
||||
},
|
||||
})
|
||||
.input(apiFindCompose)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.composeId, {
|
||||
@@ -1025,6 +1193,12 @@ export const composeRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
search: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Search compose services",
|
||||
description: "Searches compose services by name, appName, or description with pagination. Respects service-level access control.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
q: z.string().optional(),
|
||||
@@ -1133,6 +1307,12 @@ export const composeRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
readLogs: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Read compose container logs",
|
||||
description: "Retrieves Docker container logs for a specific container within the compose service with configurable tail length, time range, and optional text search filtering.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
apiFindCompose.extend({
|
||||
containerId: z
|
||||
|
||||
@@ -34,6 +34,12 @@ import { createTRPCRouter, protectedProcedure, withPermission } from "../trpc";
|
||||
|
||||
export const deploymentRouter = createTRPCRouter({
|
||||
all: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "List deployments by application",
|
||||
description: "Returns all deployments associated with the given application, ordered by creation date.",
|
||||
},
|
||||
})
|
||||
.input(apiFindAllByApplication)
|
||||
.query(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.applicationId, {
|
||||
@@ -43,6 +49,12 @@ export const deploymentRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
allByCompose: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "List deployments by compose",
|
||||
description: "Returns all deployments associated with the given compose service.",
|
||||
},
|
||||
})
|
||||
.input(apiFindAllByCompose)
|
||||
.query(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.composeId, {
|
||||
@@ -51,11 +63,24 @@ export const deploymentRouter = createTRPCRouter({
|
||||
return await findAllDeploymentsByComposeId(input.composeId);
|
||||
}),
|
||||
allByServer: withPermission("deployment", "read")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "List deployments by server",
|
||||
description: "Returns all deployments associated with the given server.",
|
||||
},
|
||||
})
|
||||
.input(apiFindAllByServer)
|
||||
.query(async ({ input }) => {
|
||||
return await findAllDeploymentsByServerId(input.serverId);
|
||||
}),
|
||||
allCentralized: withPermission("deployment", "read").query(
|
||||
allCentralized: withPermission("deployment", "read")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "List all deployments centralized",
|
||||
description: "Returns all deployments across all services in the organization. Non-admin users only see deployments for their accessible services.",
|
||||
},
|
||||
})
|
||||
.query(
|
||||
async ({ ctx }) => {
|
||||
const orgId = ctx.session.activeOrganizationId;
|
||||
const accessedServices =
|
||||
@@ -69,7 +94,14 @@ export const deploymentRouter = createTRPCRouter({
|
||||
},
|
||||
),
|
||||
|
||||
queueList: withPermission("deployment", "read").query(async ({ ctx }) => {
|
||||
queueList: withPermission("deployment", "read")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "List deployment queue jobs",
|
||||
description: "Returns all jobs in the deployment queue with their current state, timestamps, and resolved service paths.",
|
||||
},
|
||||
})
|
||||
.query(async ({ ctx }) => {
|
||||
const orgId = ctx.session.activeOrganizationId;
|
||||
let rows: QueueJobRow[];
|
||||
|
||||
@@ -116,6 +148,12 @@ export const deploymentRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
allByType: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "List deployments by service type",
|
||||
description: "Returns all deployments for a given service ID and type (application, compose, etc.), including associated rollback information.",
|
||||
},
|
||||
})
|
||||
.input(apiFindAllByType)
|
||||
.query(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.id, {
|
||||
@@ -131,6 +169,12 @@ export const deploymentRouter = createTRPCRouter({
|
||||
return deploymentsList;
|
||||
}),
|
||||
killProcess: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Cancel a running deployment",
|
||||
description: "Kills the running process of a deployment by sending SIGKILL to its PID. Updates the deployment status to error.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
deploymentId: z.string().min(1),
|
||||
@@ -168,6 +212,12 @@ export const deploymentRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
removeDeployment: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Delete a deployment",
|
||||
description: "Permanently removes a deployment record and its associated data.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
deploymentId: z.string().min(1),
|
||||
|
||||
@@ -22,6 +22,12 @@ import {
|
||||
|
||||
export const destinationRouter = createTRPCRouter({
|
||||
create: withPermission("destination", "create")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Create backup destination",
|
||||
description: "Creates a new S3-compatible backup destination for the current organization and logs an audit event.",
|
||||
},
|
||||
})
|
||||
.input(apiCreateDestination)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -45,6 +51,12 @@ export const destinationRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
testConnection: withPermission("destination", "create")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Test backup destination connection",
|
||||
description: "Tests connectivity to an S3-compatible bucket using rclone. Runs locally or on a remote server depending on configuration.",
|
||||
},
|
||||
})
|
||||
.input(apiCreateDestination)
|
||||
.mutation(async ({ input }) => {
|
||||
const {
|
||||
@@ -102,6 +114,12 @@ export const destinationRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
one: withPermission("destination", "read")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get backup destination",
|
||||
description: "Returns a single backup destination by ID. Verifies the caller belongs to the same organization.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneDestination)
|
||||
.query(async ({ input, ctx }) => {
|
||||
const destination = await findDestinationById(input.destinationId);
|
||||
@@ -113,13 +131,26 @@ export const destinationRouter = createTRPCRouter({
|
||||
}
|
||||
return destination;
|
||||
}),
|
||||
all: withPermission("destination", "read").query(async ({ ctx }) => {
|
||||
all: withPermission("destination", "read")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "List all backup destinations",
|
||||
description: "Returns all S3-compatible backup destinations for the current organization, ordered by creation date descending.",
|
||||
},
|
||||
})
|
||||
.query(async ({ ctx }) => {
|
||||
return await db.query.destinations.findMany({
|
||||
where: eq(destinations.organizationId, ctx.session.activeOrganizationId),
|
||||
orderBy: [desc(destinations.createdAt)],
|
||||
});
|
||||
}),
|
||||
remove: withPermission("destination", "delete")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Delete backup destination",
|
||||
description: "Removes a backup destination by ID. Verifies organization ownership and logs an audit event before deletion.",
|
||||
},
|
||||
})
|
||||
.input(apiRemoveDestination)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -147,6 +178,12 @@ export const destinationRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
update: withPermission("destination", "create")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Update backup destination",
|
||||
description: "Updates an existing backup destination. Verifies organization ownership before applying changes and logs an audit event.",
|
||||
},
|
||||
})
|
||||
.input(apiUpdateDestination)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
|
||||
@@ -20,6 +20,12 @@ export const containerIdRegex = /^[a-zA-Z0-9.\-_]+$/;
|
||||
|
||||
export const dockerRouter = createTRPCRouter({
|
||||
getContainers: withPermission("docker", "read")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get Docker containers",
|
||||
description: "Retrieves a list of all Docker containers. Optionally targets a specific remote server by ID.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
serverId: z.string().optional(),
|
||||
@@ -36,6 +42,12 @@ export const dockerRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
restartContainer: withPermission("docker", "read")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Restart a Docker container",
|
||||
description: "Restarts a Docker container by its ID. An audit log entry is created for the action.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
containerId: z
|
||||
@@ -56,6 +68,12 @@ export const dockerRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
removeContainer: withPermission("docker", "read")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Remove a Docker container",
|
||||
description: "Removes a Docker container by its ID. Optionally targets a remote server. An audit log entry is created for the deletion.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
containerId: z
|
||||
@@ -82,6 +100,12 @@ export const dockerRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
getConfig: withPermission("docker", "read")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get Docker container configuration",
|
||||
description: "Retrieves the configuration (inspect data) for a specific Docker container. Optionally targets a remote server.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
containerId: z
|
||||
@@ -102,6 +126,12 @@ export const dockerRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
getContainersByAppNameMatch: withPermission("service", "read")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get containers by app name match",
|
||||
description: "Retrieves containers whose names match the given application name. Supports filtering by app type (stack or docker-compose) and optionally targets a remote server.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
appType: z.enum(["stack", "docker-compose"]).optional(),
|
||||
@@ -124,6 +154,12 @@ export const dockerRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
getContainersByAppLabel: withPermission("docker", "read")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get containers by app label",
|
||||
description: "Retrieves containers filtered by application label. Supports standalone and swarm deployment types, and optionally targets a remote server.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
appName: z.string().min(1).regex(containerIdRegex, "Invalid app name."),
|
||||
@@ -146,6 +182,12 @@ export const dockerRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
getStackContainersByAppName: withPermission("docker", "read")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get stack containers by app name",
|
||||
description: "Retrieves all containers belonging to a Docker stack by application name. Optionally targets a remote server.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
appName: z.string().min(1).regex(containerIdRegex, "Invalid app name."),
|
||||
@@ -163,6 +205,12 @@ export const dockerRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
getServiceContainersByAppName: withPermission("docker", "read")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get service containers by app name",
|
||||
description: "Retrieves all containers belonging to a Docker Swarm service by application name. Optionally targets a remote server.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
appName: z.string().min(1).regex(containerIdRegex, "Invalid app name."),
|
||||
@@ -180,6 +228,12 @@ export const dockerRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
uploadFileToContainer: withPermission("docker", "read")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Upload a file to a Docker container",
|
||||
description: "Uploads a file to a specified path inside a Docker container. The file is converted to a buffer and transferred to the container's filesystem.",
|
||||
},
|
||||
})
|
||||
.input(uploadFileToContainerSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
if (input.serverId) {
|
||||
|
||||
@@ -33,6 +33,12 @@ import {
|
||||
|
||||
export const domainRouter = createTRPCRouter({
|
||||
create: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Create a domain",
|
||||
description: "Creates a new domain for an application or compose service. Validates permissions and logs an audit entry.",
|
||||
},
|
||||
})
|
||||
.input(apiCreateDomain)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -65,6 +71,12 @@ export const domainRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
byApplicationId: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "List domains by application",
|
||||
description: "Returns all domains associated with a given application ID.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneApplication)
|
||||
.query(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.applicationId, {
|
||||
@@ -73,6 +85,12 @@ export const domainRouter = createTRPCRouter({
|
||||
return await findDomainsByApplicationId(input.applicationId);
|
||||
}),
|
||||
byComposeId: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "List domains by compose service",
|
||||
description: "Returns all domains associated with a given compose service ID.",
|
||||
},
|
||||
})
|
||||
.input(apiFindCompose)
|
||||
.query(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.composeId, {
|
||||
@@ -81,6 +99,12 @@ export const domainRouter = createTRPCRouter({
|
||||
return await findDomainsByComposeId(input.composeId);
|
||||
}),
|
||||
generateDomain: withPermission("domain", "create")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Generate a traefik.me domain",
|
||||
description: "Generates a free traefik.me domain for an application, using the server IP to create a wildcard subdomain.",
|
||||
},
|
||||
})
|
||||
.input(z.object({ appName: z.string(), serverId: z.string().optional() }))
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
return generateTraefikMeDomain(
|
||||
@@ -90,6 +114,12 @@ export const domainRouter = createTRPCRouter({
|
||||
);
|
||||
}),
|
||||
canGenerateTraefikMeDomains: withPermission("domain", "read")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Check traefik.me domain availability",
|
||||
description: "Checks whether traefik.me domains can be generated by returning the server IP address. Returns the IP from the server record or web server settings.",
|
||||
},
|
||||
})
|
||||
.input(z.object({ serverId: z.string() }))
|
||||
.query(async ({ input }) => {
|
||||
if (input.serverId) {
|
||||
@@ -101,6 +131,12 @@ export const domainRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
update: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Update a domain",
|
||||
description: "Updates a domain's configuration and refreshes the Traefik routing rules for the associated application or preview deployment.",
|
||||
},
|
||||
})
|
||||
.input(apiUpdateDomain)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const currentDomain = await findDomainById(input.domainId);
|
||||
@@ -141,7 +177,15 @@ export const domainRouter = createTRPCRouter({
|
||||
}
|
||||
return result;
|
||||
}),
|
||||
one: protectedProcedure.input(apiFindDomain).query(async ({ input, ctx }) => {
|
||||
one: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get a domain",
|
||||
description: "Returns a single domain by its ID. Validates read permissions against the associated service or preview deployment.",
|
||||
},
|
||||
})
|
||||
.input(apiFindDomain)
|
||||
.query(async ({ input, ctx }) => {
|
||||
const domain = await findDomainById(input.domainId);
|
||||
const serviceId = domain.applicationId || domain.composeId;
|
||||
if (serviceId) {
|
||||
@@ -159,6 +203,12 @@ export const domainRouter = createTRPCRouter({
|
||||
return domain;
|
||||
}),
|
||||
delete: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Delete a domain",
|
||||
description: "Deletes a domain by its ID and removes the associated Traefik routing configuration for the application.",
|
||||
},
|
||||
})
|
||||
.input(apiFindDomain)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const domain = await findDomainById(input.domainId);
|
||||
@@ -193,6 +243,12 @@ export const domainRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
validateDomain: withPermission("domain", "read")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Validate a domain",
|
||||
description: "Checks whether a domain's DNS records are correctly configured, optionally verifying against a specific server IP.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
domain: z.string(),
|
||||
|
||||
@@ -63,6 +63,12 @@ const filterEnvironmentServices = (
|
||||
|
||||
export const environmentRouter = createTRPCRouter({
|
||||
create: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Create environment",
|
||||
description: "Creates a new environment within a project. The name 'production' is reserved and cannot be used. Checks creation permissions and logs an audit event.",
|
||||
},
|
||||
})
|
||||
.input(apiCreateEnvironment)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -99,6 +105,12 @@ export const environmentRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
one: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get environment",
|
||||
description: "Returns a single environment by ID with all its services. Non-admin users only see services they have been granted access to.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneEnvironment)
|
||||
.query(async ({ input, ctx }) => {
|
||||
const environment = await findEnvironmentById(input.environmentId);
|
||||
@@ -137,6 +149,12 @@ export const environmentRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
byProjectId: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "List environments by project",
|
||||
description: "Returns all environments for a given project. Non-admin users only see environments and services they have been granted access to.",
|
||||
},
|
||||
})
|
||||
.input(z.object({ projectId: z.string() }))
|
||||
.query(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -183,6 +201,12 @@ export const environmentRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
remove: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Delete environment",
|
||||
description: "Deletes an environment by ID. The default environment cannot be deleted. Checks deletion permissions and environment access before removing.",
|
||||
},
|
||||
})
|
||||
.input(apiRemoveEnvironment)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -229,6 +253,12 @@ export const environmentRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
update: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Update environment",
|
||||
description: "Updates an environment's name, description, or env variables. The default environment cannot be renamed. Checks environment access and env-var write permissions.",
|
||||
},
|
||||
})
|
||||
.input(apiUpdateEnvironment)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -296,6 +326,12 @@ export const environmentRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
duplicate: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Duplicate environment",
|
||||
description: "Creates a copy of an existing environment including its services. Checks environment access and organization ownership before duplicating.",
|
||||
},
|
||||
})
|
||||
.input(apiDuplicateEnvironment)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -343,6 +379,12 @@ export const environmentRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
search: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Search environments",
|
||||
description: "Searches environments by name, description, or project with pagination. Non-admin users only see environments they have been granted access to.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
q: z.string().optional(),
|
||||
|
||||
@@ -21,7 +21,14 @@ import {
|
||||
} from "@/server/db/schema";
|
||||
|
||||
export const gitProviderRouter = createTRPCRouter({
|
||||
getAll: protectedProcedure.query(async ({ ctx }) => {
|
||||
getAll: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "List all git providers",
|
||||
description: "Returns all git providers (GitHub, GitLab, Bitbucket, Gitea) accessible to the current user within the active organization, ordered by creation date.",
|
||||
},
|
||||
})
|
||||
.query(async ({ ctx }) => {
|
||||
const accessibleIds = await getAccessibleGitProviderIds(ctx.session);
|
||||
|
||||
if (accessibleIds.size === 0) {
|
||||
@@ -46,6 +53,12 @@ export const gitProviderRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
toggleShare: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Toggle git provider sharing",
|
||||
description: "Toggles whether a git provider is shared with the entire organization. Only the owner of the provider can change this setting.",
|
||||
},
|
||||
})
|
||||
.input(apiToggleShareGitProvider)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const provider = await findGitProviderById(input.gitProviderId);
|
||||
@@ -73,6 +86,12 @@ export const gitProviderRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
allForPermissions: withPermission("member", "update")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "List git providers for permissions",
|
||||
description: "Returns a minimal list of all git providers in the organization for use in permission assignment UIs. Requires a valid enterprise license and member update permission.",
|
||||
},
|
||||
})
|
||||
.use(async ({ ctx, next }) => {
|
||||
const licensed = await hasValidLicense(ctx.session.activeOrganizationId);
|
||||
if (!licensed) {
|
||||
@@ -96,6 +115,12 @@ export const gitProviderRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
remove: withPermission("gitProviders", "delete")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Remove git provider",
|
||||
description: "Deletes a git provider from the organization. Requires gitProviders delete permission and the provider must belong to the active organization.",
|
||||
},
|
||||
})
|
||||
.input(apiRemoveGitProvider)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
|
||||
@@ -27,6 +27,12 @@ import {
|
||||
|
||||
export const giteaRouter = createTRPCRouter({
|
||||
create: withPermission("gitProviders", "create")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Create Gitea provider",
|
||||
description: "Creates a new Gitea provider configuration linked to the active organization. Requires gitProviders create permission.",
|
||||
},
|
||||
})
|
||||
.input(apiCreateGitea)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -53,11 +59,26 @@ export const giteaRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
|
||||
one: protectedProcedure.input(apiFindOneGitea).query(async ({ input }) => {
|
||||
return await findGiteaById(input.giteaId);
|
||||
}),
|
||||
one: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get Gitea provider",
|
||||
description: "Returns a single Gitea provider configuration by its ID.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneGitea)
|
||||
.query(async ({ input }) => {
|
||||
return await findGiteaById(input.giteaId);
|
||||
}),
|
||||
|
||||
giteaProviders: protectedProcedure.query(async ({ ctx }) => {
|
||||
giteaProviders: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "List Gitea providers",
|
||||
description: "Returns all Gitea providers accessible to the current user within the active organization, filtered to only those with valid credentials.",
|
||||
},
|
||||
})
|
||||
.query(async ({ ctx }) => {
|
||||
const accessibleIds = await getAccessibleGitProviderIds(ctx.session);
|
||||
|
||||
let result = await db.query.gitea.findMany({
|
||||
@@ -88,6 +109,12 @@ export const giteaRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
getGiteaRepositories: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "List Gitea repositories",
|
||||
description: "Fetches the list of repositories accessible by the Gitea provider. Calls the Gitea API using the provider's credentials.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneGitea)
|
||||
.query(async ({ input }) => {
|
||||
const { giteaId } = input;
|
||||
@@ -112,6 +139,12 @@ export const giteaRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
getGiteaBranches: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "List Gitea branches",
|
||||
description: "Fetches the list of branches for a specific Gitea repository. Calls the Gitea API using the provider's credentials.",
|
||||
},
|
||||
})
|
||||
.input(apiFindGiteaBranches)
|
||||
.query(async ({ input }) => {
|
||||
const { giteaId, owner, repositoryName } = input;
|
||||
@@ -140,6 +173,12 @@ export const giteaRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
testConnection: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Test Gitea connection",
|
||||
description: "Tests the connection to a Gitea provider by attempting to fetch its repositories. Returns the number of repositories found or throws an error on failure.",
|
||||
},
|
||||
})
|
||||
.input(apiGiteaTestConnection)
|
||||
.mutation(async ({ input }) => {
|
||||
const giteaId = input.giteaId ?? "";
|
||||
@@ -160,6 +199,12 @@ export const giteaRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
update: withPermission("gitProviders", "create")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Update Gitea provider",
|
||||
description: "Updates a Gitea provider configuration and its associated git provider record. Requires gitProviders create permission.",
|
||||
},
|
||||
})
|
||||
.input(apiUpdateGitea)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
if (input.name) {
|
||||
@@ -188,6 +233,12 @@ export const giteaRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
getGiteaUrl: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get Gitea instance URL",
|
||||
description: "Returns the base URL of the Gitea instance associated with the given provider ID.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneGitea)
|
||||
.query(async ({ input }) => {
|
||||
const { giteaId } = input;
|
||||
|
||||
@@ -22,20 +22,47 @@ import {
|
||||
} from "@/server/db/schema";
|
||||
|
||||
export const githubRouter = createTRPCRouter({
|
||||
one: protectedProcedure.input(apiFindOneGithub).query(async ({ input }) => {
|
||||
return await findGithubById(input.githubId);
|
||||
}),
|
||||
one: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get GitHub provider",
|
||||
description: "Returns a single GitHub provider configuration by its ID.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneGithub)
|
||||
.query(async ({ input }) => {
|
||||
return await findGithubById(input.githubId);
|
||||
}),
|
||||
getGithubRepositories: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "List GitHub repositories",
|
||||
description: "Fetches the list of repositories accessible by the GitHub provider. Calls the GitHub API using the provider's credentials.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneGithub)
|
||||
.query(async ({ input }) => {
|
||||
return await getGithubRepositories(input.githubId);
|
||||
}),
|
||||
getGithubBranches: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "List GitHub branches",
|
||||
description: "Fetches the list of branches for a specific GitHub repository. Calls the GitHub API using the provider's credentials.",
|
||||
},
|
||||
})
|
||||
.input(apiFindGithubBranches)
|
||||
.query(async ({ input }) => {
|
||||
return await getGithubBranches(input);
|
||||
}),
|
||||
githubProviders: protectedProcedure.query(async ({ ctx }) => {
|
||||
githubProviders: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "List GitHub providers",
|
||||
description: "Returns all GitHub providers accessible to the current user within the active organization, filtered to only those with valid credentials.",
|
||||
},
|
||||
})
|
||||
.query(async ({ ctx }) => {
|
||||
const accessibleIds = await getAccessibleGitProviderIds(ctx.session);
|
||||
|
||||
let result = await db.query.github.findMany({
|
||||
@@ -66,6 +93,12 @@ export const githubRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
testConnection: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Test GitHub connection",
|
||||
description: "Tests the connection to a GitHub provider by attempting to fetch its repositories. Returns the number of repositories found or throws an error on failure.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneGithub)
|
||||
.mutation(async ({ input }) => {
|
||||
try {
|
||||
@@ -79,6 +112,12 @@ export const githubRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
update: withPermission("gitProviders", "create")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Update GitHub provider",
|
||||
description: "Updates a GitHub provider configuration and its associated git provider record. Requires gitProviders create permission.",
|
||||
},
|
||||
})
|
||||
.input(apiUpdateGithub)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await updateGitProvider(input.gitProviderId, {
|
||||
|
||||
@@ -27,6 +27,12 @@ import {
|
||||
|
||||
export const gitlabRouter = createTRPCRouter({
|
||||
create: withPermission("gitProviders", "create")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Create GitLab provider",
|
||||
description: "Creates a new GitLab provider configuration linked to the active organization. Requires gitProviders create permission.",
|
||||
},
|
||||
})
|
||||
.input(apiCreateGitlab)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -51,10 +57,25 @@ export const gitlabRouter = createTRPCRouter({
|
||||
});
|
||||
}
|
||||
}),
|
||||
one: protectedProcedure.input(apiFindOneGitlab).query(async ({ input }) => {
|
||||
return await findGitlabById(input.gitlabId);
|
||||
}),
|
||||
gitlabProviders: protectedProcedure.query(async ({ ctx }) => {
|
||||
one: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get GitLab provider",
|
||||
description: "Returns a single GitLab provider configuration by its ID.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneGitlab)
|
||||
.query(async ({ input }) => {
|
||||
return await findGitlabById(input.gitlabId);
|
||||
}),
|
||||
gitlabProviders: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "List GitLab providers",
|
||||
description: "Returns all GitLab providers accessible to the current user within the active organization, filtered to only those with valid credentials.",
|
||||
},
|
||||
})
|
||||
.query(async ({ ctx }) => {
|
||||
const accessibleIds = await getAccessibleGitProviderIds(ctx.session);
|
||||
|
||||
let result = await db.query.gitlab.findMany({
|
||||
@@ -85,17 +106,35 @@ export const gitlabRouter = createTRPCRouter({
|
||||
return filtered;
|
||||
}),
|
||||
getGitlabRepositories: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "List GitLab repositories",
|
||||
description: "Fetches the list of repositories accessible by the GitLab provider. Calls the GitLab API using the provider's credentials.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneGitlab)
|
||||
.query(async ({ input }) => {
|
||||
return await getGitlabRepositories(input.gitlabId);
|
||||
}),
|
||||
|
||||
getGitlabBranches: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "List GitLab branches",
|
||||
description: "Fetches the list of branches for a specific GitLab repository. Calls the GitLab API using the provider's credentials.",
|
||||
},
|
||||
})
|
||||
.input(apiFindGitlabBranches)
|
||||
.query(async ({ input }) => {
|
||||
return await getGitlabBranches(input);
|
||||
}),
|
||||
testConnection: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Test GitLab connection",
|
||||
description: "Tests the connection to a GitLab provider by attempting to fetch its repositories. Returns the number of repositories found or throws an error on failure.",
|
||||
},
|
||||
})
|
||||
.input(apiGitlabTestConnection)
|
||||
.mutation(async ({ input }) => {
|
||||
try {
|
||||
@@ -110,6 +149,12 @@ export const gitlabRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
update: withPermission("gitProviders", "create")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Update GitLab provider",
|
||||
description: "Updates a GitLab provider configuration and its associated git provider record. Requires gitProviders create permission.",
|
||||
},
|
||||
})
|
||||
.input(apiUpdateGitlab)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
if (input.name) {
|
||||
|
||||
@@ -43,6 +43,12 @@ import {
|
||||
} from "@/server/db/schema";
|
||||
export const libsqlRouter = createTRPCRouter({
|
||||
create: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Create a LibSQL database",
|
||||
description: "Creates a new LibSQL database service with the specified configuration, sets up a persistent data volume, and registers it in the project.",
|
||||
},
|
||||
})
|
||||
.input(apiCreateLibsql)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -100,6 +106,12 @@ export const libsqlRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
one: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get a LibSQL database by ID",
|
||||
description: "Returns the full details of a LibSQL database service, including its environment and project configuration.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneLibsql)
|
||||
.query(async ({ input, ctx }) => {
|
||||
await checkServiceAccess(ctx, input.libsqlId, "read");
|
||||
@@ -118,6 +130,12 @@ export const libsqlRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
start: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Start a LibSQL database",
|
||||
description: "Starts the Docker container for the specified LibSQL database and sets its status to done.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneLibsql)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.libsqlId, {
|
||||
@@ -143,6 +161,12 @@ export const libsqlRouter = createTRPCRouter({
|
||||
return libsql;
|
||||
}),
|
||||
stop: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Stop a LibSQL database",
|
||||
description: "Stops the Docker container for the specified LibSQL database and sets its status to idle.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneLibsql)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.libsqlId, {
|
||||
@@ -168,6 +192,12 @@ export const libsqlRouter = createTRPCRouter({
|
||||
return libsql;
|
||||
}),
|
||||
saveExternalPorts: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Save the external ports for a LibSQL database",
|
||||
description: "Updates the external port mappings (HTTP, gRPC, admin) for the LibSQL database and triggers a redeployment. Validates that ports are not already in use.",
|
||||
},
|
||||
})
|
||||
.input(apiSaveExternalPortsLibsql)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.libsqlId, {
|
||||
@@ -230,6 +260,12 @@ export const libsqlRouter = createTRPCRouter({
|
||||
return libsql;
|
||||
}),
|
||||
deploy: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Deploy a LibSQL database",
|
||||
description: "Triggers a deployment for the specified LibSQL database, rebuilding and restarting its Docker container with the current configuration.",
|
||||
},
|
||||
})
|
||||
.input(apiDeployLibsql)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.libsqlId, {
|
||||
@@ -282,6 +318,12 @@ export const libsqlRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
changeStatus: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Change LibSQL database status",
|
||||
description: "Updates the application status of a LibSQL database without starting or stopping the container.",
|
||||
},
|
||||
})
|
||||
.input(apiChangeLibsqlStatus)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.libsqlId, {
|
||||
@@ -300,6 +342,12 @@ export const libsqlRouter = createTRPCRouter({
|
||||
return libsql;
|
||||
}),
|
||||
remove: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Delete a LibSQL database",
|
||||
description: "Removes the LibSQL database service, its Docker container, and deletes the database record.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneLibsql)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServiceAccess(ctx, input.libsqlId, "delete");
|
||||
@@ -335,6 +383,12 @@ export const libsqlRouter = createTRPCRouter({
|
||||
return libsql;
|
||||
}),
|
||||
saveEnvironment: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Save environment variables for a LibSQL database",
|
||||
description: "Updates the environment variables for the specified LibSQL database service.",
|
||||
},
|
||||
})
|
||||
.input(apiSaveEnvironmentVariablesLibsql)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.libsqlId, {
|
||||
@@ -359,6 +413,12 @@ export const libsqlRouter = createTRPCRouter({
|
||||
return true;
|
||||
}),
|
||||
reload: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Reload a LibSQL database",
|
||||
description: "Restarts the LibSQL database by stopping and then starting its Docker container.",
|
||||
},
|
||||
})
|
||||
.input(apiResetLibsql)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.libsqlId, {
|
||||
@@ -391,6 +451,12 @@ export const libsqlRouter = createTRPCRouter({
|
||||
return true;
|
||||
}),
|
||||
update: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Update a LibSQL database",
|
||||
description: "Updates the configuration of an existing LibSQL database service.",
|
||||
},
|
||||
})
|
||||
.input(apiUpdateLibsql)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const { libsqlId, ...rest } = input;
|
||||
@@ -417,6 +483,12 @@ export const libsqlRouter = createTRPCRouter({
|
||||
return true;
|
||||
}),
|
||||
move: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Move a LibSQL database to another environment",
|
||||
description: "Moves the LibSQL database to a different environment within the same project.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
libsqlId: z.string(),
|
||||
@@ -453,6 +525,12 @@ export const libsqlRouter = createTRPCRouter({
|
||||
return updatedLibsql;
|
||||
}),
|
||||
rebuild: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Rebuild a LibSQL database",
|
||||
description: "Rebuilds the LibSQL database Docker container from scratch, pulling the latest image and recreating the service.",
|
||||
},
|
||||
})
|
||||
.input(apiRebuildLibsql)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.libsqlId, {
|
||||
@@ -469,6 +547,12 @@ export const libsqlRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
readLogs: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Read LibSQL container logs",
|
||||
description: "Retrieves the Docker container logs for the specified LibSQL database, with support for tail count, time-based filtering, and text search.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
apiFindOneLibsql.extend({
|
||||
tail: z.number().int().min(1).max(10000).default(100),
|
||||
|
||||
@@ -54,6 +54,12 @@ import {
|
||||
import { cancelJobs } from "@/server/utils/backup";
|
||||
export const mariadbRouter = createTRPCRouter({
|
||||
create: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Create a MariaDB database",
|
||||
description: "Creates a new MariaDB database service with the specified configuration, sets up a persistent data volume, and registers it in the project.",
|
||||
},
|
||||
})
|
||||
.input(apiCreateMariaDB)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -114,6 +120,12 @@ export const mariadbRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
one: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get a MariaDB database by ID",
|
||||
description: "Returns the full details of a MariaDB database service, including its environment and project configuration.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneMariaDB)
|
||||
.query(async ({ input, ctx }) => {
|
||||
await checkServiceAccess(ctx, input.mariadbId, "read");
|
||||
@@ -131,6 +143,12 @@ export const mariadbRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
start: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Start a MariaDB database",
|
||||
description: "Starts the Docker container for the specified MariaDB database and sets its status to done.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneMariaDB)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.mariadbId, {
|
||||
@@ -155,6 +173,12 @@ export const mariadbRouter = createTRPCRouter({
|
||||
return service;
|
||||
}),
|
||||
stop: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Stop a MariaDB database",
|
||||
description: "Stops the Docker container for the specified MariaDB database and sets its status to idle.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneMariaDB)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.mariadbId, {
|
||||
@@ -180,6 +204,12 @@ export const mariadbRouter = createTRPCRouter({
|
||||
return mariadb;
|
||||
}),
|
||||
saveExternalPort: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Save the external port for a MariaDB database",
|
||||
description: "Updates the external port mapping for the MariaDB database and triggers a redeployment. Validates that the port is not already in use.",
|
||||
},
|
||||
})
|
||||
.input(apiSaveExternalPortMariaDB)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.mariadbId, {
|
||||
@@ -213,6 +243,12 @@ export const mariadbRouter = createTRPCRouter({
|
||||
return mariadb;
|
||||
}),
|
||||
deploy: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Deploy a MariaDB database",
|
||||
description: "Triggers a deployment for the specified MariaDB database, rebuilding and restarting its Docker container with the current configuration.",
|
||||
},
|
||||
})
|
||||
.input(apiDeployMariaDB)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.mariadbId, {
|
||||
@@ -250,6 +286,12 @@ export const mariadbRouter = createTRPCRouter({
|
||||
});
|
||||
}),
|
||||
changeStatus: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Change MariaDB database status",
|
||||
description: "Updates the application status of a MariaDB database without starting or stopping the container.",
|
||||
},
|
||||
})
|
||||
.input(apiChangeMariaDBStatus)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.mariadbId, {
|
||||
@@ -268,6 +310,12 @@ export const mariadbRouter = createTRPCRouter({
|
||||
return mongo;
|
||||
}),
|
||||
remove: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Delete a MariaDB database",
|
||||
description: "Removes the MariaDB database service, its Docker container, cancels associated backup jobs, and deletes the database record.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneMariaDB)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServiceAccess(ctx, input.mariadbId, "delete");
|
||||
@@ -305,6 +353,12 @@ export const mariadbRouter = createTRPCRouter({
|
||||
return mongo;
|
||||
}),
|
||||
saveEnvironment: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Save environment variables for a MariaDB database",
|
||||
description: "Updates the environment variables for the specified MariaDB database service.",
|
||||
},
|
||||
})
|
||||
.input(apiSaveEnvironmentVariablesMariaDB)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.mariadbId, {
|
||||
@@ -329,6 +383,12 @@ export const mariadbRouter = createTRPCRouter({
|
||||
return true;
|
||||
}),
|
||||
reload: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Reload a MariaDB database",
|
||||
description: "Restarts the MariaDB database by stopping and then starting its Docker container.",
|
||||
},
|
||||
})
|
||||
.input(apiResetMariadb)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.mariadbId, {
|
||||
@@ -361,6 +421,12 @@ export const mariadbRouter = createTRPCRouter({
|
||||
return true;
|
||||
}),
|
||||
update: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Update a MariaDB database",
|
||||
description: "Updates the configuration of an existing MariaDB database service.",
|
||||
},
|
||||
})
|
||||
.input(apiUpdateMariaDB)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const { mariadbId, ...rest } = input;
|
||||
@@ -387,6 +453,12 @@ export const mariadbRouter = createTRPCRouter({
|
||||
return true;
|
||||
}),
|
||||
changePassword: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Change MariaDB database password",
|
||||
description: "Changes the password for a MariaDB user or root account by executing ALTER USER inside the running container and updating the stored password.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
mariadbId: z.string().min(1),
|
||||
@@ -444,6 +516,12 @@ export const mariadbRouter = createTRPCRouter({
|
||||
return true;
|
||||
}),
|
||||
move: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Move a MariaDB database to another environment",
|
||||
description: "Moves the MariaDB database to a different environment within the same project.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
mariadbId: z.string(),
|
||||
@@ -480,6 +558,12 @@ export const mariadbRouter = createTRPCRouter({
|
||||
return updatedMariadb;
|
||||
}),
|
||||
rebuild: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Rebuild a MariaDB database",
|
||||
description: "Rebuilds the MariaDB database Docker container from scratch, pulling the latest image and recreating the service.",
|
||||
},
|
||||
})
|
||||
.input(apiRebuildMariadb)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.mariadbId, {
|
||||
@@ -495,6 +579,12 @@ export const mariadbRouter = createTRPCRouter({
|
||||
return true;
|
||||
}),
|
||||
search: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Search MariaDB databases",
|
||||
description: "Returns a paginated list of MariaDB databases matching the given filters. Supports searching by name, appName, description, project, and environment.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
q: z.string().optional(),
|
||||
@@ -593,6 +683,12 @@ export const mariadbRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
readLogs: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Read MariaDB container logs",
|
||||
description: "Retrieves the Docker container logs for the specified MariaDB database, with support for tail count, time-based filtering, and text search.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
apiFindOneMariaDB.extend({
|
||||
tail: z.number().int().min(1).max(10000).default(100),
|
||||
|
||||
@@ -53,6 +53,12 @@ import {
|
||||
import { cancelJobs } from "@/server/utils/backup";
|
||||
export const mongoRouter = createTRPCRouter({
|
||||
create: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Create a MongoDB database",
|
||||
description: "Creates a new MongoDB database service with the specified configuration, sets up a persistent data volume, and registers it in the project.",
|
||||
},
|
||||
})
|
||||
.input(apiCreateMongo)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -117,6 +123,12 @@ export const mongoRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
one: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get a MongoDB database by ID",
|
||||
description: "Returns the full details of a MongoDB database service, including its environment and project configuration.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneMongo)
|
||||
.query(async ({ input, ctx }) => {
|
||||
await checkServiceAccess(ctx, input.mongoId, "read");
|
||||
@@ -135,6 +147,12 @@ export const mongoRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
start: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Start a MongoDB database",
|
||||
description: "Starts the Docker container for the specified MongoDB database and sets its status to done.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneMongo)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.mongoId, {
|
||||
@@ -160,6 +178,12 @@ export const mongoRouter = createTRPCRouter({
|
||||
return service;
|
||||
}),
|
||||
stop: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Stop a MongoDB database",
|
||||
description: "Stops the Docker container for the specified MongoDB database and sets its status to idle.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneMongo)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.mongoId, {
|
||||
@@ -185,6 +209,12 @@ export const mongoRouter = createTRPCRouter({
|
||||
return mongo;
|
||||
}),
|
||||
saveExternalPort: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Save the external port for a MongoDB database",
|
||||
description: "Updates the external port mapping for the MongoDB database and triggers a redeployment. Validates that the port is not already in use.",
|
||||
},
|
||||
})
|
||||
.input(apiSaveExternalPortMongo)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.mongoId, {
|
||||
@@ -218,6 +248,12 @@ export const mongoRouter = createTRPCRouter({
|
||||
return mongo;
|
||||
}),
|
||||
deploy: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Deploy a MongoDB database",
|
||||
description: "Triggers a deployment for the specified MongoDB database, rebuilding and restarting its Docker container with the current configuration.",
|
||||
},
|
||||
})
|
||||
.input(apiDeployMongo)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.mongoId, {
|
||||
@@ -271,6 +307,12 @@ export const mongoRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
changeStatus: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Change MongoDB database status",
|
||||
description: "Updates the application status of a MongoDB database without starting or stopping the container.",
|
||||
},
|
||||
})
|
||||
.input(apiChangeMongoStatus)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.mongoId, {
|
||||
@@ -289,6 +331,12 @@ export const mongoRouter = createTRPCRouter({
|
||||
return mongo;
|
||||
}),
|
||||
reload: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Reload a MongoDB database",
|
||||
description: "Restarts the MongoDB database by stopping and then starting its Docker container.",
|
||||
},
|
||||
})
|
||||
.input(apiResetMongo)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.mongoId, {
|
||||
@@ -321,6 +369,12 @@ export const mongoRouter = createTRPCRouter({
|
||||
return true;
|
||||
}),
|
||||
remove: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Delete a MongoDB database",
|
||||
description: "Removes the MongoDB database service, its Docker container, cancels associated backup jobs, and deletes the database record.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneMongo)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServiceAccess(ctx, input.mongoId, "delete");
|
||||
@@ -359,6 +413,12 @@ export const mongoRouter = createTRPCRouter({
|
||||
return mongo;
|
||||
}),
|
||||
saveEnvironment: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Save environment variables for a MongoDB database",
|
||||
description: "Updates the environment variables for the specified MongoDB database service.",
|
||||
},
|
||||
})
|
||||
.input(apiSaveEnvironmentVariablesMongo)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.mongoId, {
|
||||
@@ -383,6 +443,12 @@ export const mongoRouter = createTRPCRouter({
|
||||
return true;
|
||||
}),
|
||||
update: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Update a MongoDB database",
|
||||
description: "Updates the configuration of an existing MongoDB database service.",
|
||||
},
|
||||
})
|
||||
.input(apiUpdateMongo)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const { mongoId, ...rest } = input;
|
||||
@@ -409,6 +475,12 @@ export const mongoRouter = createTRPCRouter({
|
||||
return true;
|
||||
}),
|
||||
changePassword: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Change MongoDB database password",
|
||||
description: "Changes the password for the MongoDB database user by executing changeUserPassword via mongosh inside the running container and updating the stored password.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
mongoId: z.string().min(1),
|
||||
@@ -459,6 +531,12 @@ export const mongoRouter = createTRPCRouter({
|
||||
return true;
|
||||
}),
|
||||
move: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Move a MongoDB database to another environment",
|
||||
description: "Moves the MongoDB database to a different environment within the same project.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
mongoId: z.string(),
|
||||
@@ -495,6 +573,12 @@ export const mongoRouter = createTRPCRouter({
|
||||
return updatedMongo;
|
||||
}),
|
||||
rebuild: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Rebuild a MongoDB database",
|
||||
description: "Rebuilds the MongoDB database Docker container from scratch, pulling the latest image and recreating the service.",
|
||||
},
|
||||
})
|
||||
.input(apiRebuildMongo)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.mongoId, {
|
||||
@@ -511,6 +595,12 @@ export const mongoRouter = createTRPCRouter({
|
||||
return true;
|
||||
}),
|
||||
search: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Search MongoDB databases",
|
||||
description: "Returns a paginated list of MongoDB databases matching the given filters. Supports searching by name, appName, description, project, and environment.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
q: z.string().optional(),
|
||||
@@ -604,6 +694,12 @@ export const mongoRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
readLogs: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Read MongoDB container logs",
|
||||
description: "Retrieves the Docker container logs for the specified MongoDB database, with support for tail count, time-based filtering, and text search.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
apiFindOneMongo.extend({
|
||||
tail: z.number().int().min(1).max(10000).default(100),
|
||||
|
||||
@@ -75,6 +75,12 @@ async function getServiceOrganizationId(
|
||||
|
||||
export const mountRouter = createTRPCRouter({
|
||||
create: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Create mount",
|
||||
description: "Creates a new volume, bind, or file mount for a service. Checks service-level volume permissions and logs an audit event.",
|
||||
},
|
||||
})
|
||||
.input(apiCreateMount)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.serviceId, {
|
||||
@@ -90,6 +96,12 @@ export const mountRouter = createTRPCRouter({
|
||||
return mount;
|
||||
}),
|
||||
remove: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Delete mount",
|
||||
description: "Removes a mount by ID. Resolves the owning service to check volume delete permissions and logs an audit event.",
|
||||
},
|
||||
})
|
||||
.input(apiRemoveMount)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const mount = await findMountById(input.mountId);
|
||||
@@ -116,6 +128,12 @@ export const mountRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
one: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get mount",
|
||||
description: "Returns a single mount by ID. Resolves the owning service to check volume read permissions.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneMount)
|
||||
.query(async ({ input, ctx }) => {
|
||||
const mount = await findMountById(input.mountId);
|
||||
@@ -136,6 +154,12 @@ export const mountRouter = createTRPCRouter({
|
||||
return mount;
|
||||
}),
|
||||
update: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Update mount",
|
||||
description: "Updates an existing mount. Resolves the owning service to check volume create permissions and logs an audit event.",
|
||||
},
|
||||
})
|
||||
.input(apiUpdateMount)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const mount = await findMountById(input.mountId);
|
||||
@@ -162,6 +186,12 @@ export const mountRouter = createTRPCRouter({
|
||||
return await updateMount(input.mountId, input);
|
||||
}),
|
||||
allNamedByApplicationId: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "List named volumes by application",
|
||||
description: "Returns Docker named volumes attached to the running container of a given application. Inspects the live container to retrieve mount information.",
|
||||
},
|
||||
})
|
||||
.input(z.object({ applicationId: z.string().min(1) }))
|
||||
.query(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.applicationId, {
|
||||
@@ -175,6 +205,12 @@ export const mountRouter = createTRPCRouter({
|
||||
return mounts;
|
||||
}),
|
||||
listByServiceId: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "List mounts by service",
|
||||
description: "Returns all configured mounts for a given service (application, compose, or database). Verifies service access and organization ownership.",
|
||||
},
|
||||
})
|
||||
.input(apiFindMountByApplicationId)
|
||||
.query(async ({ input, ctx }) => {
|
||||
await checkServiceAccess(ctx, input.serviceId, "read");
|
||||
|
||||
@@ -54,6 +54,12 @@ import { cancelJobs } from "@/server/utils/backup";
|
||||
|
||||
export const mysqlRouter = createTRPCRouter({
|
||||
create: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Create a MySQL database",
|
||||
description: "Creates a new MySQL database service with the specified configuration, sets up a persistent data volume, and registers it in the project.",
|
||||
},
|
||||
})
|
||||
.input(apiCreateMySql)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -118,6 +124,12 @@ export const mysqlRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
one: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get a MySQL database by ID",
|
||||
description: "Returns the full details of a MySQL database service, including its environment and project configuration.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneMySql)
|
||||
.query(async ({ input, ctx }) => {
|
||||
await checkServiceAccess(ctx, input.mysqlId, "read");
|
||||
@@ -135,6 +147,12 @@ export const mysqlRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
start: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Start a MySQL database",
|
||||
description: "Starts the Docker container for the specified MySQL database and sets its status to done.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneMySql)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.mysqlId, {
|
||||
@@ -160,6 +178,12 @@ export const mysqlRouter = createTRPCRouter({
|
||||
return service;
|
||||
}),
|
||||
stop: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Stop a MySQL database",
|
||||
description: "Stops the Docker container for the specified MySQL database and sets its status to idle.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneMySql)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.mysqlId, {
|
||||
@@ -184,6 +208,12 @@ export const mysqlRouter = createTRPCRouter({
|
||||
return mongo;
|
||||
}),
|
||||
saveExternalPort: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Save the external port for a MySQL database",
|
||||
description: "Updates the external port mapping for the MySQL database and triggers a redeployment. Validates that the port is not already in use.",
|
||||
},
|
||||
})
|
||||
.input(apiSaveExternalPortMySql)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.mysqlId, {
|
||||
@@ -217,6 +247,12 @@ export const mysqlRouter = createTRPCRouter({
|
||||
return mysql;
|
||||
}),
|
||||
deploy: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Deploy a MySQL database",
|
||||
description: "Triggers a deployment for the specified MySQL database, rebuilding and restarting its Docker container with the current configuration.",
|
||||
},
|
||||
})
|
||||
.input(apiDeployMySql)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.mysqlId, {
|
||||
@@ -270,6 +306,12 @@ export const mysqlRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
changeStatus: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Change MySQL database status",
|
||||
description: "Updates the application status of a MySQL database without starting or stopping the container.",
|
||||
},
|
||||
})
|
||||
.input(apiChangeMySqlStatus)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.mysqlId, {
|
||||
@@ -288,6 +330,12 @@ export const mysqlRouter = createTRPCRouter({
|
||||
return mongo;
|
||||
}),
|
||||
reload: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Reload a MySQL database",
|
||||
description: "Restarts the MySQL database by stopping and then starting its Docker container.",
|
||||
},
|
||||
})
|
||||
.input(apiResetMysql)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.mysqlId, {
|
||||
@@ -319,6 +367,12 @@ export const mysqlRouter = createTRPCRouter({
|
||||
return true;
|
||||
}),
|
||||
remove: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Delete a MySQL database",
|
||||
description: "Removes the MySQL database service, its Docker container, cancels associated backup jobs, and deletes the database record.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneMySql)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServiceAccess(ctx, input.mysqlId, "delete");
|
||||
@@ -355,6 +409,12 @@ export const mysqlRouter = createTRPCRouter({
|
||||
return mongo;
|
||||
}),
|
||||
saveEnvironment: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Save environment variables for a MySQL database",
|
||||
description: "Updates the environment variables for the specified MySQL database service.",
|
||||
},
|
||||
})
|
||||
.input(apiSaveEnvironmentVariablesMySql)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.mysqlId, {
|
||||
@@ -379,6 +439,12 @@ export const mysqlRouter = createTRPCRouter({
|
||||
return true;
|
||||
}),
|
||||
update: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Update a MySQL database",
|
||||
description: "Updates the configuration of an existing MySQL database service.",
|
||||
},
|
||||
})
|
||||
.input(apiUpdateMySql)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const { mysqlId, ...rest } = input;
|
||||
@@ -405,6 +471,12 @@ export const mysqlRouter = createTRPCRouter({
|
||||
return true;
|
||||
}),
|
||||
changePassword: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Change MySQL database password",
|
||||
description: "Changes the password for a MySQL user or root account by executing ALTER USER inside the running container and updating the stored password.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
mysqlId: z.string().min(1),
|
||||
@@ -462,6 +534,12 @@ export const mysqlRouter = createTRPCRouter({
|
||||
return true;
|
||||
}),
|
||||
move: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Move a MySQL database to another environment",
|
||||
description: "Moves the MySQL database to a different environment within the same project.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
mysqlId: z.string(),
|
||||
@@ -498,6 +576,12 @@ export const mysqlRouter = createTRPCRouter({
|
||||
return updatedMysql;
|
||||
}),
|
||||
rebuild: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Rebuild a MySQL database",
|
||||
description: "Rebuilds the MySQL database Docker container from scratch, pulling the latest image and recreating the service.",
|
||||
},
|
||||
})
|
||||
.input(apiRebuildMysql)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.mysqlId, {
|
||||
@@ -514,6 +598,12 @@ export const mysqlRouter = createTRPCRouter({
|
||||
return true;
|
||||
}),
|
||||
search: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Search MySQL databases",
|
||||
description: "Returns a paginated list of MySQL databases matching the given filters. Supports searching by name, appName, description, project, and environment.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
q: z.string().optional(),
|
||||
@@ -607,6 +697,12 @@ export const mysqlRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
readLogs: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Read MySQL container logs",
|
||||
description: "Retrieves the Docker container logs for the specified MySQL database, with support for tail count, time-based filtering, and text search.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
apiFindOneMySql.extend({
|
||||
tail: z.number().int().min(1).max(10000).default(100),
|
||||
|
||||
@@ -95,6 +95,12 @@ import {
|
||||
|
||||
export const notificationRouter = createTRPCRouter({
|
||||
createSlack: withPermission("notification", "create")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Create Slack notification",
|
||||
description: "Creates a new Slack notification provider for the current organization and logs an audit event.",
|
||||
},
|
||||
})
|
||||
.input(apiCreateSlack)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -114,6 +120,12 @@ export const notificationRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
updateSlack: withPermission("notification", "update")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Update Slack notification",
|
||||
description: "Updates an existing Slack notification provider. Verifies organization ownership before applying changes.",
|
||||
},
|
||||
})
|
||||
.input(apiUpdateSlack)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -140,6 +152,12 @@ export const notificationRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
testSlackConnection: withPermission("notification", "create")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Test Slack connection",
|
||||
description: "Sends a test message to the configured Slack channel to verify the webhook connection works.",
|
||||
},
|
||||
})
|
||||
.input(apiTestSlackConnection)
|
||||
.mutation(async ({ input }) => {
|
||||
try {
|
||||
@@ -157,6 +175,12 @@ export const notificationRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
createTelegram: withPermission("notification", "create")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Create Telegram notification",
|
||||
description: "Creates a new Telegram notification provider for the current organization and logs an audit event.",
|
||||
},
|
||||
})
|
||||
.input(apiCreateTelegram)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -179,6 +203,12 @@ export const notificationRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
updateTelegram: withPermission("notification", "update")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Update Telegram notification",
|
||||
description: "Updates an existing Telegram notification provider. Verifies organization ownership before applying changes.",
|
||||
},
|
||||
})
|
||||
.input(apiUpdateTelegram)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -209,6 +239,12 @@ export const notificationRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
testTelegramConnection: withPermission("notification", "create")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Test Telegram connection",
|
||||
description: "Sends a test message to the configured Telegram chat to verify the bot token and chat ID work.",
|
||||
},
|
||||
})
|
||||
.input(apiTestTelegramConnection)
|
||||
.mutation(async ({ input }) => {
|
||||
try {
|
||||
@@ -223,6 +259,12 @@ export const notificationRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
createDiscord: withPermission("notification", "create")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Create Discord notification",
|
||||
description: "Creates a new Discord notification provider for the current organization and logs an audit event.",
|
||||
},
|
||||
})
|
||||
.input(apiCreateDiscord)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -245,6 +287,12 @@ export const notificationRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
updateDiscord: withPermission("notification", "update")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Update Discord notification",
|
||||
description: "Updates an existing Discord notification provider. Verifies organization ownership before applying changes.",
|
||||
},
|
||||
})
|
||||
.input(apiUpdateDiscord)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -276,6 +324,12 @@ export const notificationRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
testDiscordConnection: withPermission("notification", "create")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Test Discord connection",
|
||||
description: "Sends a test embed message to the configured Discord webhook to verify the connection works.",
|
||||
},
|
||||
})
|
||||
.input(apiTestDiscordConnection)
|
||||
.mutation(async ({ input }) => {
|
||||
try {
|
||||
@@ -298,6 +352,12 @@ export const notificationRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
createEmail: withPermission("notification", "create")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Create Email notification",
|
||||
description: "Creates a new SMTP email notification provider for the current organization and logs an audit event.",
|
||||
},
|
||||
})
|
||||
.input(apiCreateEmail)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -316,6 +376,12 @@ export const notificationRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
updateEmail: withPermission("notification", "update")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Update Email notification",
|
||||
description: "Updates an existing SMTP email notification provider. Verifies organization ownership before applying changes.",
|
||||
},
|
||||
})
|
||||
.input(apiUpdateEmail)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -346,6 +412,12 @@ export const notificationRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
testEmailConnection: withPermission("notification", "create")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Test Email connection",
|
||||
description: "Sends a test email via the configured SMTP settings to verify the connection works.",
|
||||
},
|
||||
})
|
||||
.input(apiTestEmailConnection)
|
||||
.mutation(async ({ input }) => {
|
||||
try {
|
||||
@@ -364,6 +436,12 @@ export const notificationRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
createResend: withPermission("notification", "create")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Create Resend notification",
|
||||
description: "Creates a new Resend email notification provider for the current organization and logs an audit event.",
|
||||
},
|
||||
})
|
||||
.input(apiCreateResend)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -382,6 +460,12 @@ export const notificationRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
updateResend: withPermission("notification", "update")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Update Resend notification",
|
||||
description: "Updates an existing Resend email notification provider. Verifies organization ownership before applying changes.",
|
||||
},
|
||||
})
|
||||
.input(apiUpdateResend)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -412,6 +496,12 @@ export const notificationRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
testResendConnection: withPermission("notification", "create")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Test Resend connection",
|
||||
description: "Sends a test email via Resend to verify the API key and configuration work.",
|
||||
},
|
||||
})
|
||||
.input(apiTestResendConnection)
|
||||
.mutation(async ({ input }) => {
|
||||
try {
|
||||
@@ -430,6 +520,12 @@ export const notificationRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
remove: withPermission("notification", "delete")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Delete notification",
|
||||
description: "Removes a notification provider by ID. Verifies organization ownership and logs an audit event before deletion.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneNotification)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -458,6 +554,12 @@ export const notificationRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
one: withPermission("notification", "read")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get notification",
|
||||
description: "Returns a single notification provider by ID. Verifies the caller belongs to the same organization.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneNotification)
|
||||
.query(async ({ input, ctx }) => {
|
||||
const notification = await findNotificationById(input.notificationId);
|
||||
@@ -469,7 +571,14 @@ export const notificationRouter = createTRPCRouter({
|
||||
}
|
||||
return notification;
|
||||
}),
|
||||
all: withPermission("notification", "read").query(async ({ ctx }) => {
|
||||
all: withPermission("notification", "read")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "List all notifications",
|
||||
description: "Returns all notification providers for the current organization, including all provider-specific details (Slack, Telegram, Discord, etc.).",
|
||||
},
|
||||
})
|
||||
.query(async ({ ctx }) => {
|
||||
return await db.query.notifications.findMany({
|
||||
with: {
|
||||
slack: true,
|
||||
@@ -490,6 +599,12 @@ export const notificationRouter = createTRPCRouter({
|
||||
});
|
||||
}),
|
||||
receiveNotification: publicProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Receive server threshold notification",
|
||||
description: "Public endpoint that receives CPU/memory threshold alerts from Dokploy or remote servers. Validates the token and dispatches notifications to all configured providers.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
ServerType: z.enum(["Dokploy", "Remote"]).default("Dokploy"),
|
||||
@@ -551,6 +666,12 @@ export const notificationRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
createGotify: withPermission("notification", "create")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Create Gotify notification",
|
||||
description: "Creates a new Gotify notification provider for the current organization and logs an audit event.",
|
||||
},
|
||||
})
|
||||
.input(apiCreateGotify)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -569,6 +690,12 @@ export const notificationRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
updateGotify: withPermission("notification", "update")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Update Gotify notification",
|
||||
description: "Updates an existing Gotify notification provider. Verifies organization ownership before applying changes.",
|
||||
},
|
||||
})
|
||||
.input(apiUpdateGotify)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -598,6 +725,12 @@ export const notificationRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
testGotifyConnection: withPermission("notification", "create")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Test Gotify connection",
|
||||
description: "Sends a test notification to the configured Gotify server to verify the connection works.",
|
||||
},
|
||||
})
|
||||
.input(apiTestGotifyConnection)
|
||||
.mutation(async ({ input }) => {
|
||||
try {
|
||||
@@ -616,6 +749,12 @@ export const notificationRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
createNtfy: withPermission("notification", "create")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Create ntfy notification",
|
||||
description: "Creates a new ntfy notification provider for the current organization and logs an audit event.",
|
||||
},
|
||||
})
|
||||
.input(apiCreateNtfy)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -634,6 +773,12 @@ export const notificationRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
updateNtfy: withPermission("notification", "update")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Update ntfy notification",
|
||||
description: "Updates an existing ntfy notification provider. Verifies organization ownership before applying changes.",
|
||||
},
|
||||
})
|
||||
.input(apiUpdateNtfy)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -663,6 +808,12 @@ export const notificationRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
testNtfyConnection: withPermission("notification", "create")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Test ntfy connection",
|
||||
description: "Sends a test notification to the configured ntfy topic to verify the connection works.",
|
||||
},
|
||||
})
|
||||
.input(apiTestNtfyConnection)
|
||||
.mutation(async ({ input }) => {
|
||||
try {
|
||||
@@ -686,6 +837,12 @@ export const notificationRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
createMattermost: withPermission("notification", "create")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Create Mattermost notification",
|
||||
description: "Creates a new Mattermost notification provider for the current organization and logs an audit event.",
|
||||
},
|
||||
})
|
||||
.input(apiCreateMattermost)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -707,6 +864,12 @@ export const notificationRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
updateMattermost: withPermission("notification", "update")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Update Mattermost notification",
|
||||
description: "Updates an existing Mattermost notification provider. Verifies organization ownership before applying changes.",
|
||||
},
|
||||
})
|
||||
.input(apiUpdateMattermost)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -736,6 +899,12 @@ export const notificationRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
testMattermostConnection: withPermission("notification", "create")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Test Mattermost connection",
|
||||
description: "Sends a test message to the configured Mattermost webhook to verify the connection works.",
|
||||
},
|
||||
})
|
||||
.input(apiTestMattermostConnection)
|
||||
.mutation(async ({ input }) => {
|
||||
try {
|
||||
@@ -754,6 +923,12 @@ export const notificationRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
createCustom: withPermission("notification", "create")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Create custom webhook notification",
|
||||
description: "Creates a new custom webhook notification provider for the current organization and logs an audit event.",
|
||||
},
|
||||
})
|
||||
.input(apiCreateCustom)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -772,6 +947,12 @@ export const notificationRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
updateCustom: withPermission("notification", "update")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Update custom webhook notification",
|
||||
description: "Updates an existing custom webhook notification provider. Verifies organization ownership before applying changes.",
|
||||
},
|
||||
})
|
||||
.input(apiUpdateCustom)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -798,6 +979,12 @@ export const notificationRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
testCustomConnection: withPermission("notification", "create")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Test custom webhook connection",
|
||||
description: "Sends a test payload to the configured custom webhook URL to verify the connection works.",
|
||||
},
|
||||
})
|
||||
.input(apiTestCustomConnection)
|
||||
.mutation(async ({ input }) => {
|
||||
try {
|
||||
@@ -816,6 +1003,12 @@ export const notificationRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
createLark: withPermission("notification", "create")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Create Lark notification",
|
||||
description: "Creates a new Lark notification provider for the current organization and logs an audit event.",
|
||||
},
|
||||
})
|
||||
.input(apiCreateLark)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -834,6 +1027,12 @@ export const notificationRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
updateLark: withPermission("notification", "update")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Update Lark notification",
|
||||
description: "Updates an existing Lark notification provider. Verifies organization ownership before applying changes.",
|
||||
},
|
||||
})
|
||||
.input(apiUpdateLark)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -863,6 +1062,12 @@ export const notificationRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
testLarkConnection: withPermission("notification", "create")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Test Lark connection",
|
||||
description: "Sends a test message to the configured Lark webhook to verify the connection works.",
|
||||
},
|
||||
})
|
||||
.input(apiTestLarkConnection)
|
||||
.mutation(async ({ input }) => {
|
||||
try {
|
||||
@@ -882,6 +1087,12 @@ export const notificationRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
createTeams: withPermission("notification", "create")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Create Teams notification",
|
||||
description: "Creates a new Microsoft Teams notification provider for the current organization and logs an audit event.",
|
||||
},
|
||||
})
|
||||
.input(apiCreateTeams)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -900,6 +1111,12 @@ export const notificationRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
updateTeams: withPermission("notification", "update")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Update Teams notification",
|
||||
description: "Updates an existing Microsoft Teams notification provider. Verifies organization ownership before applying changes.",
|
||||
},
|
||||
})
|
||||
.input(apiUpdateTeams)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -929,6 +1146,12 @@ export const notificationRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
testTeamsConnection: withPermission("notification", "create")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Test Teams connection",
|
||||
description: "Sends a test message to the configured Microsoft Teams webhook to verify the connection works.",
|
||||
},
|
||||
})
|
||||
.input(apiTestTeamsConnection)
|
||||
.mutation(async ({ input }) => {
|
||||
try {
|
||||
@@ -946,6 +1169,12 @@ export const notificationRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
createPushover: withPermission("notification", "create")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Create Pushover notification",
|
||||
description: "Creates a new Pushover notification provider for the current organization and logs an audit event.",
|
||||
},
|
||||
})
|
||||
.input(apiCreatePushover)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -967,6 +1196,12 @@ export const notificationRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
updatePushover: withPermission("notification", "update")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Update Pushover notification",
|
||||
description: "Updates an existing Pushover notification provider. Verifies organization ownership before applying changes.",
|
||||
},
|
||||
})
|
||||
.input(apiUpdatePushover)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -996,6 +1231,12 @@ export const notificationRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
testPushoverConnection: withPermission("notification", "create")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Test Pushover connection",
|
||||
description: "Sends a test notification to the configured Pushover account to verify the connection works.",
|
||||
},
|
||||
})
|
||||
.input(apiTestPushoverConnection)
|
||||
.mutation(async ({ input }) => {
|
||||
try {
|
||||
@@ -1013,7 +1254,14 @@ export const notificationRouter = createTRPCRouter({
|
||||
});
|
||||
}
|
||||
}),
|
||||
getEmailProviders: withPermission("notification", "read").query(
|
||||
getEmailProviders: withPermission("notification", "read")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "List email notification providers",
|
||||
description: "Returns all notification providers that support email (SMTP and Resend) for the current organization.",
|
||||
},
|
||||
})
|
||||
.query(
|
||||
async ({ ctx }) => {
|
||||
return await db.query.notifications.findMany({
|
||||
where: eq(
|
||||
|
||||
@@ -15,6 +15,12 @@ import {
|
||||
import { createTRPCRouter, protectedProcedure, withPermission } from "../trpc";
|
||||
export const organizationRouter = createTRPCRouter({
|
||||
create: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Create an organization",
|
||||
description: "Create a new organization and add the current user as the owner. Only owners and admins can create organizations in self-hosted mode.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
name: z.string(),
|
||||
@@ -65,7 +71,14 @@ export const organizationRouter = createTRPCRouter({
|
||||
});
|
||||
return result;
|
||||
}),
|
||||
all: protectedProcedure.query(async ({ ctx }) => {
|
||||
all: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "List all organizations",
|
||||
description: "Retrieve all organizations the current user is a member of, including their membership details.",
|
||||
},
|
||||
})
|
||||
.query(async ({ ctx }) => {
|
||||
const memberResult = await db.query.organization.findMany({
|
||||
where: (organization) =>
|
||||
exists(
|
||||
@@ -88,6 +101,12 @@ export const organizationRouter = createTRPCRouter({
|
||||
return memberResult;
|
||||
}),
|
||||
one: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get an organization by ID",
|
||||
description: "Retrieve a single organization by its ID. The current user must be a member of the organization.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
organizationId: z.string(),
|
||||
@@ -114,6 +133,12 @@ export const organizationRouter = createTRPCRouter({
|
||||
});
|
||||
}),
|
||||
update: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Update an organization",
|
||||
description: "Update the name and logo of an organization. Only the organization owner can perform this action.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
organizationId: z.string(),
|
||||
@@ -178,6 +203,12 @@ export const organizationRouter = createTRPCRouter({
|
||||
return result[0];
|
||||
}),
|
||||
delete: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Delete an organization",
|
||||
description: "Delete an organization by ID. Only the owner can delete it, and they must retain at least one organization.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
organizationId: z.string(),
|
||||
@@ -248,6 +279,12 @@ export const organizationRouter = createTRPCRouter({
|
||||
return result;
|
||||
}),
|
||||
inviteMember: withPermission("member", "create")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Invite a member to organization",
|
||||
description: "Create a pending invitation for a user by email to join the active organization with the specified role. Checks for existing membership and pending invitations. Supports custom roles.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
email: z.string().email(),
|
||||
@@ -335,13 +372,26 @@ export const organizationRouter = createTRPCRouter({
|
||||
return created;
|
||||
}),
|
||||
|
||||
allInvitations: withPermission("member", "create").query(async ({ ctx }) => {
|
||||
allInvitations: withPermission("member", "create")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "List all organization invitations",
|
||||
description: "Retrieve all invitations for the active organization, ordered by status and expiration date.",
|
||||
},
|
||||
})
|
||||
.query(async ({ ctx }) => {
|
||||
return await db.query.invitation.findMany({
|
||||
where: eq(invitation.organizationId, ctx.session.activeOrganizationId),
|
||||
orderBy: [desc(invitation.status), desc(invitation.expiresAt)],
|
||||
});
|
||||
}),
|
||||
removeInvitation: withPermission("member", "create")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Remove an invitation",
|
||||
description: "Delete a pending invitation by ID. Only invitations belonging to the active organization can be removed.",
|
||||
},
|
||||
})
|
||||
.input(z.object({ invitationId: z.string() }))
|
||||
.mutation(async ({ ctx, input }) => {
|
||||
const invitationResult = await db.query.invitation.findFirst({
|
||||
@@ -377,6 +427,12 @@ export const organizationRouter = createTRPCRouter({
|
||||
return result;
|
||||
}),
|
||||
updateMemberRole: withPermission("member", "update")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Update member role",
|
||||
description: "Change the role of a member in the active organization. Users cannot change their own role, and the owner role is nontransferable. Only owners can change admin roles. Supports custom roles.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
memberId: z.string(),
|
||||
@@ -463,6 +519,12 @@ export const organizationRouter = createTRPCRouter({
|
||||
return true;
|
||||
}),
|
||||
setDefault: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Set default organization",
|
||||
description: "Set an organization as the default for the current user. Unsets any previous default and marks the specified organization as the new default.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
organizationId: z.string().min(1),
|
||||
@@ -509,7 +571,14 @@ export const organizationRouter = createTRPCRouter({
|
||||
});
|
||||
return { success: true };
|
||||
}),
|
||||
active: protectedProcedure.query(async ({ ctx }) => {
|
||||
active: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get active organization",
|
||||
description: "Retrieve the organization that is currently active in the user's session. Returns null if no organization is active.",
|
||||
},
|
||||
})
|
||||
.query(async ({ ctx }) => {
|
||||
if (!ctx.session.activeOrganizationId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -50,6 +50,12 @@ const resolvePatchServiceId = (patch: {
|
||||
|
||||
export const patchRouter = createTRPCRouter({
|
||||
create: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Create patch",
|
||||
description: "Creates a new file patch for an application or compose service. Checks service-level permissions and logs an audit event.",
|
||||
},
|
||||
})
|
||||
.input(apiCreatePatch)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const serviceId = input.applicationId ?? input.composeId;
|
||||
@@ -73,7 +79,15 @@ export const patchRouter = createTRPCRouter({
|
||||
return result;
|
||||
}),
|
||||
|
||||
one: protectedProcedure.input(apiFindPatch).query(async ({ input, ctx }) => {
|
||||
one: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get patch",
|
||||
description: "Returns a single patch by ID. Resolves the associated service to verify read permissions.",
|
||||
},
|
||||
})
|
||||
.input(apiFindPatch)
|
||||
.query(async ({ input, ctx }) => {
|
||||
const patch = await findPatchById(input.patchId);
|
||||
const serviceId = resolvePatchServiceId(patch);
|
||||
await checkServicePermissionAndAccess(ctx, serviceId, {
|
||||
@@ -83,6 +97,12 @@ export const patchRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
byEntityId: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "List patches by entity",
|
||||
description: "Returns all patches associated with a given application or compose service.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({ id: z.string(), type: z.enum(["application", "compose"]) }),
|
||||
)
|
||||
@@ -94,6 +114,12 @@ export const patchRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
update: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Update patch",
|
||||
description: "Updates the content or configuration of an existing patch. Resolves the associated service to verify permissions and logs an audit event.",
|
||||
},
|
||||
})
|
||||
.input(apiUpdatePatch)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const patch = await findPatchById(input.patchId);
|
||||
@@ -114,6 +140,12 @@ export const patchRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
delete: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Delete patch",
|
||||
description: "Deletes a patch by ID. Resolves the associated service to verify delete permissions and logs an audit event.",
|
||||
},
|
||||
})
|
||||
.input(apiDeletePatch)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const patch = await findPatchById(input.patchId);
|
||||
@@ -133,6 +165,12 @@ export const patchRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
toggleEnabled: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Toggle patch enabled state",
|
||||
description: "Enables or disables a patch without deleting it. Resolves the associated service to verify permissions and logs an audit event.",
|
||||
},
|
||||
})
|
||||
.input(apiTogglePatchEnabled)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const patch = await findPatchById(input.patchId);
|
||||
@@ -155,6 +193,12 @@ export const patchRouter = createTRPCRouter({
|
||||
|
||||
// Repository Operations
|
||||
ensureRepo: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Ensure patch repository exists",
|
||||
description: "Ensures a patch repository is initialized for the given application or compose service. Creates the repo if it does not exist and logs an audit event.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
id: z.string(),
|
||||
@@ -179,6 +223,12 @@ export const patchRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
readRepoDirectories: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "List patch repository directories",
|
||||
description: "Reads the directory listing at a given path inside the patch repository for an application or compose service.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
id: z.string().min(1),
|
||||
@@ -202,6 +252,12 @@ export const patchRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
readRepoFile: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Read patch repository file",
|
||||
description: "Reads a file from the patch repository. For delete-type patches it returns the current repo content; otherwise returns the patch content if available, falling back to the repo file.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
id: z.string().min(1),
|
||||
@@ -241,6 +297,12 @@ export const patchRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
saveFileAsPatch: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Save file as patch",
|
||||
description: "Creates or updates a patch record from file content. If a patch already exists for the file path, it updates the existing patch; otherwise creates a new one.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
id: z.string().min(1),
|
||||
@@ -291,6 +353,12 @@ export const patchRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
markFileForDeletion: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Mark file for deletion",
|
||||
description: "Creates a delete-type patch that will remove the specified file from the service on next deployment. Logs an audit event.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
id: z.string().min(1),
|
||||
@@ -318,6 +386,12 @@ export const patchRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
cleanPatchRepos: adminProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Clean patch repositories",
|
||||
description: "Removes all patch repository working directories on the local or a specified remote server. Admin-only operation that logs an audit event.",
|
||||
},
|
||||
})
|
||||
.input(z.object({ serverId: z.string().optional() }))
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await cleanPatchRepos(input.serverId);
|
||||
|
||||
@@ -16,6 +16,12 @@ import {
|
||||
|
||||
export const portRouter = createTRPCRouter({
|
||||
create: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Create a port",
|
||||
description: "Creates a new port mapping for an application, binding a published port to a target port. Logs an audit entry.",
|
||||
},
|
||||
})
|
||||
.input(apiCreatePort)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -39,6 +45,12 @@ export const portRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
one: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get a port",
|
||||
description: "Returns a single port mapping by its ID, including the associated application details.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOnePort)
|
||||
.query(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -58,6 +70,12 @@ export const portRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
delete: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Delete a port",
|
||||
description: "Deletes a port mapping by its ID and logs an audit entry with the published and target port details.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOnePort)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const port = await finPortById(input.portId);
|
||||
@@ -85,6 +103,12 @@ export const portRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
update: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Update a port",
|
||||
description: "Updates an existing port mapping's configuration and logs an audit entry.",
|
||||
},
|
||||
})
|
||||
.input(apiUpdatePort)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const port = await finPortById(input.portId);
|
||||
|
||||
@@ -55,6 +55,12 @@ import { cancelJobs } from "@/server/utils/backup";
|
||||
|
||||
export const postgresRouter = createTRPCRouter({
|
||||
create: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Create a PostgreSQL database",
|
||||
description: "Creates a new PostgreSQL database service with the specified configuration, sets up a persistent data volume, and registers it in the project.",
|
||||
},
|
||||
})
|
||||
.input(apiCreatePostgres)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -121,6 +127,12 @@ export const postgresRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
one: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get a PostgreSQL database by ID",
|
||||
description: "Returns the full details of a PostgreSQL database service, including its environment and project configuration.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOnePostgres)
|
||||
.query(async ({ input, ctx }) => {
|
||||
await checkServiceAccess(ctx, input.postgresId, "read");
|
||||
@@ -139,6 +151,12 @@ export const postgresRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
start: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Start a PostgreSQL database",
|
||||
description: "Starts the Docker container for the specified PostgreSQL database and sets its status to done.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOnePostgres)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.postgresId, {
|
||||
@@ -164,6 +182,12 @@ export const postgresRouter = createTRPCRouter({
|
||||
return service;
|
||||
}),
|
||||
stop: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Stop a PostgreSQL database",
|
||||
description: "Stops the Docker container for the specified PostgreSQL database and sets its status to idle.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOnePostgres)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.postgresId, {
|
||||
@@ -188,6 +212,12 @@ export const postgresRouter = createTRPCRouter({
|
||||
return postgres;
|
||||
}),
|
||||
saveExternalPort: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Save the external port for a PostgreSQL database",
|
||||
description: "Updates the external port mapping for the PostgreSQL database and triggers a redeployment. Validates that the port is not already in use.",
|
||||
},
|
||||
})
|
||||
.input(apiSaveExternalPortPostgres)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.postgresId, {
|
||||
@@ -221,6 +251,12 @@ export const postgresRouter = createTRPCRouter({
|
||||
return postgres;
|
||||
}),
|
||||
deploy: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Deploy a PostgreSQL database",
|
||||
description: "Triggers a deployment for the specified PostgreSQL database, rebuilding and restarting its Docker container with the current configuration.",
|
||||
},
|
||||
})
|
||||
.input(apiDeployPostgres)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.postgresId, {
|
||||
@@ -276,6 +312,12 @@ export const postgresRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
changeStatus: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Change PostgreSQL database status",
|
||||
description: "Updates the application status of a PostgreSQL database without starting or stopping the container.",
|
||||
},
|
||||
})
|
||||
.input(apiChangePostgresStatus)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.postgresId, {
|
||||
@@ -294,6 +336,12 @@ export const postgresRouter = createTRPCRouter({
|
||||
return postgres;
|
||||
}),
|
||||
remove: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Delete a PostgreSQL database",
|
||||
description: "Removes the PostgreSQL database service, its Docker container, cancels associated backup jobs, and deletes the database record.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOnePostgres)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServiceAccess(ctx, input.postgresId, "delete");
|
||||
@@ -332,6 +380,12 @@ export const postgresRouter = createTRPCRouter({
|
||||
return postgres;
|
||||
}),
|
||||
saveEnvironment: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Save environment variables for a PostgreSQL database",
|
||||
description: "Updates the environment variables for the specified PostgreSQL database service.",
|
||||
},
|
||||
})
|
||||
.input(apiSaveEnvironmentVariablesPostgres)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.postgresId, {
|
||||
@@ -356,6 +410,12 @@ export const postgresRouter = createTRPCRouter({
|
||||
return true;
|
||||
}),
|
||||
reload: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Reload a PostgreSQL database",
|
||||
description: "Restarts the PostgreSQL database by stopping and then starting its Docker container.",
|
||||
},
|
||||
})
|
||||
.input(apiResetPostgres)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.postgresId, {
|
||||
@@ -388,6 +448,12 @@ export const postgresRouter = createTRPCRouter({
|
||||
return true;
|
||||
}),
|
||||
update: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Update a PostgreSQL database",
|
||||
description: "Updates the configuration of an existing PostgreSQL database service.",
|
||||
},
|
||||
})
|
||||
.input(apiUpdatePostgres)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const { postgresId, ...rest } = input;
|
||||
@@ -415,6 +481,12 @@ export const postgresRouter = createTRPCRouter({
|
||||
return true;
|
||||
}),
|
||||
changePassword: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Change PostgreSQL database password",
|
||||
description: "Changes the password for the PostgreSQL database user by executing ALTER USER inside the running container and updating the stored password.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
postgresId: z.string().min(1),
|
||||
@@ -465,6 +537,12 @@ export const postgresRouter = createTRPCRouter({
|
||||
return true;
|
||||
}),
|
||||
move: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Move a PostgreSQL database to another environment",
|
||||
description: "Moves the PostgreSQL database to a different environment within the same project.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
postgresId: z.string(),
|
||||
@@ -501,6 +579,12 @@ export const postgresRouter = createTRPCRouter({
|
||||
return updatedPostgres;
|
||||
}),
|
||||
rebuild: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Rebuild a PostgreSQL database",
|
||||
description: "Rebuilds the PostgreSQL database Docker container from scratch, pulling the latest image and recreating the service.",
|
||||
},
|
||||
})
|
||||
.input(apiRebuildPostgres)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.postgresId, {
|
||||
@@ -517,6 +601,12 @@ export const postgresRouter = createTRPCRouter({
|
||||
return true;
|
||||
}),
|
||||
search: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Search PostgreSQL databases",
|
||||
description: "Returns a paginated list of PostgreSQL databases matching the given filters. Supports searching by name, appName, description, project, and environment.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
q: z.string().optional(),
|
||||
@@ -617,6 +707,12 @@ export const postgresRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
readLogs: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Read PostgreSQL container logs",
|
||||
description: "Retrieves the Docker container logs for the specified PostgreSQL database, with support for tail count, time-based filtering, and text search.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
apiFindOnePostgres.extend({
|
||||
tail: z.number().int().min(1).max(10000).default(100),
|
||||
|
||||
@@ -16,6 +16,12 @@ import { createTRPCRouter, protectedProcedure } from "../trpc";
|
||||
|
||||
export const previewDeploymentRouter = createTRPCRouter({
|
||||
all: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "List preview deployments",
|
||||
description: "Returns all preview deployments associated with the given application.",
|
||||
},
|
||||
})
|
||||
.input(apiFindAllByApplication)
|
||||
.query(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.applicationId, {
|
||||
@@ -25,6 +31,12 @@ export const previewDeploymentRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
one: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get a preview deployment",
|
||||
description: "Returns the details of a specific preview deployment by its ID.",
|
||||
},
|
||||
})
|
||||
.input(z.object({ previewDeploymentId: z.string() }))
|
||||
.query(async ({ input, ctx }) => {
|
||||
const previewDeployment = await findPreviewDeploymentById(
|
||||
@@ -39,6 +51,12 @@ export const previewDeploymentRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
delete: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Delete a preview deployment",
|
||||
description: "Permanently removes a preview deployment and its associated resources.",
|
||||
},
|
||||
})
|
||||
.input(z.object({ previewDeploymentId: z.string() }))
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const previewDeployment = await findPreviewDeploymentById(
|
||||
@@ -59,6 +77,12 @@ export const previewDeploymentRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
redeploy: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Redeploy a preview deployment",
|
||||
description: "Triggers a rebuild of an existing preview deployment by adding a new job to the deployment queue.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
previewDeploymentId: z.string(),
|
||||
|
||||
@@ -67,6 +67,12 @@ import {
|
||||
|
||||
export const projectRouter = createTRPCRouter({
|
||||
create: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Create a project",
|
||||
description: "Creates a new project in the current organization with a default environment. Validates server availability for cloud deployments.",
|
||||
},
|
||||
})
|
||||
.input(apiCreateProject)
|
||||
.mutation(async ({ ctx, input }) => {
|
||||
try {
|
||||
@@ -106,6 +112,12 @@ export const projectRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
one: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get a project",
|
||||
description: "Retrieves a project by its ID with all environments and services. Filters services based on the user's access permissions.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneProject)
|
||||
.query(async ({ input, ctx }) => {
|
||||
if (ctx.user.role !== "owner" && ctx.user.role !== "admin") {
|
||||
@@ -193,7 +205,14 @@ export const projectRouter = createTRPCRouter({
|
||||
}
|
||||
return project;
|
||||
}),
|
||||
all: protectedProcedure.query(async ({ ctx }) => {
|
||||
all: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "List all projects",
|
||||
description: "Returns all projects in the current organization with their environments and services. Filters results based on the user's access permissions.",
|
||||
},
|
||||
})
|
||||
.query(async ({ ctx }) => {
|
||||
if (ctx.user.role !== "owner" && ctx.user.role !== "admin") {
|
||||
const { accessedProjects, accessedEnvironments, accessedServices } =
|
||||
await findMemberByUserId(ctx.user.id, ctx.session.activeOrganizationId);
|
||||
@@ -375,7 +394,14 @@ export const projectRouter = createTRPCRouter({
|
||||
});
|
||||
}),
|
||||
|
||||
allForPermissions: withPermission("member", "update").query(
|
||||
allForPermissions: withPermission("member", "update")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "List all projects for permissions",
|
||||
description: "Returns all projects with their environments and services for the permissions management UI. Requires member update permission.",
|
||||
},
|
||||
})
|
||||
.query(
|
||||
async ({ ctx }) => {
|
||||
return await db.query.projects.findMany({
|
||||
where: eq(projects.organizationId, ctx.session.activeOrganizationId),
|
||||
@@ -488,6 +514,12 @@ export const projectRouter = createTRPCRouter({
|
||||
),
|
||||
|
||||
search: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Search projects",
|
||||
description: "Searches projects by name or description with pagination. Respects project-level access control for non-admin users.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
q: z.string().optional(),
|
||||
@@ -565,6 +597,12 @@ export const projectRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
remove: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Delete a project",
|
||||
description: "Permanently deletes a project and all its associated environments, services, and resources.",
|
||||
},
|
||||
})
|
||||
.input(apiRemoveProject)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -592,6 +630,12 @@ export const projectRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
update: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Update a project",
|
||||
description: "Updates a project's name, description, or environment variables. Validates organization ownership and project-level access permissions.",
|
||||
},
|
||||
})
|
||||
.input(apiUpdateProject)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -640,6 +684,12 @@ export const projectRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
duplicate: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Duplicate a project or environment",
|
||||
description: "Duplicates services from a source environment into a new project or into the same project. Copies applications, compose services, databases, and their associated domains, mounts, ports, and backups.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
sourceEnvironmentId: z.string(),
|
||||
|
||||
@@ -15,6 +15,12 @@ import {
|
||||
|
||||
export const redirectsRouter = createTRPCRouter({
|
||||
create: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Create a redirect",
|
||||
description: "Creates a new redirect rule for an application using a regex pattern. Logs an audit entry.",
|
||||
},
|
||||
})
|
||||
.input(apiCreateRedirect)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.applicationId, {
|
||||
@@ -31,6 +37,12 @@ export const redirectsRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
one: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get a redirect",
|
||||
description: "Returns a single redirect rule by its ID.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneRedirect)
|
||||
.query(async ({ input, ctx }) => {
|
||||
const redirect = await findRedirectById(input.redirectId);
|
||||
@@ -41,6 +53,12 @@ export const redirectsRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
delete: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Delete a redirect",
|
||||
description: "Deletes a redirect rule by its ID and logs an audit entry.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneRedirect)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const redirect = await findRedirectById(input.redirectId);
|
||||
@@ -57,6 +75,12 @@ export const redirectsRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
update: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Update a redirect",
|
||||
description: "Updates an existing redirect rule's configuration and logs an audit entry.",
|
||||
},
|
||||
})
|
||||
.input(apiUpdateRedirect)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const redirect = await findRedirectById(input.redirectId);
|
||||
|
||||
@@ -51,6 +51,12 @@ import {
|
||||
} from "@/server/db/schema";
|
||||
export const redisRouter = createTRPCRouter({
|
||||
create: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Create a Redis database",
|
||||
description: "Creates a new Redis database service with the specified configuration, sets up a persistent data volume, and registers it in the project.",
|
||||
},
|
||||
})
|
||||
.input(apiCreateRedis)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -108,6 +114,12 @@ export const redisRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
one: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get a Redis database by ID",
|
||||
description: "Returns the full details of a Redis database service, including its environment and project configuration.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneRedis)
|
||||
.query(async ({ input, ctx }) => {
|
||||
await checkServiceAccess(ctx, input.redisId, "read");
|
||||
@@ -126,6 +138,12 @@ export const redisRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
start: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Start a Redis database",
|
||||
description: "Starts the Docker container for the specified Redis database and sets its status to done.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneRedis)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.redisId, {
|
||||
@@ -151,6 +169,12 @@ export const redisRouter = createTRPCRouter({
|
||||
return redis;
|
||||
}),
|
||||
reload: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Reload a Redis database",
|
||||
description: "Restarts the Redis database by stopping and then starting its Docker container.",
|
||||
},
|
||||
})
|
||||
.input(apiResetRedis)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.redisId, {
|
||||
@@ -184,6 +208,12 @@ export const redisRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
stop: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Stop a Redis database",
|
||||
description: "Stops the Docker container for the specified Redis database and sets its status to idle.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneRedis)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.redisId, {
|
||||
@@ -208,6 +238,12 @@ export const redisRouter = createTRPCRouter({
|
||||
return redis;
|
||||
}),
|
||||
saveExternalPort: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Save the external port for a Redis database",
|
||||
description: "Updates the external port mapping for the Redis database and triggers a redeployment. Validates that the port is not already in use.",
|
||||
},
|
||||
})
|
||||
.input(apiSaveExternalPortRedis)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.redisId, {
|
||||
@@ -241,6 +277,12 @@ export const redisRouter = createTRPCRouter({
|
||||
return redis;
|
||||
}),
|
||||
deploy: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Deploy a Redis database",
|
||||
description: "Triggers a deployment for the specified Redis database, rebuilding and restarting its Docker container with the current configuration.",
|
||||
},
|
||||
})
|
||||
.input(apiDeployRedis)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.redisId, {
|
||||
@@ -293,6 +335,12 @@ export const redisRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
changeStatus: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Change Redis database status",
|
||||
description: "Updates the application status of a Redis database without starting or stopping the container.",
|
||||
},
|
||||
})
|
||||
.input(apiChangeRedisStatus)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.redisId, {
|
||||
@@ -311,6 +359,12 @@ export const redisRouter = createTRPCRouter({
|
||||
return mongo;
|
||||
}),
|
||||
remove: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Delete a Redis database",
|
||||
description: "Removes the Redis database service, its Docker container, and deletes the database record.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneRedis)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServiceAccess(ctx, input.redisId, "delete");
|
||||
@@ -346,6 +400,12 @@ export const redisRouter = createTRPCRouter({
|
||||
return redis;
|
||||
}),
|
||||
saveEnvironment: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Save environment variables for a Redis database",
|
||||
description: "Updates the environment variables for the specified Redis database service.",
|
||||
},
|
||||
})
|
||||
.input(apiSaveEnvironmentVariablesRedis)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.redisId, {
|
||||
@@ -370,6 +430,12 @@ export const redisRouter = createTRPCRouter({
|
||||
return true;
|
||||
}),
|
||||
update: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Update a Redis database",
|
||||
description: "Updates the configuration of an existing Redis database service.",
|
||||
},
|
||||
})
|
||||
.input(apiUpdateRedis)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const { redisId, ...rest } = input;
|
||||
@@ -396,6 +462,12 @@ export const redisRouter = createTRPCRouter({
|
||||
return true;
|
||||
}),
|
||||
changePassword: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Change Redis database password",
|
||||
description: "Changes the password for the Redis database by executing CONFIG SET requirepass inside the running container and updating the stored password.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
redisId: z.string().min(1),
|
||||
@@ -446,6 +518,12 @@ export const redisRouter = createTRPCRouter({
|
||||
return true;
|
||||
}),
|
||||
move: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Move a Redis database to another environment",
|
||||
description: "Moves the Redis database to a different environment within the same project.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
redisId: z.string(),
|
||||
@@ -482,6 +560,12 @@ export const redisRouter = createTRPCRouter({
|
||||
return updatedRedis;
|
||||
}),
|
||||
rebuild: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Rebuild a Redis database",
|
||||
description: "Rebuilds the Redis database Docker container from scratch, pulling the latest image and recreating the service.",
|
||||
},
|
||||
})
|
||||
.input(apiRebuildRedis)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.redisId, {
|
||||
@@ -497,6 +581,12 @@ export const redisRouter = createTRPCRouter({
|
||||
return true;
|
||||
}),
|
||||
search: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Search Redis databases",
|
||||
description: "Returns a paginated list of Redis databases matching the given filters. Supports searching by name, appName, description, project, and environment.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
q: z.string().optional(),
|
||||
@@ -590,6 +680,12 @@ export const redisRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
readLogs: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Read Redis container logs",
|
||||
description: "Retrieves the Docker container logs for the specified Redis database, with support for tail count, time-based filtering, and text search.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
apiFindOneRedis.extend({
|
||||
tail: z.number().int().min(1).max(10000).default(100),
|
||||
|
||||
@@ -23,6 +23,12 @@ import {
|
||||
import { createTRPCRouter, withPermission } from "../trpc";
|
||||
export const registryRouter = createTRPCRouter({
|
||||
create: withPermission("registry", "create")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Create registry",
|
||||
description: "Creates a new Docker registry entry for the current organization and logs an audit event.",
|
||||
},
|
||||
})
|
||||
.input(apiCreateRegistry)
|
||||
.mutation(async ({ ctx, input }) => {
|
||||
const reg = await createRegistry(input, ctx.session.activeOrganizationId);
|
||||
@@ -35,6 +41,12 @@ export const registryRouter = createTRPCRouter({
|
||||
return reg;
|
||||
}),
|
||||
remove: withPermission("registry", "delete")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Delete registry",
|
||||
description: "Removes a Docker registry entry by ID. Verifies organization ownership and logs an audit event before deletion.",
|
||||
},
|
||||
})
|
||||
.input(apiRemoveRegistry)
|
||||
.mutation(async ({ ctx, input }) => {
|
||||
const registry = await findRegistryById(input.registryId);
|
||||
@@ -53,6 +65,12 @@ export const registryRouter = createTRPCRouter({
|
||||
return await removeRegistry(input.registryId);
|
||||
}),
|
||||
update: withPermission("registry", "create")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Update registry",
|
||||
description: "Updates an existing Docker registry entry. Verifies organization ownership before applying changes and logs an audit event.",
|
||||
},
|
||||
})
|
||||
.input(apiUpdateRegistry)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const { registryId, ...rest } = input;
|
||||
@@ -82,13 +100,26 @@ export const registryRouter = createTRPCRouter({
|
||||
});
|
||||
return true;
|
||||
}),
|
||||
all: withPermission("registry", "read").query(async ({ ctx }) => {
|
||||
all: withPermission("registry", "read")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "List all registries",
|
||||
description: "Returns all Docker registry entries for the current organization.",
|
||||
},
|
||||
})
|
||||
.query(async ({ ctx }) => {
|
||||
const registryResponse = await db.query.registry.findMany({
|
||||
where: eq(registry.organizationId, ctx.session.activeOrganizationId),
|
||||
});
|
||||
return registryResponse;
|
||||
}),
|
||||
one: withPermission("registry", "read")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get registry",
|
||||
description: "Returns a single Docker registry entry by ID. Verifies the caller belongs to the same organization.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneRegistry)
|
||||
.query(async ({ input, ctx }) => {
|
||||
const registry = await findRegistryById(input.registryId);
|
||||
@@ -101,6 +132,12 @@ export const registryRouter = createTRPCRouter({
|
||||
return registry;
|
||||
}),
|
||||
testRegistry: withPermission("registry", "read")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Test registry credentials",
|
||||
description: "Attempts a docker login with the provided credentials to verify the registry URL, username, and password are valid. Can run locally or on a remote server.",
|
||||
},
|
||||
})
|
||||
.input(apiTestRegistry)
|
||||
.mutation(async ({ input }) => {
|
||||
try {
|
||||
@@ -143,6 +180,12 @@ export const registryRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
testRegistryById: withPermission("registry", "read")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Test registry connection by ID",
|
||||
description: "Looks up a saved registry by ID and attempts a docker login with its stored credentials. Verifies organization ownership before testing.",
|
||||
},
|
||||
})
|
||||
.input(apiTestRegistryById)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
|
||||
@@ -11,6 +11,12 @@ import { createTRPCRouter, protectedProcedure } from "../trpc";
|
||||
|
||||
export const rollbackRouter = createTRPCRouter({
|
||||
delete: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Delete a rollback",
|
||||
description: "Permanently removes a rollback record by its ID.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneRollback)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -40,6 +46,12 @@ export const rollbackRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
rollback: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Perform a rollback",
|
||||
description: "Rolls back an application to a previous deployment by restoring its Docker image and redeploying.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneRollback)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
|
||||
@@ -22,6 +22,12 @@ import { removeJob, schedule } from "@/server/utils/backup";
|
||||
import { createTRPCRouter, protectedProcedure } from "../trpc";
|
||||
export const scheduleRouter = createTRPCRouter({
|
||||
create: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Create a scheduled job",
|
||||
description: "Creates a new scheduled job for an application or compose service. If enabled, the job is automatically scheduled using the provided cron expression and timezone.",
|
||||
},
|
||||
})
|
||||
.input(createScheduleSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const serviceId = input.applicationId || input.composeId;
|
||||
@@ -54,6 +60,12 @@ export const scheduleRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
update: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Update a scheduled job",
|
||||
description: "Updates an existing scheduled job configuration. Reschedules or removes the job depending on the enabled state.",
|
||||
},
|
||||
})
|
||||
.input(updateScheduleSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const existingSchedule = await findScheduleById(input.scheduleId);
|
||||
@@ -99,6 +111,12 @@ export const scheduleRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
delete: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Delete a scheduled job",
|
||||
description: "Permanently removes a scheduled job and unschedules any associated cron job.",
|
||||
},
|
||||
})
|
||||
.input(z.object({ scheduleId: z.string() }))
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const scheduleItem = await findScheduleById(input.scheduleId);
|
||||
@@ -129,6 +147,12 @@ export const scheduleRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
list: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "List scheduled jobs",
|
||||
description: "Returns all scheduled jobs for a given service (application, compose, server, or dokploy-server), including their deployment history.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
id: z.string(),
|
||||
@@ -170,6 +194,12 @@ export const scheduleRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
one: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get a scheduled job",
|
||||
description: "Returns the details of a specific scheduled job by its ID.",
|
||||
},
|
||||
})
|
||||
.input(z.object({ scheduleId: z.string() }))
|
||||
.query(async ({ input, ctx }) => {
|
||||
const schedule = await findScheduleById(input.scheduleId);
|
||||
@@ -183,6 +213,12 @@ export const scheduleRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
runManually: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Run a scheduled job manually",
|
||||
description: "Immediately executes a scheduled job outside of its normal cron schedule.",
|
||||
},
|
||||
})
|
||||
.input(z.object({ scheduleId: z.string().min(1) }))
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const scheduleItem = await findScheduleById(input.scheduleId);
|
||||
|
||||
@@ -15,6 +15,12 @@ import {
|
||||
|
||||
export const securityRouter = createTRPCRouter({
|
||||
create: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Create a security entry",
|
||||
description: "Creates a new HTTP basic auth security entry for an application with the provided username and password. Logs an audit entry.",
|
||||
},
|
||||
})
|
||||
.input(apiCreateSecurity)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await checkServicePermissionAndAccess(ctx, input.applicationId, {
|
||||
@@ -31,6 +37,12 @@ export const securityRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
one: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get a security entry",
|
||||
description: "Returns a single HTTP basic auth security entry by its ID.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneSecurity)
|
||||
.query(async ({ input, ctx }) => {
|
||||
const security = await findSecurityById(input.securityId);
|
||||
@@ -41,6 +53,12 @@ export const securityRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
delete: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Delete a security entry",
|
||||
description: "Deletes an HTTP basic auth security entry by its ID and logs an audit entry.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneSecurity)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const security = await findSecurityById(input.securityId);
|
||||
@@ -57,6 +75,12 @@ export const securityRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
update: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Update a security entry",
|
||||
description: "Updates an existing HTTP basic auth security entry's configuration and logs an audit entry.",
|
||||
},
|
||||
})
|
||||
.input(apiUpdateSecurity)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const security = await findSecurityById(input.securityId);
|
||||
|
||||
@@ -48,6 +48,12 @@ import {
|
||||
|
||||
export const serverRouter = createTRPCRouter({
|
||||
create: withPermission("server", "create")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Create a server",
|
||||
description: "Creates a new server in the organization. In cloud mode, enforces the user's server quantity limit. Returns the newly created server.",
|
||||
},
|
||||
})
|
||||
.input(apiCreateServer)
|
||||
.mutation(async ({ ctx, input }) => {
|
||||
try {
|
||||
@@ -80,6 +86,12 @@ export const serverRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
one: withPermission("server", "read")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get a server",
|
||||
description: "Retrieves a single server by its ID. Validates that the user has access to the server within their organization.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneServer)
|
||||
.query(async ({ input, ctx }) => {
|
||||
const server = await findServerById(input.serverId);
|
||||
@@ -101,13 +113,26 @@ export const serverRouter = createTRPCRouter({
|
||||
return server;
|
||||
}),
|
||||
getDefaultCommand: withPermission("server", "read")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get default server command",
|
||||
description: "Returns the default setup command for a server. The command varies depending on whether the server is a build server or a deploy server.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneServer)
|
||||
.query(async ({ input }) => {
|
||||
const server = await findServerById(input.serverId);
|
||||
const isBuildServer = server.serverType === "build";
|
||||
return defaultCommand(isBuildServer);
|
||||
}),
|
||||
all: withPermission("server", "read").query(async ({ ctx }) => {
|
||||
all: withPermission("server", "read")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "List all servers",
|
||||
description: "Returns all servers in the current organization along with a count of associated services (applications, compose, databases). Results are filtered by the user's accessible server permissions.",
|
||||
},
|
||||
})
|
||||
.query(async ({ ctx }) => {
|
||||
const accessibleIds = await getAccessibleServerIds(ctx.session);
|
||||
|
||||
const result = await db
|
||||
@@ -130,6 +155,12 @@ export const serverRouter = createTRPCRouter({
|
||||
return result.filter((s) => accessibleIds.has(s.serverId));
|
||||
}),
|
||||
allForPermissions: withPermission("member", "update")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "List all servers for permissions",
|
||||
description: "Returns a minimal list of servers (ID, name, IP, type) used for configuring member permissions. Requires a valid enterprise license.",
|
||||
},
|
||||
})
|
||||
.use(async ({ ctx, next }) => {
|
||||
const licensed = await hasValidLicense(ctx.session.activeOrganizationId);
|
||||
if (!licensed) {
|
||||
@@ -152,7 +183,14 @@ export const serverRouter = createTRPCRouter({
|
||||
where: eq(server.organizationId, ctx.session.activeOrganizationId),
|
||||
});
|
||||
}),
|
||||
count: protectedProcedure.query(async ({ ctx }) => {
|
||||
count: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get server count",
|
||||
description: "Returns the total number of servers across all organizations owned by the current user.",
|
||||
},
|
||||
})
|
||||
.query(async ({ ctx }) => {
|
||||
const organizations = await db.query.organization.findMany({
|
||||
where: eq(organization.ownerId, ctx.user.id),
|
||||
with: {
|
||||
@@ -164,7 +202,14 @@ export const serverRouter = createTRPCRouter({
|
||||
|
||||
return servers.length ?? 0;
|
||||
}),
|
||||
withSSHKey: withPermission("server", "read").query(async ({ ctx }) => {
|
||||
withSSHKey: withPermission("server", "read")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "List servers with SSH keys",
|
||||
description: "Returns all deploy-type servers that have an SSH key configured. In cloud mode, only active servers are included. Results are filtered by the user's accessible server permissions.",
|
||||
},
|
||||
})
|
||||
.query(async ({ ctx }) => {
|
||||
const accessibleIds = await getAccessibleServerIds(ctx.session);
|
||||
|
||||
const result = await db.query.server.findMany({
|
||||
@@ -184,7 +229,14 @@ export const serverRouter = createTRPCRouter({
|
||||
});
|
||||
return result.filter((s) => accessibleIds.has(s.serverId));
|
||||
}),
|
||||
buildServers: withPermission("server", "read").query(async ({ ctx }) => {
|
||||
buildServers: withPermission("server", "read")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "List build servers",
|
||||
description: "Returns all build-type servers that have an SSH key configured. In cloud mode, only active servers are included. Results are filtered by the user's accessible server permissions.",
|
||||
},
|
||||
})
|
||||
.query(async ({ ctx }) => {
|
||||
const accessibleIds = await getAccessibleServerIds(ctx.session);
|
||||
|
||||
const result = await db.query.server.findMany({
|
||||
@@ -205,6 +257,12 @@ export const serverRouter = createTRPCRouter({
|
||||
return result.filter((s) => accessibleIds.has(s.serverId));
|
||||
}),
|
||||
setup: withPermission("server", "create")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Setup a server",
|
||||
description: "Runs the initial setup process on a remote server, installing required dependencies and configuring Docker. An audit log entry is created.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneServer)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -256,6 +314,12 @@ export const serverRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
validate: withPermission("server", "read")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Validate server configuration",
|
||||
description: "Checks the server for required tools and configuration including Docker, Rclone, Nixpacks, Buildpacks, Railpack, Swarm mode, network setup, and privilege mode.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneServer)
|
||||
.query(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -304,6 +368,12 @@ export const serverRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
security: withPermission("server", "read")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get server security audit",
|
||||
description: "Performs a security audit on the server, checking UFW firewall, SSH configuration, non-root user setup, unattended upgrades, and Fail2Ban status.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneServer)
|
||||
.query(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -354,6 +424,12 @@ export const serverRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
setupMonitoring: withPermission("server", "create")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Setup server monitoring",
|
||||
description: "Configures and deploys the monitoring agent on a server with the specified metrics configuration including refresh rates, retention, thresholds, and container service filters.",
|
||||
},
|
||||
})
|
||||
.input(apiUpdateServerMonitoring)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -402,6 +478,12 @@ export const serverRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
remove: withPermission("server", "delete")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Remove a server",
|
||||
description: "Deletes a server and removes all associated deployments. Fails if the server has active services. In cloud mode, updates the user's server quantity allocation.",
|
||||
},
|
||||
})
|
||||
.input(apiRemoveServer)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -435,6 +517,12 @@ export const serverRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
update: withPermission("server", "create")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Update a server",
|
||||
description: "Updates the configuration of an existing server. Fails if the server is inactive. An audit log entry is created for the update.",
|
||||
},
|
||||
})
|
||||
.input(apiUpdateServer)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -467,14 +555,28 @@ export const serverRouter = createTRPCRouter({
|
||||
throw error;
|
||||
}
|
||||
}),
|
||||
publicIp: protectedProcedure.query(async () => {
|
||||
publicIp: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get public IP address",
|
||||
description: "Returns the public IP address of the local server. Returns an empty string in cloud mode.",
|
||||
},
|
||||
})
|
||||
.query(async () => {
|
||||
if (IS_CLOUD) {
|
||||
return "";
|
||||
}
|
||||
const ip = await getPublicIpWithFallback();
|
||||
return ip;
|
||||
}),
|
||||
getServerTime: protectedProcedure.query(() => {
|
||||
getServerTime: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get server time",
|
||||
description: "Returns the current server time and timezone. Returns null in cloud mode.",
|
||||
},
|
||||
})
|
||||
.query(() => {
|
||||
if (IS_CLOUD) {
|
||||
return null;
|
||||
}
|
||||
@@ -484,6 +586,12 @@ export const serverRouter = createTRPCRouter({
|
||||
};
|
||||
}),
|
||||
getServerMetrics: withPermission("monitoring", "read")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get server metrics",
|
||||
description: "Fetches monitoring metrics (CPU, memory, disk, network) from the server's monitoring agent endpoint. Requires the monitoring service to be configured and running.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
url: z.string(),
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -25,6 +25,12 @@ import {
|
||||
|
||||
export const sshRouter = createTRPCRouter({
|
||||
create: withPermission("sshKeys", "create")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Create SSH key",
|
||||
description: "Stores a new SSH key for the current organization and logs an audit event.",
|
||||
},
|
||||
})
|
||||
.input(apiCreateSshKey)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -46,6 +52,12 @@ export const sshRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
remove: withPermission("sshKeys", "delete")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Delete SSH key",
|
||||
description: "Removes an SSH key by ID. Verifies organization ownership and logs an audit event before deletion.",
|
||||
},
|
||||
})
|
||||
.input(apiRemoveSshKey)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -69,6 +81,12 @@ export const sshRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
one: withPermission("sshKeys", "read")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get SSH key",
|
||||
description: "Returns a single SSH key by ID. Verifies the caller belongs to the same organization.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneSshKey)
|
||||
.query(async ({ input, ctx }) => {
|
||||
const sshKey = await findSSHKeyById(input.sshKeyId);
|
||||
@@ -81,13 +99,27 @@ export const sshRouter = createTRPCRouter({
|
||||
}
|
||||
return sshKey;
|
||||
}),
|
||||
all: withPermission("sshKeys", "read").query(async ({ ctx }) => {
|
||||
all: withPermission("sshKeys", "read")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "List all SSH keys",
|
||||
description: "Returns all SSH keys for the current organization, ordered by creation date descending.",
|
||||
},
|
||||
})
|
||||
.query(async ({ ctx }) => {
|
||||
return await db.query.sshKeys.findMany({
|
||||
where: eq(sshKeys.organizationId, ctx.session.activeOrganizationId),
|
||||
orderBy: desc(sshKeys.createdAt),
|
||||
});
|
||||
}),
|
||||
allForApps: protectedProcedure.query(async ({ ctx }) => {
|
||||
allForApps: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "List SSH keys for app selection",
|
||||
description: "Returns a lightweight list of SSH keys (ID and name only) for the current organization, suitable for dropdown selectors in application forms.",
|
||||
},
|
||||
})
|
||||
.query(async ({ ctx }) => {
|
||||
return await db.query.sshKeys.findMany({
|
||||
columns: {
|
||||
sshKeyId: true,
|
||||
@@ -98,11 +130,23 @@ export const sshRouter = createTRPCRouter({
|
||||
});
|
||||
}),
|
||||
generate: withPermission("sshKeys", "read")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Generate SSH key pair",
|
||||
description: "Generates a new SSH key pair of the specified type (RSA, ED25519, etc.) and returns both public and private keys.",
|
||||
},
|
||||
})
|
||||
.input(apiGenerateSSHKey)
|
||||
.mutation(async ({ input }) => {
|
||||
return await generateSSHKey(input.type);
|
||||
}),
|
||||
update: withPermission("sshKeys", "create")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Update SSH key",
|
||||
description: "Updates an existing SSH key. Verifies organization ownership before applying changes and logs an audit event.",
|
||||
},
|
||||
})
|
||||
.input(apiUpdateSshKey)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
|
||||
@@ -30,7 +30,14 @@ import {
|
||||
|
||||
export const stripeRouter = createTRPCRouter({
|
||||
/** Returns the current billing plan for the user's organization. Used to gate features like chat (Startup only). */
|
||||
getCurrentPlan: protectedProcedure.query(async ({ ctx }) => {
|
||||
getCurrentPlan: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get current billing plan",
|
||||
description: "Returns the active Stripe billing plan (hobby, startup, or legacy) for the caller's organization owner. Returns null if not on cloud or no subscription exists.",
|
||||
},
|
||||
})
|
||||
.query(async ({ ctx }) => {
|
||||
if (!IS_CLOUD) return null;
|
||||
const owner = await findUserById(ctx.user.ownerId);
|
||||
if (!owner?.stripeCustomerId) return null;
|
||||
@@ -71,7 +78,14 @@ export const stripeRouter = createTRPCRouter({
|
||||
return null;
|
||||
}),
|
||||
|
||||
getProducts: adminProcedure.query(async ({ ctx }) => {
|
||||
getProducts: adminProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "List Stripe products and subscriptions",
|
||||
description: "Returns available Stripe products, the user's active subscriptions, current plan tier, billing interval, and price amount.",
|
||||
},
|
||||
})
|
||||
.query(async ({ ctx }) => {
|
||||
const user = await findUserById(ctx.user.ownerId);
|
||||
const stripeCustomerId = user.stripeCustomerId;
|
||||
|
||||
@@ -162,6 +176,12 @@ export const stripeRouter = createTRPCRouter({
|
||||
};
|
||||
}),
|
||||
createCheckoutSession: adminProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Create Stripe checkout session",
|
||||
description: "Creates a Stripe checkout session for subscribing to a billing plan. Handles customer creation or reuse and returns the session ID for redirect.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z
|
||||
.object({
|
||||
@@ -222,7 +242,14 @@ export const stripeRouter = createTRPCRouter({
|
||||
|
||||
return { sessionId: session.id };
|
||||
}),
|
||||
createCustomerPortalSession: adminProcedure.mutation(async ({ ctx }) => {
|
||||
createCustomerPortalSession: adminProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Create Stripe customer portal session",
|
||||
description: "Creates a Stripe billing portal session URL so the user can manage their subscription, payment methods, and invoices.",
|
||||
},
|
||||
})
|
||||
.mutation(async ({ ctx }) => {
|
||||
// Use the organization's owner account for billing portal
|
||||
const owner = await findUserById(ctx.user.ownerId);
|
||||
|
||||
@@ -253,6 +280,12 @@ export const stripeRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
upgradeSubscription: adminProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Upgrade subscription",
|
||||
description: "Upgrades or changes the current Stripe subscription to a different tier or server quantity. Applies prorated charges for the billing period change.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z
|
||||
.object({
|
||||
@@ -324,7 +357,14 @@ export const stripeRouter = createTRPCRouter({
|
||||
return { ok: true };
|
||||
}),
|
||||
|
||||
canCreateMoreServers: withPermission("server", "create").query(
|
||||
canCreateMoreServers: withPermission("server", "create")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Check server creation quota",
|
||||
description: "Returns whether the organization can create more servers based on their subscription's server quantity limit. Always returns true for self-hosted instances.",
|
||||
},
|
||||
})
|
||||
.query(
|
||||
async ({ ctx }) => {
|
||||
const user = await findUserById(ctx.user.ownerId);
|
||||
const servers = await findServersByUserId(user.id);
|
||||
@@ -338,6 +378,12 @@ export const stripeRouter = createTRPCRouter({
|
||||
),
|
||||
|
||||
updateInvoiceNotifications: adminProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Update invoice notification preference",
|
||||
description: "Enables or disables email notifications for invoice events. Only available on Dokploy Cloud.",
|
||||
},
|
||||
})
|
||||
.input(z.object({ enabled: z.boolean() }))
|
||||
.mutation(async ({ ctx, input }) => {
|
||||
if (!IS_CLOUD) {
|
||||
@@ -353,7 +399,14 @@ export const stripeRouter = createTRPCRouter({
|
||||
return { ok: true };
|
||||
}),
|
||||
|
||||
getInvoices: adminProcedure.query(async ({ ctx }) => {
|
||||
getInvoices: adminProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "List invoices",
|
||||
description: "Returns up to 100 Stripe invoices for the organization owner, including status, amounts, and download links.",
|
||||
},
|
||||
})
|
||||
.query(async ({ ctx }) => {
|
||||
const user = await findUserById(ctx.user.ownerId);
|
||||
const stripeCustomerId = user.stripeCustomerId;
|
||||
|
||||
|
||||
@@ -13,6 +13,12 @@ import { containerIdRegex } from "./docker";
|
||||
|
||||
export const swarmRouter = createTRPCRouter({
|
||||
getNodes: withPermission("server", "read")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get Swarm nodes",
|
||||
description: "Retrieves all nodes in the Docker Swarm. Optionally targets a remote server by ID.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
serverId: z.string().optional(),
|
||||
@@ -22,11 +28,23 @@ export const swarmRouter = createTRPCRouter({
|
||||
return await getSwarmNodes(input.serverId);
|
||||
}),
|
||||
getNodeInfo: withPermission("server", "read")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get Swarm node info",
|
||||
description: "Retrieves detailed information about a specific Docker Swarm node by its node ID. Optionally targets a remote server.",
|
||||
},
|
||||
})
|
||||
.input(z.object({ nodeId: z.string(), serverId: z.string().optional() }))
|
||||
.query(async ({ input }) => {
|
||||
return await getNodeInfo(input.nodeId, input.serverId);
|
||||
}),
|
||||
getNodeApps: withPermission("server", "read")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get Swarm node applications",
|
||||
description: "Retrieves all applications (services) running across Docker Swarm nodes. Optionally targets a remote server.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
serverId: z.string().optional(),
|
||||
@@ -58,6 +76,12 @@ export const swarmRouter = createTRPCRouter({
|
||||
return await getApplicationInfo(input.appName, input.serverId);
|
||||
}),
|
||||
getContainerStats: withPermission("server", "read")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get container stats",
|
||||
description: "Retrieves resource usage statistics for all containers. Optionally targets a remote server and validates organization access.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
serverId: z.string().optional(),
|
||||
|
||||
@@ -16,6 +16,12 @@ import { createTRPCRouter, protectedProcedure, withPermission } from "../trpc";
|
||||
|
||||
export const tagRouter = createTRPCRouter({
|
||||
create: withPermission("tag", "create")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Create tag",
|
||||
description: "Creates a new tag with a name and color for the current organization. Tag names must be unique within the organization.",
|
||||
},
|
||||
})
|
||||
.input(apiCreateTag)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -47,7 +53,14 @@ export const tagRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
|
||||
all: protectedProcedure.query(async ({ ctx }) => {
|
||||
all: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "List all tags",
|
||||
description: "Returns all tags for the current organization, ordered alphabetically by name.",
|
||||
},
|
||||
})
|
||||
.query(async ({ ctx }) => {
|
||||
try {
|
||||
const organizationTags = await db.query.tags.findMany({
|
||||
where: eq(tags.organizationId, ctx.session.activeOrganizationId),
|
||||
@@ -64,7 +77,15 @@ export const tagRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
|
||||
one: protectedProcedure.input(apiFindOneTag).query(async ({ input, ctx }) => {
|
||||
one: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get tag",
|
||||
description: "Returns a single tag by ID. Only returns tags belonging to the caller's organization.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneTag)
|
||||
.query(async ({ input, ctx }) => {
|
||||
try {
|
||||
const tag = await db.query.tags.findFirst({
|
||||
where: and(
|
||||
@@ -94,6 +115,12 @@ export const tagRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
update: withPermission("tag", "update")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Update tag",
|
||||
description: "Updates an existing tag's name and/or color. Verifies the tag belongs to the caller's organization. Tag names must remain unique.",
|
||||
},
|
||||
})
|
||||
.input(apiUpdateTag)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -144,6 +171,12 @@ export const tagRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
remove: withPermission("tag", "delete")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Delete tag",
|
||||
description: "Deletes a tag by ID. Cascade-deletes all project-tag associations. Verifies the tag belongs to the caller's organization.",
|
||||
},
|
||||
})
|
||||
.input(apiRemoveTag)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -179,6 +212,12 @@ export const tagRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
assignToProject: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Assign tag to project",
|
||||
description: "Associates a tag with a project. Verifies that both the tag and project belong to the caller's organization and that the caller has project access.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
projectId: z.string().min(1),
|
||||
@@ -267,6 +306,12 @@ export const tagRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
removeFromProject: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Remove tag from project",
|
||||
description: "Removes a tag-project association. Verifies that both the tag and project belong to the caller's organization and that the caller has project access.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
projectId: z.string().min(1),
|
||||
@@ -347,6 +392,12 @@ export const tagRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
bulkAssign: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Bulk assign tags to project",
|
||||
description: "Replaces all tag associations for a project with the provided list of tag IDs. Removes existing associations first, then inserts the new set.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
projectId: z.string().min(1),
|
||||
|
||||
@@ -60,7 +60,14 @@ const apiCreateApiKey = z.object({
|
||||
});
|
||||
|
||||
export const userRouter = createTRPCRouter({
|
||||
all: withPermission("member", "read").query(async ({ ctx }) => {
|
||||
all: withPermission("member", "read")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "List all organization members",
|
||||
description: "Retrieve all members of the current active organization, including their associated user data, ordered by creation date.",
|
||||
},
|
||||
})
|
||||
.query(async ({ ctx }) => {
|
||||
return await db.query.member.findMany({
|
||||
where: eq(member.organizationId, ctx.session.activeOrganizationId),
|
||||
with: {
|
||||
@@ -70,6 +77,12 @@ export const userRouter = createTRPCRouter({
|
||||
});
|
||||
}),
|
||||
one: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get a user by ID",
|
||||
description: "Retrieve a specific user's membership and profile within the active organization. Users can view their own data; admins and owners can view any member. Requires member.update permission for non-self lookups.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
userId: z.string(),
|
||||
@@ -114,7 +127,14 @@ export const userRouter = createTRPCRouter({
|
||||
|
||||
return memberResult;
|
||||
}),
|
||||
session: publicProcedure.query(async ({ ctx }) => {
|
||||
session: publicProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get current session",
|
||||
description: "Return the current user's ID and active organization ID from the session. Returns null if no valid session exists.",
|
||||
},
|
||||
})
|
||||
.query(async ({ ctx }) => {
|
||||
if (!ctx.user || !ctx.session || !ctx.session.activeOrganizationId) {
|
||||
return null;
|
||||
}
|
||||
@@ -127,7 +147,14 @@ export const userRouter = createTRPCRouter({
|
||||
},
|
||||
};
|
||||
}),
|
||||
get: protectedProcedure.query(async ({ ctx }) => {
|
||||
get: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get current user profile",
|
||||
description: "Retrieve the current authenticated user's membership record including user profile and API keys for the active organization.",
|
||||
},
|
||||
})
|
||||
.query(async ({ ctx }) => {
|
||||
const memberResult = await db.query.member.findFirst({
|
||||
where: and(
|
||||
eq(member.userId, ctx.user.id),
|
||||
@@ -144,10 +171,24 @@ export const userRouter = createTRPCRouter({
|
||||
|
||||
return memberResult;
|
||||
}),
|
||||
getPermissions: protectedProcedure.query(async ({ ctx }) => {
|
||||
getPermissions: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get resolved permissions",
|
||||
description: "Return the fully resolved permissions for the current user in the active organization, combining role-based and custom permissions.",
|
||||
},
|
||||
})
|
||||
.query(async ({ ctx }) => {
|
||||
return resolvePermissions(ctx);
|
||||
}),
|
||||
haveRootAccess: protectedProcedure.query(async ({ ctx }) => {
|
||||
haveRootAccess: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Check root access",
|
||||
description: "Check whether the current user has root admin access. Only returns true in cloud mode for the designated admin user or impersonating sessions.",
|
||||
},
|
||||
})
|
||||
.query(async ({ ctx }) => {
|
||||
if (!IS_CLOUD) {
|
||||
return false;
|
||||
}
|
||||
@@ -159,7 +200,14 @@ export const userRouter = createTRPCRouter({
|
||||
}
|
||||
return false;
|
||||
}),
|
||||
getBackups: adminProcedure.query(async ({ ctx }) => {
|
||||
getBackups: adminProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get user backups",
|
||||
description: "Retrieve the current admin user's backup configurations including destinations, deployments, and API keys.",
|
||||
},
|
||||
})
|
||||
.query(async ({ ctx }) => {
|
||||
const memberResult = await db.query.member.findFirst({
|
||||
where: and(
|
||||
eq(member.userId, ctx.user.id),
|
||||
@@ -182,8 +230,14 @@ export const userRouter = createTRPCRouter({
|
||||
|
||||
return memberResult?.user;
|
||||
}),
|
||||
getServerMetrics: withPermission("monitoring", "read").query(
|
||||
async ({ ctx }) => {
|
||||
getServerMetrics: withPermission("monitoring", "read")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get server metrics user",
|
||||
description: "Retrieve the user record associated with server metrics access for the current organization membership.",
|
||||
},
|
||||
})
|
||||
.query(async ({ ctx }) => {
|
||||
const memberResult = await db.query.member.findFirst({
|
||||
where: and(
|
||||
eq(member.userId, ctx.user.id),
|
||||
@@ -198,6 +252,12 @@ export const userRouter = createTRPCRouter({
|
||||
},
|
||||
),
|
||||
update: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Update current user",
|
||||
description: "Update the current user's profile. If changing the password, the current password must be provided and verified. Logs an audit event on success.",
|
||||
},
|
||||
})
|
||||
.input(apiUpdateUser)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
if (input.password || input.currentPassword) {
|
||||
@@ -248,12 +308,24 @@ export const userRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
getUserByToken: publicProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get user by token",
|
||||
description: "Look up a user by their authentication token. This is a public endpoint that does not require an active session.",
|
||||
},
|
||||
})
|
||||
.input(apiFindOneToken)
|
||||
.query(async ({ input }) => {
|
||||
return await getUserByToken(input.token);
|
||||
}),
|
||||
getMetricsToken: withPermission("monitoring", "read").query(
|
||||
async ({ ctx }) => {
|
||||
getMetricsToken: withPermission("monitoring", "read")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get metrics token and configuration",
|
||||
description: "Retrieve the server IP, paid features flag, and monitoring configuration needed for metrics collection.",
|
||||
},
|
||||
})
|
||||
.query(async ({ ctx }) => {
|
||||
const user = await findUserById(ctx.user.ownerId);
|
||||
const settings = await getWebServerSettings();
|
||||
return {
|
||||
@@ -264,6 +336,12 @@ export const userRouter = createTRPCRouter({
|
||||
},
|
||||
),
|
||||
remove: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Remove a user",
|
||||
description: "Delete a user from the organization. Only owners and admins can remove users; owners cannot be removed, and admins cannot remove themselves or other admins. Disabled on cloud.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
userId: z.string(),
|
||||
@@ -333,6 +411,12 @@ export const userRouter = createTRPCRouter({
|
||||
return result;
|
||||
}),
|
||||
assignPermissions: withPermission("member", "update")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Assign member permissions",
|
||||
description: "Update permissions for a specific member in the organization. Only the organization owner can assign permissions. Git provider and server access restrictions require a valid license.",
|
||||
},
|
||||
})
|
||||
.input(apiAssignPermissions)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
@@ -383,7 +467,14 @@ export const userRouter = createTRPCRouter({
|
||||
throw error;
|
||||
}
|
||||
}),
|
||||
getInvitations: protectedProcedure.query(async ({ ctx }) => {
|
||||
getInvitations: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get pending invitations for current user",
|
||||
description: "Retrieve all pending organization invitations for the current user's email that have not yet expired.",
|
||||
},
|
||||
})
|
||||
.query(async ({ ctx }) => {
|
||||
return await db.query.invitation.findMany({
|
||||
where: and(
|
||||
eq(invitation.email, ctx.user.email),
|
||||
@@ -397,6 +488,12 @@ export const userRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
getContainerMetrics: withPermission("monitoring", "read")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get container metrics",
|
||||
description: "Fetch monitoring metrics for a specific container by querying the metrics endpoint. Requires an application name, metrics URL, and authentication token.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
url: z.string(),
|
||||
@@ -455,11 +552,24 @@ export const userRouter = createTRPCRouter({
|
||||
}
|
||||
}),
|
||||
|
||||
generateToken: protectedProcedure.mutation(async () => {
|
||||
generateToken: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Generate authentication token",
|
||||
description: "Generate a new authentication token for the current user.",
|
||||
},
|
||||
})
|
||||
.mutation(async () => {
|
||||
return "token";
|
||||
}),
|
||||
|
||||
deleteApiKey: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Delete an API key",
|
||||
description: "Delete an API key by ID. Only the owner of the API key can delete it. Logs an audit event on success.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
apiKeyId: z.string(),
|
||||
@@ -499,6 +609,12 @@ export const userRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
createApiKey: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Create an API key",
|
||||
description: "Create a new API key for the current user, scoped to a specific organization. Supports optional rate limiting and request limiting configuration.",
|
||||
},
|
||||
})
|
||||
.input(apiCreateApiKey)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
// Verify user is a member of the organization specified in metadata
|
||||
@@ -529,6 +645,12 @@ export const userRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
checkUserOrganizations: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Check user organization count",
|
||||
description: "Return the number of organizations a user belongs to. Users can check their own count; admins and owners can check counts for members in the active organization.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
userId: z.string(),
|
||||
@@ -570,6 +692,12 @@ export const userRouter = createTRPCRouter({
|
||||
return organizations.length;
|
||||
}),
|
||||
createUserWithCredentials: withPermission("member", "create")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Create user with credentials",
|
||||
description: "Create a new user with email and password and add them to the active organization with the specified role. Only available in self-hosted mode.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
email: z.string().email(),
|
||||
@@ -601,6 +729,12 @@ export const userRouter = createTRPCRouter({
|
||||
});
|
||||
}),
|
||||
sendInvitation: withPermission("member", "create")
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Send invitation email",
|
||||
description: "Send an invitation email to a pending invitee using a configured email or Resend notification provider. Returns the generated invite link. Disabled on cloud.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
invitationId: z.string().min(1),
|
||||
@@ -676,7 +810,14 @@ export const userRouter = createTRPCRouter({
|
||||
return inviteLink;
|
||||
}),
|
||||
|
||||
getBookmarkedTemplates: protectedProcedure.query(async ({ ctx }) => {
|
||||
getBookmarkedTemplates: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get bookmarked templates",
|
||||
description: "Retrieve the list of template IDs that the current user has bookmarked.",
|
||||
},
|
||||
})
|
||||
.query(async ({ ctx }) => {
|
||||
const result = await db.query.user.findFirst({
|
||||
where: eq(user.id, ctx.user.id),
|
||||
columns: { bookmarkedTemplates: true },
|
||||
@@ -686,6 +827,12 @@ export const userRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
toggleTemplateBookmark: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Toggle template bookmark",
|
||||
description: "Add or remove a template from the current user's bookmarks. Returns whether the template is now bookmarked.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
templateId: z.string().min(1),
|
||||
|
||||
@@ -30,6 +30,12 @@ import { createTRPCRouter, protectedProcedure, withPermission } from "../trpc";
|
||||
|
||||
export const volumeBackupsRouter = createTRPCRouter({
|
||||
list: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "List volume backups",
|
||||
description: "Returns all volume backup configurations for a given service, including related service details.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
id: z.string().min(1),
|
||||
@@ -65,6 +71,12 @@ export const volumeBackupsRouter = createTRPCRouter({
|
||||
});
|
||||
}),
|
||||
create: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Create a volume backup",
|
||||
description: "Creates a new volume backup configuration for a service. If enabled, automatically schedules the backup using the provided cron expression.",
|
||||
},
|
||||
})
|
||||
.input(createVolumeBackupSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const serviceId =
|
||||
@@ -102,6 +114,12 @@ export const volumeBackupsRouter = createTRPCRouter({
|
||||
return newVolumeBackup;
|
||||
}),
|
||||
one: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Get a volume backup",
|
||||
description: "Returns the details of a specific volume backup configuration by its ID.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
volumeBackupId: z.string().min(1),
|
||||
@@ -126,6 +144,12 @@ export const volumeBackupsRouter = createTRPCRouter({
|
||||
return vb;
|
||||
}),
|
||||
delete: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Delete a volume backup",
|
||||
description: "Permanently removes a volume backup configuration by its ID.",
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
volumeBackupId: z.string().min(1),
|
||||
@@ -156,6 +180,12 @@ export const volumeBackupsRouter = createTRPCRouter({
|
||||
return result;
|
||||
}),
|
||||
update: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Update a volume backup",
|
||||
description: "Updates an existing volume backup configuration. Reschedules or removes the backup job depending on the enabled state.",
|
||||
},
|
||||
})
|
||||
.input(updateVolumeBackupSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const existingVb = await findVolumeBackupById(input.volumeBackupId);
|
||||
@@ -216,6 +246,12 @@ export const volumeBackupsRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
runManually: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
summary: "Run a volume backup manually",
|
||||
description: "Immediately executes a volume backup outside of its normal cron schedule.",
|
||||
},
|
||||
})
|
||||
.input(z.object({ volumeBackupId: z.string().min(1) }))
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const vb = await findVolumeBackupById(input.volumeBackupId);
|
||||
|
||||
@@ -13,7 +13,12 @@ import { hasValidLicense } from "@dokploy/server/index";
|
||||
import type { statements } from "@dokploy/server/lib/access-control";
|
||||
import { validateRequest } from "@dokploy/server/lib/auth";
|
||||
import { checkPermission } from "@dokploy/server/services/permission";
|
||||
import type { OpenApiMeta } from "@dokploy/trpc-openapi";
|
||||
import type { OpenApiMeta as _OpenApiMeta } from "@dokploy/trpc-openapi";
|
||||
|
||||
// method and path are auto-generated by @dokploy/trpc-openapi, make them optional
|
||||
type OpenApiMeta = {
|
||||
openapi?: Partial<NonNullable<_OpenApiMeta["openapi"]>>;
|
||||
};
|
||||
import { initTRPC, TRPCError } from "@trpc/server";
|
||||
import type { CreateNextContextOptions } from "@trpc/server/adapters/next";
|
||||
import type { Session, User } from "better-auth";
|
||||
|
||||
Generated
+5
-5
@@ -150,8 +150,8 @@ importers:
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/server
|
||||
'@dokploy/trpc-openapi':
|
||||
specifier: 0.0.18
|
||||
version: 0.0.18(@trpc/server@11.10.0(typescript@5.9.3))(zod-openapi@5.4.6(zod@4.3.6))(zod@4.3.6)
|
||||
specifier: 0.0.19
|
||||
version: 0.0.19(@trpc/server@11.10.0(typescript@5.9.3))(zod-openapi@5.4.6(zod@4.3.6))(zod@4.3.6)
|
||||
'@faker-js/faker':
|
||||
specifier: ^8.4.1
|
||||
version: 8.4.1
|
||||
@@ -1312,8 +1312,8 @@ packages:
|
||||
'@codemirror/view@6.39.15':
|
||||
resolution: {integrity: sha512-aCWjgweIIXLBHh7bY6cACvXuyrZ0xGafjQ2VInjp4RM4gMfscK5uESiNdrH0pE+e1lZr2B4ONGsjchl2KsKZzg==}
|
||||
|
||||
'@dokploy/trpc-openapi@0.0.18':
|
||||
resolution: {integrity: sha512-CbppvUEe8eK1fiNGQL5AH8KIRRlHk5bGPUEIyc2VBZE0un4kfUs5DXKSKsMLDomoES5ZEdrjT4nKpwYvhDha0w==}
|
||||
'@dokploy/trpc-openapi@0.0.19':
|
||||
resolution: {integrity: sha512-pmajIu1tIU3yqqbYdRHKFbA8gP37gO7F61PL9AFNtoyhh6gVxALXQLDabtE7dobKAhNDmIj6vV1a2vyP1Zi7/w==}
|
||||
peerDependencies:
|
||||
'@trpc/server': ^11.1.0
|
||||
zod: ^4.3.6
|
||||
@@ -9006,7 +9006,7 @@ snapshots:
|
||||
style-mod: 4.1.3
|
||||
w3c-keyname: 2.2.8
|
||||
|
||||
'@dokploy/trpc-openapi@0.0.18(@trpc/server@11.10.0(typescript@5.9.3))(zod-openapi@5.4.6(zod@4.3.6))(zod@4.3.6)':
|
||||
'@dokploy/trpc-openapi@0.0.19(@trpc/server@11.10.0(typescript@5.9.3))(zod-openapi@5.4.6(zod@4.3.6))(zod@4.3.6)':
|
||||
dependencies:
|
||||
'@trpc/server': 11.10.0(typescript@5.9.3)
|
||||
co-body: 6.2.0
|
||||
|
||||
Reference in New Issue
Block a user