Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/src/packages/next/pages/api/v2/latex.ts
Views: 687
/*1Turn LaTeX .tex file contents into a pdf. This run in a CoCalc2project with a configurable timeout and command, so can involve3arbitrarily sophisticated processing.45Then the path .tex file is created, if content is specified. Next the command is run which should hopefully produce a pdf file.6Finally, the pdf file is read into our database (as a blob).7*/89import getAccountId from "lib/account/get-account";10import getOneProject from "@cocalc/server/projects/get-one";11import { getProject } from "@cocalc/server/projects/control";12import callProject from "@cocalc/server/projects/call";13import getParams from "lib/api/get-params";14import { path_split } from "@cocalc/util/misc";15import getCustomize from "@cocalc/database/settings/customize";16import isCollaborator from "@cocalc/server/projects/is-collaborator";17import { DEFAULT_LATEX_COMMAND } from "lib/api/latex";1819import { apiRoute, apiRouteOperation } from "lib/api";20import {21LatexInputSchema,22LatexOutputSchema,23} from "lib/api/schema/latex";242526async function handle(req, res) {27const account_id = await getAccountId(req);28const params = getParams(req);29try {30if (!account_id) {31throw Error("must be authenticated");32}33if (!params.path || !params.path.endsWith(".tex")) {34throw Error("path must be specified and end in .tex");35}36const { head: dir, tail: filename } = path_split(params.path);37if (params.only_read_pdf) {38if (params.project_id == null) {39throw Error("if only_read_pdf is set then project_id must also be set");40}41if (params.path.startsWith("/tmp")) {42throw Error(43"if only_read_pdf is set then path must not start with /tmp (otherwise the pdf would be removed)",44);45}46}4748let project_id;49if (params.project_id != null) {50project_id = params.project_id;51if (!(await isCollaborator({ project_id, account_id }))) {52throw Error("must be signed in as a collaborator on the project");53}54} else {55// don't need to check collaborator in this case:56project_id = (await getOneProject(account_id)).project_id;57}5859let result: any = undefined;60let compile: any = undefined;61let pdf: any = undefined;62let url: string | undefined = undefined;63try {64// ensure the project is running.65const project = getProject(project_id);66await project.start();6768if (!params.only_read_pdf) {69if (params.content != null) {70// write content to the project as the file path71await callProject({72account_id,73project_id,74mesg: {75event: "write_text_file_to_project",76path: params.path,77content: params.content,78},79});80}81compile = await callProject({82account_id,83project_id,84mesg: {85event: "project_exec",86timeout: params.timeout ?? 30,87path: dir,88command: params.command ?? `${DEFAULT_LATEX_COMMAND} ${filename}`,89},90});91}92// TODO: should we check for errors in compile before trying to read pdf?93const ttlSeconds = params.ttl ?? 3600;94try {95pdf = await callProject({96account_id,97project_id,98mesg: {99event: "read_file_from_project",100path: pdfFile(params.path),101ttlSeconds,102},103});104const { siteURL } = await getCustomize();105if (pdf != null) {106url = pdf.data_uuid107? siteURL + `/blobs/${pdfFile(params.path)}?uuid=${pdf.data_uuid}`108: undefined;109}110result = { compile, url, pdf };111} catch (err) {112result = { compile, error: err.message };113}114} finally {115if (params.path.startsWith("/tmp")) {116await callProject({117account_id,118project_id,119mesg: {120event: "project_exec",121path: "/tmp",122bash: true,123command: `rm ${rmGlob(params.path)}`,124},125});126}127}128res.json(result);129} catch (err) {130res.json({ error: err.message });131}132}133134function pdfFile(path: string): string {135return path.slice(0, path.length - 4) + ".pdf";136}137138function rmGlob(path: string): string {139return path.slice(0, path.length - 4) + ".*";140}141142export default apiRoute({143latex: apiRouteOperation({144method: "POST",145openApiOperation: {146tags: ["Utils"]147},148})149.input({150contentType: "application/json",151body: LatexInputSchema,152})153.outputs([154{155status: 200,156contentType: "application/json",157body: LatexOutputSchema,158},159])160.handler(handle),161});162163164