Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ CMS_CONTENT_DIR=
CMS_MEDIA_DIR=
CMS_PUBLIC_MEDIA_PATH=
CMS_ADAPTER=
CMS_PUBLISHER=
9 changes: 7 additions & 2 deletions apps/studio/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
"version": "0.0.0",
"type": "module",
"scripts": {
"predev": "pnpm --filter @sourcedraft/core --filter @sourcedraft/adapter-astro-mdx --filter @sourcedraft/adapter-markdown --filter @sourcedraft/github-publisher --filter @sourcedraft/config build",
"prebuild": "pnpm --filter @sourcedraft/core --filter @sourcedraft/adapter-astro-mdx --filter @sourcedraft/adapter-markdown --filter @sourcedraft/github-publisher --filter @sourcedraft/config build",
"predev": "pnpm --filter @sourcedraft/core --filter @sourcedraft/adapter-astro-mdx --filter @sourcedraft/adapter-markdown --filter @sourcedraft/adapter-nextjs-mdx --filter @sourcedraft/adapter-hugo-markdown --filter @sourcedraft/adapter-eleventy-jekyll-markdown --filter @sourcedraft/adapter-docusaurus-mdx --filter @sourcedraft/adapter-mkdocs-markdown --filter @sourcedraft/adapter-nuxt-content-markdown --filter @sourcedraft/adapters --filter @sourcedraft/publishers --filter @sourcedraft/github-publisher --filter @sourcedraft/config build",
"prebuild": "pnpm --filter @sourcedraft/core --filter @sourcedraft/adapter-astro-mdx --filter @sourcedraft/adapter-markdown --filter @sourcedraft/adapter-nextjs-mdx --filter @sourcedraft/adapter-hugo-markdown --filter @sourcedraft/adapter-eleventy-jekyll-markdown --filter @sourcedraft/adapter-docusaurus-mdx --filter @sourcedraft/adapter-mkdocs-markdown --filter @sourcedraft/adapter-nuxt-content-markdown --filter @sourcedraft/adapters --filter @sourcedraft/publishers --filter @sourcedraft/github-publisher --filter @sourcedraft/config build",
"dev": "concurrently -n web,api -c blue,gray \"vite\" \"tsx watch server/index.ts\"",
"dev:web": "vite",
"dev:server": "tsx watch server/index.ts",
Expand All @@ -23,8 +23,13 @@
"@fontsource/ibm-plex-sans": "^5.2.8",
"@fontsource/ibm-plex-serif": "^5.2.7",
"@sourcedraft/adapter-astro-mdx": "workspace:*",
"@sourcedraft/adapter-eleventy-jekyll-markdown": "workspace:*",
"@sourcedraft/adapter-hugo-markdown": "workspace:*",
"@sourcedraft/adapter-markdown": "workspace:*",
"@sourcedraft/adapter-nextjs-mdx": "workspace:*",
"@sourcedraft/adapters": "workspace:*",
"@sourcedraft/config": "workspace:*",
"@sourcedraft/publishers": "workspace:*",
"@sourcedraft/core": "workspace:*",
"@sourcedraft/github-publisher": "workspace:*",
"busboy": "^1.6.0",
Expand Down
73 changes: 65 additions & 8 deletions apps/studio/server/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,21 @@ import {
normalizePublicMediaPath,
} from "@sourcedraft/config";
import type { SourceDraftConfig } from "@sourcedraft/config";
import {
isAdapterId,
listAdapterIds,
supportedAdapterSummary,
type AdapterId,
} from "@sourcedraft/adapters";
import {
isPublisherId,
listPublisherIds,
supportedPublisherSummary,
type PublisherId,
} from "@sourcedraft/publishers";

export type SupportedAdapter = "astro-mdx" | "markdown";
export type SupportedAdapter = AdapterId;
export type SupportedPublisher = PublisherId;

export type PublishEnvConfig = {
token: string;
Expand All @@ -16,25 +29,34 @@ export type PublishEnvConfig = {
mediaDir: string;
publicMediaPath: string;
adapter: SupportedAdapter;
publisher: SupportedPublisher;
adapterOptions?: Record<string, unknown>;
publisherOptions?: Record<string, unknown>;
categories: string[];
};

export type PublishEnvResult =
| { ok: true; config: PublishEnvConfig }
| { ok: false; error: string };

const SUPPORTED_ADAPTERS = new Set<string>(["astro-mdx", "markdown"]);
export function loadProjectConfig(): SourceDraftConfig {
return loadSourceDraftConfig();
}

function resolveAdapter(rawAdapter: string): SupportedAdapter | null {
if (SUPPORTED_ADAPTERS.has(rawAdapter)) {
return rawAdapter as SupportedAdapter;
if (isAdapterId(rawAdapter)) {
return rawAdapter;
}

return null;
}

export function loadProjectConfig(): SourceDraftConfig {
return loadSourceDraftConfig();
function resolvePublisher(rawPublisher: string): SupportedPublisher | null {
if (isPublisherId(rawPublisher)) {
return rawPublisher;
}

return null;
}

function resolvePublicMediaPath(
Expand Down Expand Up @@ -66,7 +88,9 @@ export function loadPublishEnv(): PublishEnvResult {
const mediaDir = process.env.CMS_MEDIA_DIR?.trim() || project.mediaDir;
const publicMediaPath = resolvePublicMediaPath(mediaDir, project);
const rawAdapter = process.env.CMS_ADAPTER?.trim() || project.adapter;
const rawPublisher = process.env.CMS_PUBLISHER?.trim() || project.publisher;
const adapter = resolveAdapter(rawAdapter);
const publisher = resolvePublisher(rawPublisher);

if (!token) {
return { ok: false, error: "GITHUB_TOKEN is not configured." };
Expand All @@ -83,7 +107,14 @@ export function loadPublishEnv(): PublishEnvResult {
if (adapter === null) {
return {
ok: false,
error: `Unsupported adapter "${rawAdapter}". Supported adapters: astro-mdx, markdown.`,
error: `Unsupported adapter "${rawAdapter}". Supported adapters: ${supportedAdapterSummary()}.`,
};
}

if (publisher === null) {
return {
ok: false,
error: `Unsupported publisher "${rawPublisher}". Supported publishers: ${supportedPublisherSummary()}.`,
};
}

Expand All @@ -98,15 +129,26 @@ export function loadPublishEnv(): PublishEnvResult {
mediaDir,
publicMediaPath,
adapter,
publisher,
...(project.adapterOptions !== undefined
? { adapterOptions: project.adapterOptions }
: {}),
...(project.publisherOptions !== undefined
? { publisherOptions: project.publisherOptions }
: {}),
categories: project.categories,
},
};
}

export function loadPublicConfig(): Omit<PublishEnvConfig, "token"> {
export type PublicStudioConfig = Omit<PublishEnvConfig, "token">;

export function loadPublicConfig(): PublicStudioConfig {
const project = loadProjectConfig();
const rawAdapter = process.env.CMS_ADAPTER?.trim() || project.adapter;
const rawPublisher = process.env.CMS_PUBLISHER?.trim() || project.publisher;
const adapter = resolveAdapter(rawAdapter) ?? "astro-mdx";
const publisher = resolvePublisher(rawPublisher) ?? "github";
const mediaDir = process.env.CMS_MEDIA_DIR?.trim() || project.mediaDir;

return {
Expand All @@ -117,6 +159,21 @@ export function loadPublicConfig(): Omit<PublishEnvConfig, "token"> {
mediaDir,
publicMediaPath: resolvePublicMediaPath(mediaDir, project),
adapter,
publisher,
...(project.adapterOptions !== undefined
? { adapterOptions: project.adapterOptions }
: {}),
...(project.publisherOptions !== undefined
? { publisherOptions: project.publisherOptions }
: {}),
categories: project.categories,
};
}

export function listSupportedAdapters(): SupportedAdapter[] {
return listAdapterIds();
}

export function listSupportedPublishers(): SupportedPublisher[] {
return listPublisherIds();
}
1 change: 1 addition & 0 deletions apps/studio/server/demoPosts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export async function loadDemoPost(
safe.path,
parsed.frontmatter,
parsed.body,
env.adapter,
);
const validation = validateArticle(article);
if (!validation.valid) {
Expand Down
32 changes: 15 additions & 17 deletions apps/studio/server/demoPublish.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { getAstroMdxPath, toAstroMdx } from "@sourcedraft/adapter-astro-mdx";
import { getMarkdownPath, toMarkdown } from "@sourcedraft/adapter-markdown";
import {
getAdapterPostPath,
renderAdapterOutput,
} from "@sourcedraft/adapters";
import {
normalizeArticle,
validateArticle,
Expand All @@ -11,24 +13,20 @@ import { demoCommitSha, upsertDemoPost } from "./demoStore.js";
import { safePostPath } from "./postPaths.js";
import type { PublishRequestBody, PublishResponse } from "./publish.js";

function renderArticle(article: Article, adapter: PublishEnvConfig["adapter"]): string {
if (adapter === "markdown") {
return toMarkdown(article);
}

return toAstroMdx(article);
function renderArticle(article: Article, env: Omit<PublishEnvConfig, "token">): string {
return renderAdapterOutput(env.adapter, article, env.adapterOptions);
}

function defaultPostPath(
article: Article,
adapter: PublishEnvConfig["adapter"],
contentDir: string,
env: Omit<PublishEnvConfig, "token">,
): string {
if (adapter === "markdown") {
return getMarkdownPath(article, { contentDir });
}

return getAstroMdxPath(article, { contentDir });
return getAdapterPostPath(env.adapter, article, {
contentDir: env.contentDir,
...(env.adapterOptions !== undefined
? { adapterOptions: env.adapterOptions }
: {}),
});
}

export async function publishDemoArticle(
Expand Down Expand Up @@ -65,11 +63,11 @@ export async function publishDemoArticle(

path = safe.path;
} else {
path = defaultPostPath(article, env.adapter, env.contentDir);
path = defaultPostPath(article, env);
created = true;
}

const content = renderArticle(article, env.adapter);
const content = renderArticle(article, env);
const commitSha = demoCommitSha();

upsertDemoPost(path, content, {
Expand Down
3 changes: 3 additions & 0 deletions apps/studio/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@ app.get("/api/config", requireAuth, (req, res) => {
publicMediaPath: runtime.publicMediaPath,
defaultBranch: runtime.branch,
categories: runtime.categories,
...(runtime.adapterOptions !== undefined
? { adapterOptions: runtime.adapterOptions }
: {}),
githubOwner: demoMode ? "demo" : runtime.owner,
githubRepo: demoMode ? "sample-posts" : runtime.repo,
demoMode,
Expand Down
11 changes: 3 additions & 8 deletions apps/studio/server/listMedia.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { joinPublicMediaPath } from "@sourcedraft/config";
import { createGitHubPublisher } from "@sourcedraft/github-publisher";
import type { PublishEnvConfig } from "./config.js";
import { createPublisherFromEnv } from "./publisherRuntime.js";
import { filenameFromRepoPath, normalizeMediaDir, safeMediaPath } from "./mediaPaths.js";
import {
mediaKindFromExtension,
Expand Down Expand Up @@ -39,14 +39,9 @@ export async function listMedia(
};
}

const publisher = createGitHubPublisher({
token: env.token,
owner: env.owner,
repo: env.repo,
branch: env.branch,
});
const publisher = createPublisherFromEnv(env);

const listed = await publisher.listFiles({ path: mediaDir, contentDir: mediaDir });
const listed = await publisher.listPosts({ contentDir: mediaDir });
if (!listed.ok) {
return {
status: listed.status === 404 ? 404 : 502,
Expand Down
14 changes: 4 additions & 10 deletions apps/studio/server/media.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { randomBytes } from "node:crypto";
import type { Request } from "express";
import Busboy from "busboy";
import { joinPublicMediaPath } from "@sourcedraft/config";
import { createGitHubPublisher } from "@sourcedraft/github-publisher";
import type { PublishEnvConfig } from "./config.js";
import { createPublisherFromEnv } from "./publisherRuntime.js";
import { normalizeMediaDir } from "./mediaPaths.js";
import {
ALLOWED_MIME_TYPES,
Expand Down Expand Up @@ -203,18 +203,12 @@ export async function uploadMedia(
const repoPath = `${mediaDir}/${repoFilename}`;
const publicPath = joinPublicMediaPath(env.publicMediaPath, repoFilename);

const publisher = createGitHubPublisher({
token: env.token,
owner: env.owner,
repo: env.repo,
branch: env.branch,
});
const publisher = createPublisherFromEnv(env);

const result = await publisher.publishFile({
path: repoPath,
const result = await publisher.uploadMedia({
repoPath,
contentBase64: parsed.buffer.toString("base64"),
message: `Upload media: ${repoFilename}`,
purpose: "media",
});

if (!result.ok) {
Expand Down
Loading