-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Security review, other small improvements / fixes #2
Changes from 14 commits
2c70594
5bd95e2
5097668
b098da8
7973622
9dbe16b
e459a82
9553bda
8cb819e
fd16621
30b3012
bfe404d
1a06f82
a197dac
12abaa1
cd0d5a7
d019bde
5f2abd9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
{ | ||
"root": true, | ||
"extends": "next/core-web-vitals" | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
strict-peer-dependencies=false | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This solves the peer dependency issue with |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
"use client" | ||
|
||
import { useState, useEffect } from "react" | ||
import { useState, useEffect, useCallback } from "react" | ||
import { useAuth } from "@clerk/nextjs" | ||
import { useRouter } from "next/navigation" | ||
import { Button } from "@/components/ui/button" | ||
|
@@ -33,17 +33,11 @@ export default function AccountsPage() { | |
const router = useRouter() | ||
const debugMode = process.env.NEXT_PUBLIC_DEBUG_MODE === 'true' | ||
|
||
const fetchAccounts = async () => { | ||
const fetchAccounts = useCallback(async () => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Next convention |
||
setLoading(true) | ||
try { | ||
// First check if our environment variables are correctly configured | ||
const envCheck = await fetch("/api/check-env") | ||
const envData = await envCheck.json() | ||
|
||
// Only log in debug mode | ||
if (debugMode) { | ||
console.log("Environment check:", envData) | ||
|
||
// Log current user information from Clerk's client-side hooks | ||
console.log("Current user from Clerk client:", { | ||
userId, | ||
|
@@ -99,12 +93,15 @@ export default function AccountsPage() { | |
} finally { | ||
setLoading(false) | ||
} | ||
} | ||
}, [userId, debugMode, isLoaded, setLoading, setAccounts]) | ||
|
||
const deleteAccount = async (accountId: string) => { | ||
setDeletingId(accountId) | ||
try { | ||
const response = await fetch(`/api/accounts?id=${accountId}`, { | ||
// Import the enhanced fetch with CSRF protection | ||
const { fetchWithCSRF } = await import('@/lib/fetch-with-csrf') | ||
|
||
const response = await fetchWithCSRF(`/api/accounts?id=${accountId}`, { | ||
method: "DELETE", | ||
}) | ||
|
||
|
@@ -138,7 +135,7 @@ export default function AccountsPage() { | |
// Only fetch accounts if user is authenticated | ||
fetchAccounts() | ||
} | ||
}, [isLoaded, userId]) | ||
}, [isLoaded, userId, fetchAccounts]) | ||
|
||
// Show loading state while clerk auth is loading | ||
if (!isLoaded) { | ||
|
@@ -171,23 +168,12 @@ export default function AccountsPage() { | |
// Helper function to test Pipedream API directly - only shown in debug mode | ||
const testPipedreamApi = async () => { | ||
try { | ||
// First test connection through the check-env endpoint | ||
const response = await fetch(`/api/check-env?includePipedreamTest=true`) | ||
const data = await response.json() | ||
|
||
if (debugMode) { | ||
console.log("Pipedream API test result:", data) | ||
|
||
// Log current user info from client side | ||
console.log("Current user info from client:", { | ||
clerkUserId: userId, | ||
externalUserId: data.pipedream?.externalUserId ? "Set in ENV" : "Using Clerk ID" | ||
}) | ||
|
||
// Then try the accounts endpoint explicitly | ||
// Then try the accounts endpoint directly | ||
console.log("Testing accounts endpoint directly...") | ||
} | ||
|
||
// Use existing fetch without CSRF since this is a GET request | ||
const accountsResponse = await fetch("/api/accounts") | ||
const accountsData = await accountsResponse.json() | ||
|
||
|
@@ -201,8 +187,8 @@ export default function AccountsPage() { | |
|
||
toast({ | ||
title: "Pipedream API test", | ||
description: `Test ${data.pipedreamTest?.success ? 'succeeded' : 'failed'}. Found ${accountsCount} accounts. Check console for details.`, | ||
variant: data.pipedreamTest?.success ? "default" : "destructive", | ||
description: `Test completed. Found ${accountsCount} accounts. Check console for details.`, | ||
variant: "default" | ||
}) | ||
} catch (error) { | ||
if (debugMode) { | ||
|
@@ -278,7 +264,7 @@ export default function AccountsPage() { | |
<div className="text-center py-10"> | ||
<h3 className="text-lg font-medium mb-2">No connected accounts</h3> | ||
<p className="text-muted-foreground mb-4"> | ||
You haven't connected any accounts yet. | ||
You haven't connected any accounts yet. | ||
</p> | ||
<Button onClick={() => router.push("/")}> | ||
Browse MCP servers | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,88 +1,26 @@ | ||
import { NextResponse } from "next/server" | ||
import { supabase } from "@/lib/supabase" | ||
|
||
export async function GET(request: Request, { params }: { params: { slug: string } }) { | ||
const slug = params.slug | ||
|
||
if (!slug) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Moved lots of code into |
||
return NextResponse.json({ error: "App slug is required" }, { status: 400 }) | ||
} | ||
|
||
import { NextRequest } from 'next/server' | ||
import { secureJsonResponse } from "@/lib/security" | ||
import { getAppActionsBySlug } from "@/lib/services/apps" | ||
|
||
// Define a more specific type for the context parameter | ||
export async function GET( | ||
request: NextRequest, | ||
{ params }: { params: Promise<{ slug: string }> } | ||
) { | ||
const { slug } = await params | ||
try { | ||
console.log(`Fetching actions for app with slug: ${slug}`) | ||
|
||
// First, get the app ID from the slug | ||
const { data: appData, error: appError } = await supabase | ||
.from("apps") | ||
.select("APP_ID") | ||
.eq("APP_NAME_SLUG", slug) | ||
.single() | ||
|
||
if (appError || !appData) { | ||
console.error("Error fetching app ID:", appError) | ||
return NextResponse.json({ error: "App not found", details: appError?.message }, { status: 404 }) | ||
} | ||
|
||
const appId = appData.APP_ID | ||
console.log(`Found app ID: ${appId} for slug: ${slug}`) | ||
|
||
// Now fetch components using the app_id | ||
// Try with APP_ID (uppercase, matching the apps table convention) | ||
let { data: actions, error } = await supabase.from("published_components").select("*").eq("APP_ID", appId) | ||
|
||
if (error) { | ||
console.error("Error with APP_ID, trying lowercase app_id:", error) | ||
|
||
// If that fails, try with app_id (lowercase) | ||
const { data: actionsLowercase, error: errorLowercase } = await supabase | ||
.from("published_components") | ||
.select("*") | ||
.eq("app_id", appId) | ||
|
||
if (errorLowercase) { | ||
console.error("Error with both APP_ID and app_id:", errorLowercase) | ||
|
||
// As a last resort, let's try to get a sample row to see the structure | ||
const { data: sampleData, error: sampleError } = await supabase | ||
.from("published_components") | ||
.select("*") | ||
.limit(1) | ||
|
||
if (!sampleError && sampleData && sampleData.length > 0) { | ||
console.log("Sample published_components columns:", Object.keys(sampleData[0])) | ||
console.log("Sample published_components data:", JSON.stringify(sampleData[0])) | ||
} | ||
|
||
return NextResponse.json({ error: "Failed to fetch actions", details: errorLowercase.message }, { status: 500 }) | ||
} | ||
|
||
actions = actionsLowercase | ||
} | ||
|
||
console.log(`Found ${actions?.length || 0} actions for app ID ${appId}`) | ||
|
||
// Log the first action to see its structure | ||
if (actions && actions.length > 0) { | ||
console.log("First action structure:", Object.keys(actions[0])) | ||
console.log("First action data:", JSON.stringify(actions[0])) | ||
} | ||
|
||
// Ensure we're returning a valid array | ||
const safeActions = Array.isArray(actions) ? actions : [] | ||
|
||
return NextResponse.json({ | ||
actions: safeActions, | ||
count: safeActions.length, | ||
}) | ||
const result = await getAppActionsBySlug(slug) | ||
return secureJsonResponse(result) | ||
} catch (error) { | ||
console.error("Error in actions API:", error instanceof Error ? error.message : String(error)) | ||
return NextResponse.json( | ||
{ | ||
error: "Failed to fetch actions", | ||
details: error instanceof Error ? error.message : String(error), | ||
console.error("Error fetching app actions:", error) | ||
const statusCode = (error as any)?.message?.includes("App not found") ? 404 : 500 | ||
|
||
return secureJsonResponse( | ||
{ | ||
error: "Failed to fetch actions", | ||
details: String(error) | ||
}, | ||
{ status: 500 }, | ||
{ status: statusCode } | ||
) | ||
} | ||
} | ||
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adding ESLint