feat: Enhance deployment logic for multiple Git providers

- Added support for handling commit normalization across GitHub, GitLab, and Gitea in the deployment API.
- Implemented a new utility function to determine the provider based on request headers.
- Improved deployment path validation to ensure consistency across different source types.
- Cleaned up the code by removing redundant checks and enhancing readability.
This commit is contained in:
Mauricio Siu 2025-03-23 03:55:11 -06:00
parent a067abd3e4
commit 95f79f2afb
5 changed files with 122 additions and 30 deletions

View File

@ -84,6 +84,33 @@ export default async function handler(
res.status(301).json({ message: "Branch Not Match" }); res.status(301).json({ message: "Branch Not Match" });
return; return;
} }
const provider = getProviderByHeader(req.headers);
let normalizedCommits: string[] = [];
if (provider === "github") {
normalizedCommits = req.body?.commits?.flatMap(
(commit: any) => commit.modified,
);
} else if (provider === "gitlab") {
normalizedCommits = req.body?.commits?.flatMap(
(commit: any) => commit.modified,
);
} else if (provider === "gitea") {
normalizedCommits = req.body?.commits?.flatMap(
(commit: any) => commit.modified,
);
}
const shouldDeployPaths = shouldDeploy(
application.watchPaths,
normalizedCommits,
);
if (!shouldDeployPaths) {
res.status(301).json({ message: "Watch Paths Not Match" });
return;
}
} else if (sourceType === "gitlab") { } else if (sourceType === "gitlab") {
const branchName = extractBranchName(req.headers, req.body); const branchName = extractBranchName(req.headers, req.body);
@ -295,6 +322,26 @@ export const extractBranchName = (headers: any, body: any) => {
return null; return null;
}; };
export const getProviderByHeader = (headers: any) => {
if (headers["x-github-event"]) {
return "github";
}
if (headers["x-gitea-event"]) {
return "gitea";
}
if (headers["x-gitlab-event"]) {
return "gitlab";
}
if (headers["x-event-key"]?.includes("repo:push")) {
return "bitbucket";
}
return null;
};
export const extractCommitedPaths = async ( export const extractCommitedPaths = async (
body: any, body: any,
bitbucketUsername: string | null, bitbucketUsername: string | null,

View File

@ -11,6 +11,7 @@ import {
extractCommitMessage, extractCommitMessage,
extractCommitedPaths, extractCommitedPaths,
extractHash, extractHash,
getProviderByHeader,
} from "../[refreshToken]"; } from "../[refreshToken]";
export default async function handler( export default async function handler(
@ -91,12 +92,6 @@ export default async function handler(
res.status(301).json({ message: "Branch Not Match" }); res.status(301).json({ message: "Branch Not Match" });
return; return;
} }
} else if (sourceType === "git") {
const branchName = extractBranchName(req.headers, req.body);
if (!branchName || branchName !== composeResult.customGitBranch) {
res.status(301).json({ message: "Branch Not Match" });
return;
}
const commitedPaths = await extractCommitedPaths( const commitedPaths = await extractCommitedPaths(
req.body, req.body,
@ -104,6 +99,7 @@ export default async function handler(
composeResult.bitbucket?.appPassword || "", composeResult.bitbucket?.appPassword || "",
composeResult.bitbucketRepository || "", composeResult.bitbucketRepository || "",
); );
const shouldDeployPaths = shouldDeploy( const shouldDeployPaths = shouldDeploy(
composeResult.watchPaths, composeResult.watchPaths,
commitedPaths, commitedPaths,
@ -113,6 +109,59 @@ export default async function handler(
res.status(301).json({ message: "Watch Paths Not Match" }); res.status(301).json({ message: "Watch Paths Not Match" });
return; return;
} }
} else if (sourceType === "git") {
const branchName = extractBranchName(req.headers, req.body);
if (!branchName || branchName !== composeResult.customGitBranch) {
res.status(301).json({ message: "Branch Not Match" });
return;
}
const provider = getProviderByHeader(req.headers);
let normalizedCommits: string[] = [];
if (provider === "github") {
normalizedCommits = req.body?.commits?.flatMap(
(commit: any) => commit.modified,
);
} else if (provider === "gitlab") {
normalizedCommits = req.body?.commits?.flatMap(
(commit: any) => commit.modified,
);
} else if (provider === "gitea") {
normalizedCommits = req.body?.commits?.flatMap(
(commit: any) => commit.modified,
);
}
const shouldDeployPaths = shouldDeploy(
composeResult.watchPaths,
normalizedCommits,
);
if (!shouldDeployPaths) {
res.status(301).json({ message: "Watch Paths Not Match" });
return;
}
} else if (sourceType === "gitea") {
const branchName = extractBranchName(req.headers, req.body);
const normalizedCommits = req.body?.commits?.flatMap(
(commit: any) => commit.modified,
);
const shouldDeployPaths = shouldDeploy(
composeResult.watchPaths,
normalizedCommits,
);
if (!shouldDeployPaths) {
res.status(301).json({ message: "Watch Paths Not Match" });
return;
}
if (!branchName || branchName !== composeResult.giteaBranch) {
res.status(301).json({ message: "Branch Not Match" });
return;
}
} }
try { try {

View File

@ -73,7 +73,7 @@ export default async function handler(
: null; : null;
try { try {
const updatedGitea = await updateGitea(gitea.giteaId, { await updateGitea(gitea.giteaId, {
accessToken: result.access_token, accessToken: result.access_token,
refreshToken: result.refresh_token, refreshToken: result.refresh_token,
expiresAt, expiresAt,
@ -82,7 +82,6 @@ export default async function handler(
: {}), : {}),
}); });
console.log("Gitea provider updated successfully:", updatedGitea);
return res.redirect( return res.redirect(
307, 307,
"/dashboard/settings/git-providers?connected=true", "/dashboard/settings/git-providers?connected=true",

View File

@ -1,7 +1,6 @@
import { findGiteaById } from "@dokploy/server"; import { findGiteaById } from "@dokploy/server";
import type { NextApiResponse } from "next"; import type { NextApiResponse } from "next";
// Shared Gitea interface
export interface Gitea { export interface Gitea {
giteaId: string; giteaId: string;
gitProviderId: string; gitProviderId: string;
@ -22,7 +21,6 @@ export interface Gitea {
}; };
} }
// Shared function to find Gitea by ID
export const findGitea = async (giteaId: string): Promise<Gitea | null> => { export const findGitea = async (giteaId: string): Promise<Gitea | null> => {
try { try {
const gitea = await findGiteaById(giteaId); const gitea = await findGiteaById(giteaId);
@ -33,7 +31,6 @@ export const findGitea = async (giteaId: string): Promise<Gitea | null> => {
} }
}; };
// Helper for redirecting with error message
export const redirectWithError = (res: NextApiResponse, error: string) => { export const redirectWithError = (res: NextApiResponse, error: string) => {
return res.redirect( return res.redirect(
307, 307,

View File

@ -32,26 +32,26 @@ const { handler, api } = betterAuth({
clientSecret: process.env.GOOGLE_CLIENT_SECRET as string, clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,
}, },
}, },
...(!IS_CLOUD && { // ...(!IS_CLOUD && {
async trustedOrigins() { // async trustedOrigins() {
const admin = await db.query.member.findFirst({ // const admin = await db.query.member.findFirst({
where: eq(schema.member.role, "owner"), // where: eq(schema.member.role, "owner"),
with: { // with: {
user: true, // user: true,
}, // },
}); // });
if (admin) { // if (admin) {
return [ // return [
...(admin.user.serverIp // ...(admin.user.serverIp
? [`http://${admin.user.serverIp}:3000`] // ? [`http://${admin.user.serverIp}:3000`]
: []), // : []),
...(admin.user.host ? [`https://${admin.user.host}`] : []), // ...(admin.user.host ? [`https://${admin.user.host}`] : []),
]; // ];
} // }
return []; // return [];
}, // },
}), // }),
emailVerification: { emailVerification: {
sendOnSignUp: true, sendOnSignUp: true,
autoSignInAfterVerification: true, autoSignInAfterVerification: true,