Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
7b624aa
feat: Add Template Library functionality
krishna9358 Feb 11, 2026
2083f46
fix: Resolve ESLint/Prettier formatting issues in Template Library
krishna9358 Feb 11, 2026
6fc48cc
fix: Resolve ESLint/Prettier formatting issues in backend templates
krishna9358 Feb 11, 2026
371a634
fix: Temporarily disable Templates module due to Bun+NestJS compatibi…
krishna9358 Feb 12, 2026
f572b89
feat: Refactor Template Library to use GitHub web flow
krishna9358 Feb 12, 2026
f662302
feat: Add GitHub webhook endpoint for automatic template sync
krishna9358 Feb 12, 2026
c8f8180
feat: Add GITHUB_WEBHOOK_SECRET env var placeholder
krishna9358 Feb 12, 2026
c1505d0
fix: Remove dead syncTemplates method from TemplateService
krishna9358 Feb 12, 2026
fa06696
feat: Simplify template sync to startup-only with rate limit handling
krishna9358 Feb 12, 2026
533ee25
fix: Resolve lint and prettier formatting errors in frontend templates
krishna9358 Feb 12, 2026
008ef7f
fix: Use clipboard instead of URL for template content to avoid GitHu…
krishna9358 Feb 12, 2026
1fa8249
fix: Fix template sync validation and improve publish UX
krishna9358 Feb 12, 2026
2f8f0a6
feat: Implement Use Template, fix publish UX, fix card layout
krishna9358 Feb 12, 2026
5843cff
fix: Fix Use Template redirect, restore JSON formatting, add GitHub t…
krishna9358 Feb 12, 2026
6880881
feat: Add ETag conditional requests and fix missing node positions
krishna9358 Feb 13, 2026
4bf2b8e
merge: Resolve conflicts merging main into template-library
krishna9358 Feb 13, 2026
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
9 changes: 8 additions & 1 deletion backend/.env.docker
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,11 @@ LOKI_USERNAME=""
LOKI_PASSWORD=""

# Kafka / Redpanda configuration for node I/O, log, and event ingestion (Docker network)
LOG_KAFKA_BROKERS="redpanda:9092"
LOG_KAFKA_BROKERS="redpanda:9092"


# GitHub template repository configuration
GITHUB_TEMPLATE_REPO=krishna9358/workflow-templates
GITHUB_TEMPLATE_BRANCH=main
# Optional: GitHub personal access token for higher rate limits (60/hr → 5000/hr)
GITHUB_TEMPLATE_TOKEN=
59 changes: 59 additions & 0 deletions backend/drizzle/0020_create-templates.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
-- Create templates table
CREATE TABLE "templates" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"name" varchar(255) NOT NULL,
"description" text,
"category" varchar(100),
"tags" jsonb DEFAULT '[]'::jsonb NOT NULL,
"author" varchar(255),
"repository" varchar(255) NOT NULL,
"path" varchar(500) NOT NULL,
"branch" varchar(100) DEFAULT 'main' NOT NULL,
"version" varchar(50),
"commit_sha" varchar(100),
"manifest" jsonb NOT NULL,
"graph" jsonb,
"required_secrets" jsonb DEFAULT '[]'::jsonb NOT NULL,
"popularity" integer DEFAULT 0 NOT NULL,
"is_official" boolean DEFAULT false NOT NULL,
"is_verified" boolean DEFAULT false NOT NULL,
"is_active" boolean DEFAULT true NOT NULL,
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
);
--> statement-breakpoint
-- Create templates_submissions table
CREATE TABLE "templates_submissions" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"template_name" varchar(255) NOT NULL,
"description" text,
"category" varchar(100),
"repository" varchar(255) NOT NULL,
"branch" varchar(100),
"path" varchar(500) NOT NULL,
"commit_sha" varchar(100),
"pr_number" integer,
"pr_url" varchar(500),
"status" varchar(50) DEFAULT 'pending' NOT NULL,
"submitted_by" varchar(191) NOT NULL,
"organization_id" varchar(191),
"manifest" jsonb,
"graph" jsonb,
"feedback" text,
"reviewed_by" varchar(191),
"reviewed_at" timestamp with time zone,
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
);
--> statement-breakpoint
-- Create indexes for templates
CREATE INDEX "templates_repository_path_idx" ON "templates" ("repository", "path");
CREATE INDEX "templates_category_idx" ON "templates" ("category");
CREATE INDEX "templates_is_active_idx" ON "templates" ("is_active");
CREATE INDEX "templates_popularity_idx" ON "templates" ("popularity" DESC);
--> statement-breakpoint
-- Create indexes for templates_submissions
CREATE INDEX "templates_submissions_pr_number_idx" ON "templates_submissions" ("pr_number");
CREATE INDEX "templates_submissions_submitted_by_idx" ON "templates_submissions" ("submitted_by");
CREATE INDEX "templates_submissions_status_idx" ON "templates_submissions" ("status");
CREATE INDEX "templates_submissions_organization_id_idx" ON "templates_submissions" ("organization_id");
1 change: 1 addition & 0 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"bcryptjs": "^3.0.3",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.3",
"cookie-parser": "^1.4.7",
"date-fns": "^4.1.0",
"dotenv": "^17.2.3",
"drizzle-orm": "^0.44.7",
Expand Down
2 changes: 2 additions & 0 deletions backend/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import { WebhooksModule } from './webhooks/webhooks.module';
import { HumanInputsModule } from './human-inputs/human-inputs.module';
import { McpServersModule } from './mcp-servers/mcp-servers.module';
import { McpGroupsModule } from './mcp-groups/mcp-groups.module';
import { TemplatesModule } from './templates/templates.module';

const coreModules = [
AgentsModule,
Expand All @@ -51,6 +52,7 @@ const coreModules = [
McpGroupsModule,
McpModule,
StudioMcpModule,
TemplatesModule,
];

const testingModules = process.env.NODE_ENV === 'production' ? [] : [TestingSupportModule];
Expand Down
1 change: 1 addition & 0 deletions backend/src/database/schema/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ export * from './mcp-servers';

export * from './node-io';
export * from './organization-settings';
export * from './templates';
99 changes: 99 additions & 0 deletions backend/src/database/schema/templates.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import {
pgTable,
uuid,
varchar,
text,
timestamp,
jsonb,
boolean,
integer,
} from 'drizzle-orm/pg-core';
import { z } from 'zod';

/**
* Templates table - stores workflow template metadata
* Templates are synced from GitHub repository
*/
export const templatesTable = pgTable('templates', {
id: uuid('id').primaryKey().defaultRandom(),
name: varchar('name', { length: 255 }).notNull(),
description: text('description'),
category: varchar('category', { length: 100 }),
tags: jsonb('tags').$type<string[]>().default([]),
author: varchar('author', { length: 255 }),
// GitHub repository info
repository: varchar('repository', { length: 255 }).notNull(), // e.g., "org/templates"
path: varchar('path', { length: 500 }).notNull(), // Path to template in repo
branch: varchar('branch', { length: 100 }).default('main'),
version: varchar('version', { length: 50 }), // Optional version tag
commitSha: varchar('commit_sha', { length: 100 }),
// Template content
manifest: jsonb('manifest').$type<TemplateManifest>().notNull(),
graph: jsonb('graph').$type<Record<string, unknown>>(), // Sanitized workflow graph
requiredSecrets: jsonb('required_secrets').$type<RequiredSecret[]>().default([]),
// Stats and flags
popularity: integer('popularity').notNull().default(0),
isOfficial: boolean('is_official').notNull().default(false),
isVerified: boolean('is_verified').notNull().default(false),
isActive: boolean('is_active').notNull().default(true),
// Timestamps
createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow().notNull(),
});

/**
* Template submissions table - tracks PR-based template submissions
*/
export const templatesSubmissionsTable = pgTable('templates_submissions', {
id: uuid('id').primaryKey().defaultRandom(),
templateName: varchar('template_name', { length: 255 }).notNull(),
description: text('description'),
category: varchar('category', { length: 100 }),
repository: varchar('repository', { length: 255 }).notNull(),
branch: varchar('branch', { length: 100 }),
path: varchar('path', { length: 500 }).notNull(),
commitSha: varchar('commit_sha', { length: 100 }),
pullRequestNumber: integer('pr_number'),
pullRequestUrl: varchar('pr_url', { length: 500 }),
status: varchar('status', { length: 50 }).notNull().default('pending'), // pending, approved, rejected, merged
submittedBy: varchar('submitted_by', { length: 191 }).notNull(),
organizationId: varchar('organization_id', { length: 191 }),
manifest: jsonb('manifest').$type<TemplateManifest>(),
graph: jsonb('graph').$type<Record<string, unknown>>(),
feedback: text('feedback'),
reviewedBy: varchar('reviewed_by', { length: 191 }),
reviewedAt: timestamp('reviewed_at', { withTimezone: true }),
createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow().notNull(),
});

// Zod schemas for validation
export const RequiredSecretSchema = z.object({
name: z.string(),
type: z.string(),
description: z.string().optional(),
placeholder: z.string().optional(),
});

export const TemplateManifestSchema = z.object({
name: z.string(),
description: z.string().optional(),
version: z.string().optional(),
author: z.string().optional(),
category: z.string().optional(),
tags: z.array(z.string()).optional(),
requiredSecrets: z.array(RequiredSecretSchema).optional(),
entryPoint: z.string().optional(),
screenshots: z.array(z.string()).optional(),
documentation: z.string().optional(),
});

// Type exports
export type TemplateManifest = z.infer<typeof TemplateManifestSchema>;
export type RequiredSecret = z.infer<typeof RequiredSecretSchema>;

export type Template = typeof templatesTable.$inferSelect;
export type NewTemplate = typeof templatesTable.$inferInsert;

export type TemplateSubmission = typeof templatesSubmissionsTable.$inferSelect;
export type NewTemplateSubmission = typeof templatesSubmissionsTable.$inferInsert;
Loading