Skip to content

Commit 6cbcaa9

Browse files
authored
feat(notebooks): SSR published notebooks in catchall page (#5468)
1 parent 99b5e88 commit 6cbcaa9

File tree

5 files changed

+88
-100
lines changed

5 files changed

+88
-100
lines changed

apps/frontend/app/[...catchall]/page.tsx

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1-
import { PlasmicComponent } from "@plasmicapp/loader-nextjs";
1+
import { ComponentMeta, PlasmicComponent } from "@plasmicapp/loader-nextjs";
22
import { notFound } from "next/navigation";
33
import { PLASMIC } from "@/plasmic-init";
44
import { PlasmicClientRootProvider } from "@/plasmic-init-client";
55
import type { Metadata, Viewport } from "next";
66
import { generateNotebookMetadata } from "@/lib/utils/og-metadata";
7+
import { logger } from "@/lib/logger";
8+
import { getPublishedNotebookByNames } from "@/lib/notebook/utils-server";
79

810
// Use revalidate if you want incremental static regeneration
911
// export const dynamic = "force-static";
@@ -29,13 +31,24 @@ export default async function PlasmicLoaderPage({
2931
}
3032

3133
const pageMeta = prefetchedData.entryCompMetas[0];
34+
35+
let pageProps = {};
36+
try {
37+
pageProps = await getPageProps(pageMeta);
38+
} catch (error) {
39+
logger.error("Error fetching page props:", error);
40+
}
41+
3242
return (
3343
<PlasmicClientRootProvider
3444
prefetchedData={prefetchedData}
3545
pageParams={pageMeta.params}
3646
pageQuery={searchParams}
3747
>
38-
<PlasmicComponent component={pageMeta.displayName} />
48+
<PlasmicComponent
49+
component={pageMeta.displayName}
50+
componentProps={pageProps}
51+
/>
3952
</PlasmicClientRootProvider>
4053
);
4154
}
@@ -95,3 +108,23 @@ export async function generateStaticParams() {
95108
};
96109
});
97110
}
111+
112+
async function getPageProps(
113+
pageMeta: ComponentMeta & { params?: Record<string, string> },
114+
): Promise<Record<string, any>> {
115+
const { params } = pageMeta;
116+
if (pageMeta.name === "OrganizationNotebookPage") {
117+
const { orgName, notebookName } = params || {};
118+
if (!orgName || !notebookName) {
119+
return {};
120+
}
121+
const publishedNotebook = await getPublishedNotebookByNames(
122+
orgName,
123+
notebookName,
124+
);
125+
return {
126+
publishedHtml: publishedNotebook?.html,
127+
};
128+
}
129+
return {};
130+
}

apps/frontend/app/api/v1/notebooks/publish/route.tsx

Lines changed: 1 addition & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { gzipSync, gunzipSync } from "zlib";
1+
import { gzipSync } from "zlib";
22
import { NextRequest, NextResponse } from "next/server";
33
import { MARIMO_URL } from "@/lib/config";
44
import { withPostHogTracking } from "@/lib/clients/posthog";
@@ -116,74 +116,6 @@ export const POST = withPostHogTracking(async (req: NextRequest) => {
116116
return NextResponse.json({});
117117
});
118118

119-
export const GET = withPostHogTracking(async (req: NextRequest) => {
120-
const searchParams = req.nextUrl.searchParams;
121-
const notebookName = searchParams.get("notebookName");
122-
const orgName = searchParams.get("orgName");
123-
124-
if (!notebookName || !orgName) {
125-
return NextResponse.json(
126-
{ error: "Missing notebookName or orgName" },
127-
{ status: 400 },
128-
);
129-
}
130-
131-
const supabaseAdmin = createAdminClient();
132-
const { data, error } = await supabaseAdmin
133-
.from("published_notebooks")
134-
.select(
135-
`
136-
*,
137-
notebooks!inner (
138-
notebook_name,
139-
organizations!inner (
140-
org_name
141-
)
142-
)
143-
`,
144-
)
145-
.eq("notebooks.notebook_name", notebookName)
146-
.eq("notebooks.organizations.org_name", orgName)
147-
.is("deleted_at", null)
148-
.is("notebooks.deleted_at", null)
149-
.single();
150-
151-
if (error) {
152-
logger.error(error);
153-
if (error.code === "PGRST116") {
154-
return NextResponse.json(
155-
{ error: "Published notebook not found" },
156-
{ status: 404 },
157-
);
158-
}
159-
return NextResponse.json(
160-
{ error: "Error fetching published notebook" },
161-
{ status: 500 },
162-
);
163-
}
164-
165-
const { notebooks: _, ...publishedNotebookData } = data;
166-
167-
const { data: blob, error: downloadError } = await supabaseAdmin.storage
168-
.from("published-notebooks")
169-
.download(data.data_path);
170-
171-
if (downloadError) {
172-
logger.error(downloadError);
173-
return NextResponse.json(
174-
{ error: "Error downloading published notebook" },
175-
{ status: 500 },
176-
);
177-
}
178-
179-
const html = gunzipSync(await blob.bytes()).toString();
180-
181-
return NextResponse.json({
182-
...publishedNotebookData,
183-
html: html.toString(),
184-
});
185-
});
186-
187119
export const DELETE = withPostHogTracking(async (req: NextRequest) => {
188120
const { notebookId } = await req.json();
189121
if (!notebookId || typeof notebookId !== "string") {

apps/frontend/components/dataprovider/oso-global-context.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,6 @@ const OsoGlobalActionNames: ExtractMethodNames<OsoAppClient>[] = _.sortBy([
5959
"deleteNotebookById",
6060
"publishNotebook",
6161
"unpublishNotebook",
62-
"getPublishedNotebookByNames",
6362
"getOrganizationCredits",
6463
"getOrganizationCreditTransactions",
6564
"getConnectors",

apps/frontend/lib/clients/oso-app/oso-app.ts

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ import type {
2222
DynamicConnectorsInsert,
2323
DynamicConnectorsRow,
2424
DynamicTableContextsRow,
25-
PublishedNotebooksRow,
2625
} from "@/lib/types/schema-types";
2726
import { NotebookKey } from "@/lib/types/db";
2827
import { CREDIT_PACKAGES } from "@/lib/clients/stripe";
@@ -894,33 +893,6 @@ class OsoAppClient {
894893
return true;
895894
}
896895

897-
async getPublishedNotebookByNames(
898-
args: Partial<{ notebookName: string; orgName: string }>,
899-
): Promise<(PublishedNotebooksRow & { html: string }) | null> {
900-
console.log("getPublishedNotebook: ", args);
901-
const notebookName = ensure(
902-
args.notebookName,
903-
"Missing notebookName argument",
904-
);
905-
const orgName = ensure(args.orgName, "Missing orgName argument");
906-
907-
const searchParams = new URLSearchParams({ orgName, notebookName });
908-
const response = await fetch(`/api/v1/notebooks/publish?${searchParams}`, {
909-
method: "GET",
910-
headers: {
911-
"Content-Type": "application/json",
912-
},
913-
});
914-
915-
const json = await response.json();
916-
917-
if (!response.ok) {
918-
throw new Error("Error fetching published notebook: " + json.error);
919-
}
920-
921-
return json;
922-
}
923-
924896
/**
925897
* Gets the current credit balance for an organization.
926898
* @param orgName - The unique organization name

apps/frontend/lib/notebook/utils-server.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import puppeteer, { Browser } from "puppeteer-core";
44
import chromium from "@sparticuz/chromium-min";
55
import { NODE_ENV, PUPPETEER_CHROMIUM_PATH } from "@/lib/config";
66
import { logger } from "@/lib/logger";
7+
import { createAdminClient } from "@/lib/supabase/admin";
8+
import { gunzipSync } from "zlib";
79

810
export async function tryGenerateNotebookHtml(url: string) {
911
const browser = await createBrowserInstance();
@@ -91,6 +93,56 @@ async function generateNotebookHtml(browser: Browser, url: string) {
9193
`;
9294
}
9395

96+
export async function getPublishedNotebookByNames(
97+
orgName: string,
98+
notebookName: string,
99+
) {
100+
const supabaseAdmin = createAdminClient();
101+
const { data, error } = await supabaseAdmin
102+
.from("published_notebooks")
103+
.select(
104+
`
105+
*,
106+
notebooks!inner (
107+
notebook_name,
108+
organizations!inner (
109+
org_name
110+
)
111+
)
112+
`,
113+
)
114+
.eq("notebooks.notebook_name", notebookName)
115+
.eq("notebooks.organizations.org_name", orgName)
116+
.is("deleted_at", null)
117+
.is("notebooks.deleted_at", null)
118+
.single();
119+
120+
if (error) {
121+
if (error.code !== "PGRST116") {
122+
logger.error(error);
123+
}
124+
return undefined;
125+
}
126+
127+
const { notebooks: _, ...publishedNotebookData } = data;
128+
129+
const { data: blob, error: downloadError } = await supabaseAdmin.storage
130+
.from("published-notebooks")
131+
.download(data.data_path);
132+
133+
if (downloadError) {
134+
logger.error(downloadError);
135+
return undefined;
136+
}
137+
138+
const html = gunzipSync(await blob.bytes()).toString();
139+
140+
return {
141+
...publishedNotebookData,
142+
html: html.toString(),
143+
};
144+
}
145+
94146
async function createBrowserInstance() {
95147
try {
96148
return await puppeteer.launch({

0 commit comments

Comments
 (0)