Skip to content
Merged
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
4 changes: 2 additions & 2 deletions .github/workflows/deploy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ jobs:
timeout: 5m0s
name: ${{ env.SERVICE_NAME }}
chart-repository: https://techops-services.github.io/helm-charts
version: 0.0.33
version: 0.2.1
atomic: true

- name: Deploying Job Spawner to Kubernetes with Helm
Expand All @@ -171,5 +171,5 @@ jobs:
timeout: 5m0s
name: keeperhub-job-spawner
chart-repository: https://techops-services.github.io/helm-charts
version: 0.0.33
version: 0.2.1
atomic: true
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ COPY --from=deps /app/node_modules ./node_modules
COPY --from=builder /app/drizzle ./drizzle
COPY --from=builder /app/drizzle.config.ts ./drizzle.config.ts
COPY --from=builder /app/lib ./lib
COPY --from=builder /app/keeperhub ./keeperhub
COPY --from=builder /app/package.json ./package.json

# This stage can be used to run migrations
Expand Down
35 changes: 35 additions & 0 deletions app/api/workflows/public/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { desc, eq } from "drizzle-orm";
import { NextResponse } from "next/server";
import { db } from "@/lib/db";
import { workflows } from "@/lib/db/schema";

// start custom KeeperHub code
export async function GET() {
try {
const publicWorkflows = await db
.select()
.from(workflows)
.where(eq(workflows.visibility, "public"))
.orderBy(desc(workflows.updatedAt));

const mappedWorkflows = publicWorkflows.map((workflow) => ({
...workflow,
createdAt: workflow.createdAt.toISOString(),
updatedAt: workflow.updatedAt.toISOString(),
}));

return NextResponse.json(mappedWorkflows);
} catch (error) {
console.error("Failed to get public workflows:", error);
return NextResponse.json(
{
error:
error instanceof Error
? error.message
: "Failed to get public workflows",
},
{ status: 500 }
);
}
}
// end custom KeeperHub code
42 changes: 42 additions & 0 deletions app/hub/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
"use client";

import { useEffect, useState } from "react";
import { WorkflowTemplateGrid } from "@/keeperhub/components/hub/workflow-template-grid";
import { api, type SavedWorkflow } from "@/lib/api-client";

export default function HubPage() {
// start custom KeeperHub code
const [workflows, setWorkflows] = useState<SavedWorkflow[]>([]);
const [isLoading, setIsLoading] = useState(true);

useEffect(() => {
const fetchPublicWorkflows = async () => {
try {
const publicWorkflows = await api.workflow.getPublic();
setWorkflows(publicWorkflows);
} catch (error) {
console.error("Failed to fetch public workflows:", error);
} finally {
setIsLoading(false);
}
};

fetchPublicWorkflows();
}, []);
// end custom KeeperHub code

return (
<div className="pointer-events-auto">
<div className="container mx-auto px-4 py-4 pt-28">
<h1 className="mb-8 font-bold text-3xl">Public Workflows</h1>
{/* start custom KeeperHub code */}
{isLoading ? (
<p className="text-muted-foreground">Loading workflows...</p>
) : (
<WorkflowTemplateGrid workflows={workflows} />
)}
{/* end custom KeeperHub code */}
</div>
</div>
);
}
13 changes: 1 addition & 12 deletions app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,16 @@ import type { Metadata, Viewport } from "next";
import "./globals.css";
import { Analytics } from "@vercel/analytics/react";
import { SpeedInsights } from "@vercel/speed-insights/next";
import { ReactFlowProvider } from "@xyflow/react";
import { Provider } from "jotai";
import { type ReactNode, Suspense } from "react";
import { AuthProvider } from "@/components/auth/provider";
import { GitHubStarsLoader } from "@/components/github-stars-loader";
import { GitHubStarsProvider } from "@/components/github-stars-provider";
import { GlobalModals } from "@/components/global-modals";
import { LayoutContent } from "@/components/layout-content";
import { OverlayProvider } from "@/components/overlays/overlay-provider";
import { ThemeProvider } from "@/components/theme-provider";
import { Toaster } from "@/components/ui/sonner";
import { PersistentCanvas } from "@/components/workflow/persistent-canvas";
import { KeeperHubExtensionLoader } from "@/keeperhub/components/extension-loader";
import { mono, sans } from "@/lib/fonts";
import { cn } from "@/lib/utils";
Expand All @@ -35,16 +34,6 @@ type RootLayoutProps = {
children: ReactNode;
};

// Inner content wrapped by GitHubStarsProvider (used for both loading and loaded states)
function LayoutContent({ children }: { children: ReactNode }) {
return (
<ReactFlowProvider>
<PersistentCanvas />
<div className="pointer-events-none relative z-10">{children}</div>
</ReactFlowProvider>
);
}

const RootLayout = ({ children }: RootLayoutProps) => (
<html lang="en" suppressHydrationWarning>
<body className={cn(sans.variable, mono.variable, "antialiased")}>
Expand Down
16 changes: 16 additions & 0 deletions components/layout-content.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
"use client";

import { ReactFlowProvider } from "@xyflow/react";
import type { ReactNode } from "react";
import { PersistentCanvas } from "@/components/workflow/persistent-canvas";
import { WorkflowToolbar } from "@/components/workflow/workflow-toolbar";

export function LayoutContent({ children }: { children: ReactNode }) {
return (
<ReactFlowProvider>
<WorkflowToolbar persistent />
<PersistentCanvas />
<div className="pointer-events-none relative z-10">{children}</div>
</ReactFlowProvider>
);
}
15 changes: 15 additions & 0 deletions components/overlays/add-connection-overlay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ import {
aiGatewayTeamsLoadingAtom,
} from "@/lib/ai-gateway/state";
import { api } from "@/lib/api-client";
// start keeperhub
import { getCustomIntegrationFormHandler } from "@/lib/extension-registry";
// end keeperhub
import type { IntegrationType } from "@/lib/types/integration";
import {
getIntegration,
Expand Down Expand Up @@ -361,6 +364,18 @@ export function ConfigureConnectionOverlay({

// Render config fields
const renderConfigFields = () => {
// start keeperhub - check for custom form handlers (e.g., web3 wallet)
const customHandler = getCustomIntegrationFormHandler(type);
if (customHandler) {
return customHandler({
integrationType: type,
isEditMode: false,
config,
updateConfig,
});
}
// end keeperhub

if (type === "database") {
return (
<SecretField
Expand Down
15 changes: 13 additions & 2 deletions components/overlays/configuration-overlay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ export function ConfigurationOverlay({ overlayId }: ConfigurationOverlayProps) {
currentWorkflowNameAtom
);
const isOwner = useAtomValue(isWorkflowOwnerAtom);

const updateNodeData = useSetAtom(updateNodeDataAtom);
const deleteNode = useSetAtom(deleteNodeAtom);
const deleteEdge = useSetAtom(deleteEdgeAtom);
Expand All @@ -111,6 +112,7 @@ export function ConfigurationOverlay({ overlayId }: ConfigurationOverlayProps) {

// Auto-fix invalid integration references
const globalIntegrations = useAtomValue(integrationsAtom);

useEffect(() => {
if (!(selectedNode && isOwner)) {
return;
Expand Down Expand Up @@ -465,17 +467,26 @@ export function ConfigurationOverlay({ overlayId }: ConfigurationOverlayProps) {
</p>
</div>
)}
{isOwner && (
{isOwner ? (
<div className="flex items-center gap-2 pt-2">
<Button onClick={handleClearWorkflow} variant="ghost">
<Eraser className="mr-2 size-4" />
Clear
</Button>
<Button onClick={handleDeleteWorkflow} variant="ghost">
<Button
data-testid="delete-workflow-button"
onClick={() => handleDeleteWorkflow()}
type="button"
variant="ghost"
>
<Trash2 className="mr-2 size-4" />
Delete
</Button>
</div>
) : (
<div className="text-red-500 text-xs">
DEBUG: isOwner is false - Delete button not rendered
</div>
)}
</div>
)}
Expand Down
15 changes: 15 additions & 0 deletions components/overlays/edit-connection-overlay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { useIsMobile } from "@/hooks/use-mobile";
import { api, type Integration } from "@/lib/api-client";
// start keeperhub
import { getCustomIntegrationFormHandler } from "@/lib/extension-registry";
import { getIntegration, getIntegrationLabels } from "@/plugins";
// end keeperhub
import { ConfirmOverlay } from "./confirm-overlay";
import { Overlay } from "./overlay";
import { useOverlay } from "./overlay-provider";
Expand Down Expand Up @@ -267,6 +270,18 @@ export function EditConnectionOverlay({

// Render config fields
const renderConfigFields = () => {
// start keeperhub - check for custom form handlers (e.g., web3 wallet display)
const customHandler = getCustomIntegrationFormHandler(integration.type);
if (customHandler) {
return customHandler({
integrationType: integration.type,
isEditMode: true,
config,
updateConfig,
});
}
// end keeperhub

if (integration.type === "database") {
return (
<SecretField
Expand Down
27 changes: 15 additions & 12 deletions components/workflow/config/action-config.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import {
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
// start keeperhub
import { integrationRequiresCredentials } from "@/keeperhub/lib/integration-helpers";
// end keeperhub
import { aiGatewayStatusAtom } from "@/lib/ai-gateway/state";
Expand All @@ -39,6 +38,7 @@ import {
findActionById,
getActionsByCategory,
getAllIntegrations,
getIntegration,
} from "@/plugins";
import { ActionConfigRenderer } from "./action-config-renderer";
import { SchemaBuilder, type SchemaField } from "./schema-builder";
Expand Down Expand Up @@ -524,17 +524,20 @@ export function ActionConfig({
</Tooltip>
</TooltipProvider>
</div>
{hasExistingConnections && (
<Button
className="size-6"
disabled={disabled}
onClick={handleAddSecondaryConnection}
size="icon"
variant="ghost"
>
<Plus className="size-4" />
</Button>
)}
{/* start keeperhub - hide + button for singleConnection integrations */}
{hasExistingConnections &&
!getIntegration(integrationType)?.singleConnection && (
<Button
className="size-6"
disabled={disabled}
onClick={handleAddSecondaryConnection}
size="icon"
variant="ghost"
>
<Plus className="size-4" />
</Button>
)}
{/* end keeperhub */}
</div>
<IntegrationSelector
disabled={disabled}
Expand Down
Loading
Loading