From 041ab25b7bf08882b85fe6562c426541cdcaf037 Mon Sep 17 00:00:00 2001 From: arkml Date: Mon, 15 Sep 2025 23:44:01 +0530 Subject: [PATCH] remove url from share and add auth (#260) --- .../app/actions/shared-workflow.actions.ts | 27 +++++++++---------- .../components/build-assistant-section.tsx | 5 ++-- .../projects/components/create-project.tsx | 13 +++++---- 3 files changed, 20 insertions(+), 25 deletions(-) diff --git a/apps/rowboat/app/actions/shared-workflow.actions.ts b/apps/rowboat/app/actions/shared-workflow.actions.ts index 94123ebc..f651d4a5 100644 --- a/apps/rowboat/app/actions/shared-workflow.actions.ts +++ b/apps/rowboat/app/actions/shared-workflow.actions.ts @@ -5,6 +5,7 @@ import { nanoid } from "nanoid"; import { Workflow } from "@/app/lib/types/workflow_types"; import { db } from "@/app/lib/mongodb"; import { SHARED_WORKFLOWS_COLLECTION } from "@/src/infrastructure/repositories/mongodb.shared-workflows.indexes"; +import { requireAuth } from "@/app/lib/auth"; const DEFAULT_TTL_SECONDS = 60 * 60 * 24; // 24 hours @@ -26,6 +27,8 @@ function validateWorkflowJson(obj: unknown) { export async function createSharedWorkflowFromJson(json: string): Promise<{ id: string; ttlSeconds: number; }> { + // Require an authenticated user (respects guest mode when auth is disabled) + await requireAuth(); const obj = JSON.parse(json); const workflow = validateWorkflowJson(obj); @@ -38,23 +41,18 @@ export async function createSharedWorkflowFromJson(json: string): Promise<{ id: return { id, ttlSeconds: DEFAULT_TTL_SECONDS }; } -export async function loadSharedWorkflow(idOrUrl: string): Promise> { - // If it's an http(s) URL, fetch JSON and validate - const isHttp = idOrUrl.startsWith('http://') || idOrUrl.startsWith('https://'); - if (isHttp) { - const resp = await fetch(idOrUrl, { cache: 'no-store' }); - if (!resp.ok) { - throw new Error(`Failed to fetch URL: ${resp.status} ${resp.statusText}`); - } - const text = await resp.text(); - const obj = JSON.parse(text); - return validateWorkflowJson(obj); - } +/** + * Load a shared workflow by ephemeral share id stored in MongoDB. + * Expected when the query param `shared` is present in the UI. + */ +export async function loadSharedWorkflow(id: string): Promise> { + // Ensure caller is authenticated (guest allowed when auth disabled) + await requireAuth(); - // Otherwise, look up by shared id in MongoDB + // Look up by shared id in MongoDB const coll = db.collection(SHARED_WORKFLOWS_COLLECTION); const doc = await coll.findOne( - { _id: idOrUrl }, + { _id: id }, { projection: { workflow: 1, expiresAt: 1 } } ); if (!doc) { @@ -65,4 +63,3 @@ export async function loadSharedWorkflow(idOrUrl: string): Promise { - if (sharedId || importUrl) { + if (sharedId) { try { setAutoCreateLoading(true); - const workflowObj = await loadSharedWorkflow(sharedId || importUrl!); + const workflowObj = await loadSharedWorkflow(sharedId); await createProjectFromJsonWithOptions({ workflowJson: JSON.stringify(workflowObj), router, diff --git a/apps/rowboat/app/projects/components/create-project.tsx b/apps/rowboat/app/projects/components/create-project.tsx index fb6b755c..c948b694 100644 --- a/apps/rowboat/app/projects/components/create-project.tsx +++ b/apps/rowboat/app/projects/components/create-project.tsx @@ -151,7 +151,6 @@ export function CreateProject({ defaultName, onOpenProjectPane, isProjectPaneOpe const urlPrompt = searchParams.get('prompt'); const urlTemplate = searchParams.get('template'); const sharedId = searchParams.get('shared'); - const importUrl = searchParams.get('importUrl'); // Add this effect to update name when defaultName changes useEffect(() => { @@ -168,13 +167,13 @@ export function CreateProject({ defaultName, onOpenProjectPane, isProjectPaneOpe // Add effect to handle URL parameters for auto-creation useEffect(() => { const handleAutoCreate = async () => { - // Auto-create from template/prompt, or import from shared/id/url - if ((urlPrompt || urlTemplate || sharedId || importUrl) && !importLoading && !autoCreateLoading) { + // Auto-create from template/prompt, or import from shared id + if ((urlPrompt || urlTemplate || sharedId) && !importLoading && !autoCreateLoading) { setAutoCreateLoading(true); try { - if (sharedId || importUrl) { - // Load workflow via server action (supports id or URL) - const workflowObj = await loadSharedWorkflow(sharedId || importUrl!); + if (sharedId) { + // Load workflow via server action (by id) + const workflowObj = await loadSharedWorkflow(sharedId); await createProjectFromJsonWithOptions({ workflowJson: JSON.stringify(workflowObj), router, @@ -203,7 +202,7 @@ export function CreateProject({ defaultName, onOpenProjectPane, isProjectPaneOpe }; handleAutoCreate(); - }, [urlPrompt, urlTemplate, sharedId, importUrl, importLoading, autoCreateLoading, router]); + }, [urlPrompt, urlTemplate, sharedId, importLoading, autoCreateLoading, router]); // Inject glow animation styles useEffect(() => {