diff --git a/backend/src/routes/functions.ts b/backend/src/routes/functions.ts index d2d57ff..47943d8 100644 --- a/backend/src/routes/functions.ts +++ b/backend/src/routes/functions.ts @@ -2,6 +2,10 @@ import { FastifyInstance } from "fastify"; import {createInvoicePDF, createTimeSheetPDF} from "../utils/pdf"; import {encodeBase64ToNiimbot, generateLabel, useNextNumberRangeNumber} from "../utils/functions"; import { GetObjectCommand } from "@aws-sdk/client-s3"; +import { execFile } from "node:child_process"; +import { existsSync } from "node:fs"; +import path from "node:path"; +import { promisify } from "node:util"; import dayjs from "dayjs"; //import { ready as zplReady } from 'zpl-renderer-js' //import { renderZPL } from "zpl-image"; @@ -28,6 +32,31 @@ dayjs.extend(isSameOrBefore) dayjs.extend(duration) dayjs.extend(timezone) +const execFileAsync = promisify(execFile) + +function resolveGitRoot() { + const searchRoots = [ + process.cwd(), + path.resolve(process.cwd(), ".."), + path.resolve(__dirname, "../../.."), + path.resolve(__dirname, "../../../.."), + ] + + for (const startDir of searchRoots) { + let currentDir = startDir + + while (currentDir && currentDir !== path.dirname(currentDir)) { + if (existsSync(path.join(currentDir, ".git"))) { + return currentDir + } + + currentDir = path.dirname(currentDir) + } + } + + return null +} + export default async function functionRoutes(server: FastifyInstance) { const streamToBuffer = async (stream: any): Promise => new Promise((resolve, reject) => { @@ -162,6 +191,55 @@ export default async function functionRoutes(server: FastifyInstance) { } }) + server.get('/functions/changelog', async (req, reply) => { + const { limit } = req.query as { limit?: string | number } + const parsedLimit = Number(limit) + const safeLimit = Number.isFinite(parsedLimit) + ? Math.min(Math.max(parsedLimit, 1), 50) + : 15 + + const gitRoot = resolveGitRoot() + + if (!gitRoot) { + return reply.code(500).send({ error: 'Git repository not found' }) + } + + try { + const { stdout } = await execFileAsync('git', [ + '-C', + gitRoot, + 'log', + `--max-count=${safeLimit}`, + '--date=iso-strict', + '--pretty=format:%H%x1f%h%x1f%s%x1f%an%x1f%aI%x1e' + ]) + + const entries = stdout + .split('\x1e') + .map(entry => entry.trim()) + .filter(Boolean) + .map(entry => { + const [hash, shortHash, subject, authorName, committedAt] = entry.split('\x1f') + + return { + hash, + shortHash, + subject, + authorName, + committedAt + } + }) + + return reply.send({ + repositoryRoot: gitRoot, + entries + }) + } catch (err) { + req.log.error(err) + return reply.code(500).send({ error: 'Failed to load changelog' }) + } + }) + server.post('/functions/serial/start', async (req, reply) => { console.log(req.body) const {executionDate,templateIds,tenantId} = req.body as {executionDate:string,templateIds:Number[],tenantId:Number} diff --git a/frontend/components/HelpSlideover.vue b/frontend/components/HelpSlideover.vue index 5ba08f8..d0fcc4f 100644 --- a/frontend/components/HelpSlideover.vue +++ b/frontend/components/HelpSlideover.vue @@ -1,6 +1,9 @@