gitship-agent 0.0.2 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/server.d.ts +1 -0
- package/dist/server.js +86 -0
- package/dist/server.js.map +1 -0
- package/package.json +4 -1
- package/src/server.ts +0 -123
- package/tsconfig.json +0 -8
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import express from "express";
|
|
2
|
+
import crypto from "crypto";
|
|
3
|
+
import { getProjects, enqueueDeployment, isWebhookDeliveryProcessed, recordWebhookDelivery, } from "gitship-core";
|
|
4
|
+
const app = express();
|
|
5
|
+
const PORT = process.env.PORT || 3000;
|
|
6
|
+
// Middleware to capture raw body for HMAC signature verification
|
|
7
|
+
app.use(express.json({
|
|
8
|
+
verify: (req, _res, buf) => {
|
|
9
|
+
req.rawBody = buf;
|
|
10
|
+
},
|
|
11
|
+
}));
|
|
12
|
+
function verifyGitHubSignature(rawBody, signatureHeader, secret) {
|
|
13
|
+
if (!signatureHeader)
|
|
14
|
+
return false;
|
|
15
|
+
try {
|
|
16
|
+
const hmac = crypto.createHmac("sha256", secret);
|
|
17
|
+
hmac.update(rawBody);
|
|
18
|
+
const digest = "sha256=" + hmac.digest("hex");
|
|
19
|
+
return crypto.timingSafeEqual(Buffer.from(digest), Buffer.from(signatureHeader));
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
app.post("/webhook/github", async (req, res) => {
|
|
26
|
+
const event = req.headers["x-github-event"];
|
|
27
|
+
const signature = req.headers["x-hub-signature-256"];
|
|
28
|
+
const deliveryId = req.headers["x-github-delivery"];
|
|
29
|
+
if (!event || event !== "push") {
|
|
30
|
+
res.status(200).send("Ignored: Not a push event");
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
// Replay Protection
|
|
34
|
+
if (deliveryId) {
|
|
35
|
+
if (isWebhookDeliveryProcessed(deliveryId)) {
|
|
36
|
+
console.log(`[Webhook] Ignored duplicate delivery: ${deliveryId}`);
|
|
37
|
+
res.status(200).send("Ignored: Duplicate delivery");
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
recordWebhookDelivery(deliveryId);
|
|
41
|
+
}
|
|
42
|
+
const payload = req.body;
|
|
43
|
+
if (!payload || !payload.repository || !payload.ref) {
|
|
44
|
+
res.status(400).send("Bad Request: Missing payload details");
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const owner = payload.repository.owner.login;
|
|
48
|
+
const repo = payload.repository.name;
|
|
49
|
+
const branch = payload.ref.replace("refs/heads/", "");
|
|
50
|
+
// Find matching projects in the database
|
|
51
|
+
const projects = getProjects().filter((p) => p.owner.toLowerCase() === owner.toLowerCase() &&
|
|
52
|
+
p.repo.toLowerCase() === repo.toLowerCase() &&
|
|
53
|
+
p.branch.toLowerCase() === branch.toLowerCase());
|
|
54
|
+
if (projects.length === 0) {
|
|
55
|
+
console.log(`[Webhook] No matching active project found for ${owner}/${repo} branch ${branch}`);
|
|
56
|
+
res.status(200).send(`Ignored: No active project matching ${owner}/${repo}:${branch}`);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const rawBody = req.rawBody || Buffer.from(JSON.stringify(req.body));
|
|
60
|
+
// Process all matching projects (usually just 1, but we loop for safety)
|
|
61
|
+
let authorizedCount = 0;
|
|
62
|
+
for (const project of projects) {
|
|
63
|
+
if (!verifyGitHubSignature(rawBody, signature, project.webhook_secret)) {
|
|
64
|
+
console.warn(`[Webhook] Signature verification failed for project: ${project.name}`);
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
authorizedCount++;
|
|
68
|
+
const commitSha = payload.after !== "0000000000000000000000000000000000000000" ? payload.after : null;
|
|
69
|
+
const commitMessage = payload.head_commit?.message || "Webhook trigger";
|
|
70
|
+
const author = payload.head_commit?.author?.username || payload.pusher?.name || "github";
|
|
71
|
+
console.log(`[Webhook] Enqueuing deployment for project ${project.name} (commit: ${commitSha})`);
|
|
72
|
+
await enqueueDeployment(project.id, branch, commitSha, commitMessage, author);
|
|
73
|
+
}
|
|
74
|
+
if (authorizedCount === 0) {
|
|
75
|
+
res.status(401).send("Unauthorized: Signature verification failed");
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
res.status(202).send("Accepted: Deployment enqueued");
|
|
79
|
+
});
|
|
80
|
+
app.get("/health", (_req, res) => {
|
|
81
|
+
res.status(200).json({ status: "OK", time: new Date() });
|
|
82
|
+
});
|
|
83
|
+
app.listen(PORT, () => {
|
|
84
|
+
console.log(`DeployKit Server Agent listening on port ${PORT}`);
|
|
85
|
+
});
|
|
86
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/server.ts
DELETED
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
import express, { Request, Response } from "express";
|
|
2
|
-
import crypto from "crypto";
|
|
3
|
-
import {
|
|
4
|
-
getProjects,
|
|
5
|
-
enqueueDeployment,
|
|
6
|
-
isWebhookDeliveryProcessed,
|
|
7
|
-
recordWebhookDelivery,
|
|
8
|
-
} from "gitship-core";
|
|
9
|
-
|
|
10
|
-
const app = express();
|
|
11
|
-
const PORT = process.env.PORT || 3000;
|
|
12
|
-
|
|
13
|
-
// Middleware to capture raw body for HMAC signature verification
|
|
14
|
-
app.use(
|
|
15
|
-
express.json({
|
|
16
|
-
verify: (req: any, _res: any, buf: Buffer) => {
|
|
17
|
-
req.rawBody = buf;
|
|
18
|
-
},
|
|
19
|
-
})
|
|
20
|
-
);
|
|
21
|
-
|
|
22
|
-
function verifyGitHubSignature(
|
|
23
|
-
rawBody: Buffer,
|
|
24
|
-
signatureHeader: string,
|
|
25
|
-
secret: string
|
|
26
|
-
): boolean {
|
|
27
|
-
if (!signatureHeader) return false;
|
|
28
|
-
try {
|
|
29
|
-
const hmac = crypto.createHmac("sha256", secret);
|
|
30
|
-
hmac.update(rawBody);
|
|
31
|
-
const digest = "sha256=" + hmac.digest("hex");
|
|
32
|
-
return crypto.timingSafeEqual(Buffer.from(digest), Buffer.from(signatureHeader));
|
|
33
|
-
} catch {
|
|
34
|
-
return false;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
app.post("/webhook/github", async (req: Request, res: Response): Promise<void> => {
|
|
39
|
-
const event = req.headers["x-github-event"];
|
|
40
|
-
const signature = req.headers["x-hub-signature-256"] as string;
|
|
41
|
-
const deliveryId = req.headers["x-github-delivery"] as string;
|
|
42
|
-
|
|
43
|
-
if (!event || event !== "push") {
|
|
44
|
-
res.status(200).send("Ignored: Not a push event");
|
|
45
|
-
return;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// Replay Protection
|
|
49
|
-
if (deliveryId) {
|
|
50
|
-
if (isWebhookDeliveryProcessed(deliveryId)) {
|
|
51
|
-
console.log(`[Webhook] Ignored duplicate delivery: ${deliveryId}`);
|
|
52
|
-
res.status(200).send("Ignored: Duplicate delivery");
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
recordWebhookDelivery(deliveryId);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const payload = req.body;
|
|
59
|
-
if (!payload || !payload.repository || !payload.ref) {
|
|
60
|
-
res.status(400).send("Bad Request: Missing payload details");
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
const owner = payload.repository.owner.login;
|
|
65
|
-
const repo = payload.repository.name;
|
|
66
|
-
const branch = payload.ref.replace("refs/heads/", "");
|
|
67
|
-
|
|
68
|
-
// Find matching projects in the database
|
|
69
|
-
const projects = getProjects().filter(
|
|
70
|
-
(p) =>
|
|
71
|
-
p.owner.toLowerCase() === owner.toLowerCase() &&
|
|
72
|
-
p.repo.toLowerCase() === repo.toLowerCase() &&
|
|
73
|
-
p.branch.toLowerCase() === branch.toLowerCase()
|
|
74
|
-
);
|
|
75
|
-
|
|
76
|
-
if (projects.length === 0) {
|
|
77
|
-
console.log(`[Webhook] No matching active project found for ${owner}/${repo} branch ${branch}`);
|
|
78
|
-
res.status(200).send(`Ignored: No active project matching ${owner}/${repo}:${branch}`);
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
const rawBody = (req as any).rawBody || Buffer.from(JSON.stringify(req.body));
|
|
83
|
-
|
|
84
|
-
// Process all matching projects (usually just 1, but we loop for safety)
|
|
85
|
-
let authorizedCount = 0;
|
|
86
|
-
for (const project of projects) {
|
|
87
|
-
if (!verifyGitHubSignature(rawBody, signature, project.webhook_secret)) {
|
|
88
|
-
console.warn(`[Webhook] Signature verification failed for project: ${project.name}`);
|
|
89
|
-
continue;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
authorizedCount++;
|
|
93
|
-
|
|
94
|
-
const commitSha = payload.after !== "0000000000000000000000000000000000000000" ? payload.after : null;
|
|
95
|
-
const commitMessage = payload.head_commit?.message || "Webhook trigger";
|
|
96
|
-
const author = payload.head_commit?.author?.username || payload.pusher?.name || "github";
|
|
97
|
-
|
|
98
|
-
console.log(`[Webhook] Enqueuing deployment for project ${project.name} (commit: ${commitSha})`);
|
|
99
|
-
|
|
100
|
-
await enqueueDeployment(
|
|
101
|
-
project.id,
|
|
102
|
-
branch,
|
|
103
|
-
commitSha,
|
|
104
|
-
commitMessage,
|
|
105
|
-
author
|
|
106
|
-
);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
if (authorizedCount === 0) {
|
|
110
|
-
res.status(401).send("Unauthorized: Signature verification failed");
|
|
111
|
-
return;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
res.status(202).send("Accepted: Deployment enqueued");
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
app.get("/health", (_req, res) => {
|
|
118
|
-
res.status(200).json({ status: "OK", time: new Date() });
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
app.listen(PORT, () => {
|
|
122
|
-
console.log(`DeployKit Server Agent listening on port ${PORT}`);
|
|
123
|
-
});
|