diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..5cb7aee --- /dev/null +++ b/Dockerfile @@ -0,0 +1,10 @@ +FROM node:alpine-22 + +RUN apk add --no-cache git +WORKDIR /app +COPY package*.json ./ +RUN npm ci --omit-dev +COPY . . +RUN npm run build +EXPOSE 3000 +CMD ["node", "--watch", "server.js"] \ No newline at end of file diff --git a/package.json b/package.json index 6418f70..80b7a96 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "remark-prism": "^1.3.6" }, "devDependencies": { + "@types/node": "^22.13.1", "@types/remark-prism": "^1.3.7" } } diff --git a/server.js b/server.js index f16aa5b..260c758 100644 --- a/server.js +++ b/server.js @@ -1,20 +1,11 @@ import Fastify from "fastify"; import fastifyStatic from "@fastify/static"; -import { exec } from "child_process"; -import { promisify } from "util"; -import path from "path"; -import crypto from "crypto"; +import { exec } from "node:child_process"; +import { promisify } from "node:util"; +import path from "node:path"; +import crypto from "node:crypto"; -const verifySignature = (payload, signature, secret) => { - const computedSignature = crypto - .createHmac("sha256", secret) - .update(JSON.stringify(payload)) - .digest("hex"); - return crypto.timingSafeEqual( - Buffer.from(signature), - Buffer.from(computedSignature) - ); -}; +const { WEBHOOK_SECRET } = process.env; const execAsync = promisify(exec); const fastify = Fastify({ logger: true }); @@ -24,27 +15,45 @@ fastify.register(fastifyStatic, { prefix: "/", }); -fastify.post("/webhook", async (request, reply) => { - const signature = request.headers["strapi-signature"]; +const verifySignature = (payload, signature) => { + const hmac = crypto.createHmac("sha256", WEBHOOK_SECRET); + const digest = "sha256=" + hmac.update(payload).digest("hex"); + return crypto.timingSafeEqual(Buffer.from(digest), Buffer.from(signature)); +}; - if (process.env.WEBHOOK_SECRET) { - const isValid = verifySignature( - request.body, - signature, - process.env.WEBHOOK_SECRET - ); - if (!isValid) { - return reply.code(401).send({ error: "Invalid signature" }); +const updateRepo = async (repoUrl) => { + try { + const { code } = await execAsync("git rev-parse --git-dir") + .then(() => ({ code: 0 })) + .catch((error) => ({ code: error.code })); + + if (code !== 0) { + await execAsync("git init"); + await execAsync(`git remote add origin ${repoUrl}`); } + + await execAsync("git fetch origin"); + await execAsync("git reset --hard origin/main"); + await execAsync("npm run build"); + } catch (error) { + console.error("Failed to update repository:", error); + throw error; + } +}; + +fastify.post("/webhook/rebuild", async (request, reply) => { + const signature = request.headers["x-gitea-signature"]; + + if (!signature || !verifySignature(JSON.stringify(request.body), signature)) { + return reply.status(401).send("Invalid signature"); } try { - const { stdout } = await execAsync("npm run build"); - fastify.log.info(`Build output: ${stdout}`); - return { status: "success", message: "Build completed" }; + const repoUrl = request.body.repository.clone_url; + await updateRepo(repoUrl); + reply.status(200).send("Updated successfully"); } catch (error) { - fastify.log.error(error); - return reply.code(500).send({ error: "Build failed" }); + reply.status(500).send("Update failed"); } }); diff --git a/yarn.lock b/yarn.lock index 2d6e0cf..d1ab286 100644 --- a/yarn.lock +++ b/yarn.lock @@ -660,6 +660,13 @@ dependencies: "@types/unist" "*" +"@types/node@^22.13.1": + version "22.13.1" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.13.1.tgz#a2a3fefbdeb7ba6b89f40371842162fac0934f33" + integrity sha512-jK8uzQlrvXqEU91UxiK5J7pKHyzgnI1Qnl0QDHIgVGuolJhRb9EEl28Cj9b3rGR8B2lhFCtvIm5os8lFnO/1Ew== + dependencies: + undici-types "~6.20.0" + "@types/remark-prism@^1.3.7": version "1.3.7" resolved "https://registry.yarnpkg.com/@types/remark-prism/-/remark-prism-1.3.7.tgz#997486629798f9daa6cf2bfea4943d7d9763d116" @@ -3591,6 +3598,11 @@ uncrypto@^0.1.3: resolved "https://registry.yarnpkg.com/uncrypto/-/uncrypto-0.1.3.tgz#e1288d609226f2d02d8d69ee861fa20d8348ef2b" integrity sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q== +undici-types@~6.20.0: + version "6.20.0" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.20.0.tgz#8171bf22c1f588d1554d55bf204bc624af388433" + integrity sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg== + unenv@^1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/unenv/-/unenv-1.10.0.tgz#c3394a6c6e4cfe68d699f87af456fe3f0db39571"