Skip to content

Commit

Permalink
Merge pull request #10 from OnedocLabs/ffo-80-update-legacy-node-sdk-…
Browse files Browse the repository at this point in the history
…with-new-api

Updates js client to use fileforge api for generation
  • Loading branch information
pierredge authored May 20, 2024
2 parents 2e97bdc + d4a2e17 commit 6683e52
Show file tree
Hide file tree
Showing 6 changed files with 134 additions and 175 deletions.
22 changes: 22 additions & 0 deletions packages/client-js/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions packages/client-js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
},
"scripts": {
"build": "tsup",
"start": "tsx ./test/client.test.ts",
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
Expand All @@ -36,5 +37,8 @@
"esm"
],
"dts": true
},
"dependencies": {
"mime-types": "^2.1.35"
}
}
228 changes: 72 additions & 156 deletions packages/client-js/src/client.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { HtmlBuilder } from "./htmlBuilder";
export interface PathString {
import mime from "mime-types";

export interface Asset {
path: string;
content: string;
}
Expand All @@ -15,7 +17,7 @@ export interface DocumentInput {
html: string;
title?: string;
test?: boolean;
assets?: PathString[] | PathBuffer[];
assets?: Asset[] | PathBuffer[];
save?: boolean;
/**
* Number of seconds to cache the file in the CDN for.
Expand All @@ -40,67 +42,11 @@ type FileBody =
| URLSearchParams
| string;

//https://www.npmjs.com/package/@supabase/storage-js?activeTab=code
async function uploadToSignedUrl(
urlToFS: string,
path: string,
token: string,
fileBody: FileBody,
fileOptions?: any
) {
const url = new URL(urlToFS + `/object/upload/sign/${path}`);
url.searchParams.set("token", token);

try {
let body;
const options = { upsert: DEFAULT_FILE_OPTIONS.upsert, ...fileOptions };
const headers: Record<string, string> = {
...{ "x-upsert": String(options.upsert as boolean) },
};

if (typeof Blob !== "undefined" && fileBody instanceof Blob) {
body = new FormData();
body.append("cacheControl", options.cacheControl as string);
body.append("", fileBody);
} else if (
typeof FormData !== "undefined" &&
fileBody instanceof FormData
) {
body = fileBody;
body.append("cacheControl", options.cacheControl as string);
} else {
body = fileBody;
headers["cache-control"] = `max-age=${options.cacheControl}`;
headers["content-type"] = options.contentType as string;
}

const res = await fetch(url.toString(), {
method: "PUT",
body: body as BodyInit,
headers,
});

const data = await res.json();

if (res.ok) {
return {
data: { path: path, fullPath: data.Key },
error: null,
};
} else {
const error = data;
return { data: null, error };
}
} catch (error) {
throw error;
}
}

export class Onedoc {
private apiKey: string;
private endpoint: string;

constructor(apiKey: string, endpoint: string = "https://app.onedoclabs.com") {
constructor(apiKey: string, endpoint: string = "https://api.fileforge.com") {
this.apiKey = apiKey;
this.endpoint = endpoint;
}
Expand All @@ -109,117 +55,87 @@ export class Onedoc {
return `${this.endpoint}${path}`;
}

async render(document: DocumentInput) {
const assets = [
...(document.assets || []),
{
path: "/index.html",
content: document.html,
},
];

const test: boolean = document.test === undefined ? true : document.test;
const save: boolean = document.save === undefined ? false : document.save;
const expiresIn: number = document.expiresIn ? document.expiresIn : 1;

// Fetch the /api/docs/initiate API endpoint
const information = await fetch(this.buildUrl("/api/docs/initiate"), {
method: "POST",
headers: {
"x-api-Key": this.apiKey,
"Content-Type": "application/json", // Set Content-Type if you are sending JSON data
},
body: JSON.stringify({
assets,
}),
});
async render(document: DocumentInput): Promise<any> {
const assets: Asset[] = document.assets ?? [];
assets.push({ path: "/index.html", content: document.html });

if (information.status !== 200) {
return {
file: null,
error: ((await information.json()).error ||
"An unknown error has occurred") as string,
info: {
status: information.status,
},
};
}
const test: boolean = document.test ?? true;
const save: boolean = document.save ?? false;

// Show the response body
const response = await information.json();
const expires_in: number = document.expiresIn ?? 1;
const expires_at: Date = new Date(
Date.now() + expires_in * 24 * 60 * 60 * 1000
);
const expires_at_iso: string = expires_at.toISOString();

//--------------- UPLOAD FILES IN ASSETS ---------------
const formData = new FormData();

const signedURLs = response.signedUrls;
const optionsBlob = new Blob(
[
JSON.stringify({
test: test,
host: save,
expiresAt: expires_at_iso,
fileName: document.title ?? "document",
}),
],
{ type: "application/json" }
);

signedURLs.forEach(async (e) => {
const asset = document.assets?.find((item) => {
return item.path == e.path;
});
formData.append("options", optionsBlob);

if (asset?.content) {
await uploadToSignedUrl(e.signedUrl, e.path, e.token, asset.content);
assets.forEach((asset) => {
if (asset.path === "/index.html" && asset.content) {
// Assuming _HtmlBuilder exists and is imported
const htmlBuilder = new HtmlBuilder(document.title);
const styleSheets = assets
.filter((a) => a.path.endsWith(".css"))
.map((a) => a.path);

} else if (e.path == "/index.html") {
let htmlBuilder = new HtmlBuilder(document.title);
const html = htmlBuilder.build(document.html, styleSheets);

const styleSheets = document.assets
?.filter((asset) => asset.path.includes(".css"))
.map((asset) => asset.path);
const fileBlob = new Blob([html],{ type: "text/html" })

const html: string = htmlBuilder.build(
document.html,
styleSheets,
test
);
formData.append('files', fileBlob, "index.html");

await uploadToSignedUrl(e.signedUrl, e.path, e.token, html);
}
});

} else if (asset.content) {

const assetType = mime.lookup(asset.path) || "application/octet-stream";

const doc = await fetch(this.buildUrl("/api/docs/generate"), {
method: "POST",
headers: {
"x-api-key": this.apiKey,
"Content-Type": "application/json",
},
body: JSON.stringify({
...response,
title: document.title || "document",
test: test,
save: save,
expiresIn: expiresIn,
}),
});
const fileBlob = new Blob([asset.content],{ type: assetType })

// If status is not 200, it means there is an error
if (doc.status !== 200) {
return {
file: null,
link: null,
error: ((await doc.json()).error ||
"An unknown error has occurred") as string,
info: {},
};
}
formData.append('files', fileBlob, asset.path);

if (!save) {
return {
file: await doc.arrayBuffer(),
link: null,
error: null,
info: {},
};
}
{
return {
file: null,
link: (await doc.json()).url_link,
error: null,
info: {},
};
}
});

const response: Response = await fetch(
await this.buildUrl("/pdf/generate"),
{
method: "POST",
headers: {
"x-api-key": this.apiKey,
},
body: formData,
}
);

if (response.status === 201) {
if (!save) {
return {
file: await response.arrayBuffer(),
link: null,
error: null,
info: {},
};
} else {
const jsonResponse = await response.json();
return { file: null, link: jsonResponse.url, error: null, info: {} };
}
} else {
const error = await response.json();
return { file: null, link: null, error: error, info: {} };
}
}
}

20 changes: 1 addition & 19 deletions packages/client-js/src/htmlBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,11 @@ export class HtmlBuilder {
private end: string = `</body>
</html>`;

private watermark: string = `<div id="watermark-onedoc" > <a href="https://www.onedoclabs.com/" target="_blank"> <svg style="transform: rotate(90deg);display:inline;margin-top:30px;" width=75 xmlns="http://www.w3.org/2000/svg" x="0" y="0" enableBackground="new 0 0 46.15 9.31" version="1.1" viewBox="0 0 46.15 9.31" xmlSpace="preserve" fill="black" {...props} > <path d="M10 9.13V2.55h1.83v.91c.35-.62 1.13-1.09 2.07-1.09.71 0 1.32.24 1.81.71s.74 1.15.74 2.03v4.02h-1.88V5.6c0-.96-.5-1.5-1.28-1.5-.85 0-1.42.62-1.42 1.55v3.48H10zM23.84 6.48h-4.83c.23.83.83 1.24 1.79 1.24.74 0 1.43-.22 2.05-.64l.74 1.28c-.8.61-1.76.91-2.88.91-1.16 0-2.05-.34-2.67-1-.61-.66-.92-1.47-.92-2.45 0-1 .32-1.81.96-2.46.64-.66 1.48-.98 2.51-.98.97 0 1.76.3 2.39.89.62.59.94 1.39.94 2.41-.01.23-.04.5-.08.8zM19 5.13h3.09c-.18-.76-.73-1.22-1.51-1.22-.76 0-1.38.46-1.58 1.22zM29.43 0h1.88v9.13h-1.82v-.71c-.52.59-1.16.88-1.96.88-.92 0-1.69-.32-2.31-.98-.61-.66-.92-1.47-.92-2.47 0-.98.31-1.8.92-2.46.62-.66 1.39-1 2.31-1 .74 0 1.38.26 1.89.8V0zm-.39 4.6c-.31-.34-.71-.5-1.2-.5s-.89.17-1.21.5c-.31.34-.47.74-.47 1.22 0 .49.16.91.47 1.25.32.34.72.5 1.21.5s.89-.17 1.2-.5c.32-.34.48-.76.48-1.25 0-.47-.15-.88-.48-1.22zM33.03 8.31c-.66-.67-.98-1.5-.98-2.47s.32-1.8.98-2.46c.66-.67 1.51-1.01 2.55-1.01 1.04 0 1.91.34 2.57 1.01.66.66 1 1.49 1 2.46s-.34 1.8-1 2.47c-.66.66-1.52 1-2.57 1-1.04 0-1.89-.34-2.55-1zm3.74-3.68c-.32-.34-.72-.5-1.19-.5s-.86.17-1.19.5c-.32.32-.48.73-.48 1.2 0 .49.16.9.48 1.24.32.32.72.49 1.19.49s.86-.17 1.19-.49c.32-.34.49-.74.49-1.24 0-.47-.17-.88-.49-1.2zM40.5 8.31c-.65-.65-.97-1.47-.97-2.48s.32-1.83.98-2.47c.66-.65 1.5-.97 2.54-.97 1.36 0 2.55.67 3.09 1.87l-1.5.8c-.38-.62-.9-.94-1.56-.94-.49 0-.89.17-1.21.49-.32.32-.48.73-.48 1.21 0 .49.16.91.47 1.24.32.32.72.48 1.2.48.66 0 1.27-.38 1.55-.92l1.52.9c-.58 1.07-1.74 1.75-3.12 1.75-1.02 0-1.86-.32-2.51-.96zM9.26 4.7c0-1.29-.44-2.36-1.34-3.25C7.03.55 5.94.1 4.63.1c-1.3 0-2.39.45-3.29 1.35C.45 2.34 0 3.43 0 4.71c0 .37.05.72.12 1.05l4.3-3.39h2.22v6.46c.47-.22.9-.5 1.29-.88.89-.89 1.33-1.97 1.33-3.25z"></path> <path d="M1.49 8.09c.62.56 1.34.94 2.17 1.1v-2.8l-2.17 1.7z"></path> </svg> <a /> </div>`;

constructor(title?: string) {
this.title = title;
}

build(document: string, styleSheets?: string[], dev = true) {
build(document: string, styleSheets?: string[]) {
if (styleSheets) {
styleSheets.forEach((path) => {
this.start += `<link rel = "stylesheet" href=${path} />`;
Expand All @@ -31,22 +29,6 @@ export class HtmlBuilder {

this.middle += document;

if (dev == true) {
// adds watermak for dev renderings

this.start += `<style>
@page {
@left-middle {
content: flow(watermark);
}
}
#watermark-onedoc { -prince-flow: static(watermark, start) }
</style>`;

this.middle += this.watermark;
}
return this.start + this.middle + this.end;
}
}
32 changes: 32 additions & 0 deletions packages/client-js/test/client.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import {Onedoc} from "../src/client";
import fs from "fs";
import path from "path";
import process from "process";

const ONEDOC_API_KEY = process.env.ONEDOC;
(async () => {

const onedoc = new Onedoc(ONEDOC_API_KEY);

let doc = {
html: "<h1>Hello World</h1>",
title: "Hello",
test: true, // if true, produce a PDF in test mode with a Onedoc's watermark
save: false, // if true, host the document and provide a download link in the console and your Onedoc's dashboard
expiresIn: 30, // the number of day you want to host your document
assets: [
{
path: "./stylesheet.css",
content: fs.readFileSync(path.join(process.cwd(), "/test/stylesheet.css")).toString(),
},
],
};

const { file, link, error, info } = await onedoc.render(doc);

if (error) {
throw error;
}

fs.writeFileSync("./Test.pdf", Buffer.from(file));
})();
Loading

0 comments on commit 6683e52

Please sign in to comment.