Use history.replaceState ageVerified query and improve export-story script
This commit is contained in:
parent
21a77ed254
commit
fb30f1b416
16 changed files with 96 additions and 83 deletions
|
|
@ -1,10 +1,13 @@
|
|||
import { spawn, spawnSync } from "node:child_process";
|
||||
import { readdir, mkdir, mkdtemp, writeFile, readFile, copyFile } from "node:fs/promises";
|
||||
import { readdirSync, mkdirSync } from "node:fs";
|
||||
import { mkdtemp, writeFile, readFile, copyFile } from "node:fs/promises";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join, normalize } from "node:path";
|
||||
import { createInterface } from "node:readline";
|
||||
import { program } from "commander";
|
||||
import fetchRetryWrapper from "fetch-retry";
|
||||
import type { HealthcheckResponse } from "../src/pages/api/healthcheck";
|
||||
import type { ExportStoryResponse } from "../src/pages/api/export-story/[...slug]";
|
||||
|
||||
function getRTFStyles(rtfSource: string) {
|
||||
const matches = rtfSource.matchAll(
|
||||
|
|
@ -53,10 +56,10 @@ async function exportStory(slug: string, options: { outputDir: string }) {
|
|||
const outputDir = normalize(options.outputDir);
|
||||
let files: string[];
|
||||
try {
|
||||
files = await readdir(outputDir);
|
||||
files = readdirSync(outputDir);
|
||||
} catch {
|
||||
files = [];
|
||||
console.log(`Created directory at ${await mkdir(outputDir, { recursive: true })}`);
|
||||
console.log(`Created directory at ${mkdirSync(outputDir, { recursive: true })}`);
|
||||
}
|
||||
if (files.length > 0) {
|
||||
console.error(`ERROR: Directory ${outputDir} is not empty!`);
|
||||
|
|
@ -84,7 +87,7 @@ async function exportStory(slug: string, options: { outputDir: string }) {
|
|||
if (!response.ok) {
|
||||
throw new Error(response.statusText);
|
||||
}
|
||||
const healthcheck: { isAlive: boolean } = await response.json();
|
||||
const healthcheck: HealthcheckResponse = await response.json();
|
||||
if (healthcheck.isAlive !== true) {
|
||||
throw new Error(JSON.stringify(healthcheck));
|
||||
}
|
||||
|
|
@ -102,32 +105,41 @@ async function exportStory(slug: string, options: { outputDir: string }) {
|
|||
console.log("Getting data from Astro...");
|
||||
const response = await fetch(new URL(`/api/export-story/${slug}`, astroURL));
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to reach API (status code ${response.status})`);
|
||||
throw new Error(`Failed to reach export-story API (status code ${response.status})`);
|
||||
}
|
||||
const data: { story: string; description: Record<string, string>; thumbnail: string | null } =
|
||||
await response.json();
|
||||
const data: ExportStoryResponse = await response.json();
|
||||
// Process response fields in parallel
|
||||
await Promise.all(
|
||||
Object.entries(data.description).map(async ([filename, description]) => {
|
||||
return await writeFile(join(outputDir, filename), description);
|
||||
}),
|
||||
[
|
||||
// Story
|
||||
(async () => {
|
||||
storyText = data.story;
|
||||
await writeFile(join(outputDir, `${slug}.txt`), storyText);
|
||||
})(),
|
||||
// Descriptions
|
||||
Object.entries(data.description).map(
|
||||
async ([filename, description]) => await writeFile(join(outputDir, filename), description),
|
||||
),
|
||||
// Thumbnail
|
||||
(async () => {
|
||||
if (data.thumbnail) {
|
||||
if (data.thumbnail.startsWith("/@fs/")) {
|
||||
const thumbnailPath = data.thumbnail
|
||||
.replace(/^\/@fs/, "")
|
||||
.replace(/\?(&?[a-z][a-zA-Z0-9_-]+=[a-zA-Z0-9_-]*)*$/, "");
|
||||
await copyFile(thumbnailPath, join(outputDir, `thumbnail${thumbnailPath.match(/\.[^.]+$/)![0]}`));
|
||||
} else {
|
||||
const thumbnail = await fetchRetry(data.thumbnail, { retries: 2, retryDelay: 10000 });
|
||||
if (!thumbnail.ok) {
|
||||
throw new Error("Failed to get thumbnail");
|
||||
}
|
||||
const thumbnailExt = thumbnail.headers.get("Content-Type")!.startsWith("image/png") ? "png" : "jpg";
|
||||
await writeFile(join(outputDir, `thumbnail.${thumbnailExt}`), Buffer.from(await thumbnail.arrayBuffer()));
|
||||
}
|
||||
}
|
||||
})(),
|
||||
].flat(),
|
||||
);
|
||||
if (data.thumbnail) {
|
||||
if (data.thumbnail.startsWith("/@fs/")) {
|
||||
const thumbnailPath = data.thumbnail
|
||||
.replace(/^\/@fs/, "")
|
||||
.replace(/\?(&?[a-z][a-zA-Z0-9_-]+=[a-zA-Z0-9_-]*)*$/, "");
|
||||
await copyFile(thumbnailPath, join(outputDir, `thumbnail${thumbnailPath.match(/\.[^.]+$/)![0]}`));
|
||||
} else {
|
||||
const thumbnail = await fetch(data.thumbnail);
|
||||
if (!thumbnail.ok) {
|
||||
throw new Error("Failed to get thumbnail");
|
||||
}
|
||||
const thumbnailExt = thumbnail.headers.get("Content-Type")!.startsWith("image/png") ? "png" : "jpg";
|
||||
await writeFile(join(outputDir, `thumbnail.${thumbnailExt}`), Buffer.from(await thumbnail.arrayBuffer()));
|
||||
}
|
||||
}
|
||||
storyText = data.story;
|
||||
writeFile(join(outputDir, `${slug}.txt`), storyText);
|
||||
} finally {
|
||||
if (devServerProcess) {
|
||||
console.log("Shutting down the Astro development server...");
|
||||
|
|
@ -139,18 +151,29 @@ async function exportStory(slug: string, options: { outputDir: string }) {
|
|||
|
||||
/* Parse story into output formats */
|
||||
console.log("Parsing story into output formats...");
|
||||
await writeFile(join(outputDir, `${slug}.md`), storyText.replaceAll(/=(?==)/g, "= ").replaceAll("*", "\\*"));
|
||||
const tempDir = await mkdtemp(join(tmpdir(), "export-story-"));
|
||||
await writeFile(join(tempDir, "temp.txt"), storyText.replaceAll(/\n\n+/g, "\n"));
|
||||
spawnSync("libreoffice", ["--convert-to", "rtf:Rich Text Format", "--outdir", tempDir, join(tempDir, "temp.txt")], {
|
||||
stdio: "ignore",
|
||||
});
|
||||
const rtfText = await readFile(join(tempDir, "temp.rtf"), "utf-8");
|
||||
const rtfStyles = getRTFStyles(rtfText);
|
||||
await writeFile(
|
||||
join(outputDir, `${slug}.rtf`),
|
||||
rtfText.replaceAll(rtfStyles["Preformatted Text"], rtfStyles["Normal"]),
|
||||
);
|
||||
// Process output files in parallel
|
||||
await Promise.all([
|
||||
// ${slug}.md
|
||||
writeFile(join(outputDir, `${slug}.md`), storyText.replaceAll(/=(?==)/g, "= ").replaceAll("*", "\\*")),
|
||||
// ${slug}.rtf
|
||||
(async () => {
|
||||
const tempDir = await mkdtemp(join(tmpdir(), "export-story-"));
|
||||
await writeFile(join(tempDir, "temp.txt"), storyText.replaceAll(/\n\n+/g, "\n"));
|
||||
spawnSync(
|
||||
"libreoffice",
|
||||
["--convert-to", "rtf:Rich Text Format", "--outdir", tempDir, join(tempDir, "temp.txt")],
|
||||
{
|
||||
stdio: "ignore",
|
||||
},
|
||||
);
|
||||
const rtfText = await readFile(join(tempDir, "temp.rtf"), "utf-8");
|
||||
const rtfStyles = getRTFStyles(rtfText);
|
||||
await writeFile(
|
||||
join(outputDir, `${slug}.rtf`),
|
||||
rtfText.replaceAll(rtfStyles["Preformatted Text"], rtfStyles["Normal"]),
|
||||
);
|
||||
})(),
|
||||
]);
|
||||
console.log("Success!");
|
||||
process.exit(0);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue