GitFOSS
.ts
TypeScript
(application/typescript)
// std
import { existsSync } from "node:fs";
import { spawn } from "node:child_process";
// 1st-party
import type { ServiceMethodFactory } from "@ethicdevs/react-monolith";
// generated via script[generate:prisma]
import type { Repository } from "@prisma/client";
// app
import type { RepositoryFileContent } from "../../types";
import { Const } from "../../const";
import { Env } from "../../env";
// service
import type { RepositoryServiceDeps } from "./types";

const makeGetRepositoryFileContentBase64: ServiceMethodFactory<
  RepositoryServiceDeps,
  [Repository, string, string | undefined],
  Promise<null | RepositoryFileContent>
> = ({ request }) => {
  return async (repo, path, ref = Const.DEFAULT_HEAD_REF) => {
    if (path.endsWith("/")) {
      throw new Error("Could not retrieve file content for a folder.");
    }

    const parentOrg = await request.prisma.organization.findUnique({
      where: {
        id: repo.organizationId,
      },
    });

    if (parentOrg == null) {
      throw new Error(
        `Could not find the parent organization for project "${repo.slug}".`
      );
    }

    try {
      const repoPath = `${Env.GIT_REPOSITORIES_ROOT}/${parentOrg.slug}/${repo.slug}.git`;
      if (existsSync(repoPath) === false) {
        throw new Error(
          `Could not find a valid git repository at: ${repoPath}`
        );
      }

      const gitCatFileProcess = spawn(
        "git",
        ["cat-file", "-p", `${ref}:${path}`],
        {
          cwd: repoPath,
          env: {
            LANG: "C",
          },
        }
      );

      // pipe gitCatFileProcess stdout into base64 stdin
      const base64FileProcess = spawn("base64", [], {
        cwd: repoPath,
        stdio: [gitCatFileProcess.stdout, "pipe"],
        env: {
          LANG: "C",
        },
      });

      const gitCatFileResult = await new Promise<string>((resolve, reject) => {
        let buffer = [] as string[];
        base64FileProcess.stdout?.on("data", (data) => buffer.push(data));
        base64FileProcess.stderr?.on("data", (data) => {
          reject(new Error(Buffer.from(data).toString("utf-8")));
        });
        base64FileProcess.stdout?.on("close", () => {
          resolve(buffer.join(""));
        });
      });

      const { mimeType } =
        await request.codeAnalysisService.getLinguistFileInfos(
          path,
          gitCatFileResult
        );

      return {
        content: `data:${mimeType};base64,${gitCatFileResult}`,
        mimeType,
      };
    } catch (_) {
      console.error("err:", _);
      return null;
    }
  };
};

export default makeGetRepositoryFileContentBase64;