Skip to content

Commit 4c22da9

Browse files
committed
fix: address code review findings for template library
- Sanitize {{secret:SECRET_ID}} references in publish validation (#1) - Migrate UseTemplateModal from Zustand to TanStack Query mutation (#2) - Reset modal state on template/open change to prevent stale data (#3) - Fetch GitHub repo config dynamically with fallback defaults (#4) - Use snake_case keys for analytics event properties (#5) Signed-off-by: Krishna Mohan <krishanmohank974@gmail.com>
1 parent e164b72 commit 4c22da9

3 files changed

Lines changed: 50 additions & 16 deletions

File tree

frontend/src/features/templates/PublishTemplateModal.tsx

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,9 @@ interface PublishTemplateModalProps {
4040
onSuccess?: () => void;
4141
}
4242

43-
// GitHub repository configuration for templates
44-
const GITHUB_TEMPLATE_REPO = 'krishna9358/workflow-templates'; // format: owner/repo
45-
const GITHUB_BRANCH = 'main';
43+
// Fallback GitHub repository configuration (overridden by backend /templates/repo-info)
44+
const DEFAULT_GITHUB_TEMPLATE_REPO = 'krishna9358/workflow-templates';
45+
const DEFAULT_GITHUB_BRANCH = 'main';
4646

4747
const TEMPLATE_CATEGORIES = [
4848
'Security',
@@ -126,13 +126,15 @@ function sanitizeGraphForTemplate(graph: Record<string, unknown>): Record<string
126126
typeof value === 'string' &&
127127
(value.includes('${secrets.') ||
128128
value.includes('${secret.') ||
129-
value.includes('{{secret.'))
129+
value.includes('{{secret.') ||
130+
value.includes('{{secret:'))
130131
) {
131132
// Replace secret interpolation expressions with placeholder
132133
result[key] = value
133134
.replace(/\$\{secrets\.[^}]+\}/g, '{{SECRET_PLACEHOLDER}}')
134135
.replace(/\$\{secret\.[^}]+\}/g, '{{SECRET_PLACEHOLDER}}')
135-
.replace(/\{\{secret\.[^}]+\}\}/g, '{{SECRET_PLACEHOLDER}}');
136+
.replace(/\{\{secret\.[^}]+\}\}/g, '{{SECRET_PLACEHOLDER}}')
137+
.replace(/\{\{secret:[a-f0-9-]+\}\}/gi, '{{SECRET_PLACEHOLDER}}');
136138
} else {
137139
result[key] = traverseAndSanitize(value);
138140
}
@@ -333,11 +335,28 @@ export function PublishTemplateModal({
333335
// Store the template JSON so user can re-copy from the success view
334336
setGeneratedTemplateJson(templateJson);
335337

336-
// Parse the GitHub repo config
337-
const [owner, repo] = GITHUB_TEMPLATE_REPO.split('/');
338+
// Fetch repo config from backend, fall back to defaults
339+
let owner: string;
340+
let repo: string;
341+
let branch: string;
342+
try {
343+
const repoInfoRes = await fetch(`${API_BASE_URL}/api/v1/templates/repo-info`);
344+
if (repoInfoRes.ok) {
345+
const repoInfo = await repoInfoRes.json();
346+
owner = repoInfo.owner;
347+
repo = repoInfo.repo;
348+
branch = repoInfo.branch;
349+
} else {
350+
[owner, repo] = DEFAULT_GITHUB_TEMPLATE_REPO.split('/');
351+
branch = DEFAULT_GITHUB_BRANCH;
352+
}
353+
} catch {
354+
[owner, repo] = DEFAULT_GITHUB_TEMPLATE_REPO.split('/');
355+
branch = DEFAULT_GITHUB_BRANCH;
356+
}
338357

339358
// Generate GitHub URL without content (avoids long URL errors)
340-
const githubUrl = generateGitHubUrl(owner, repo, GITHUB_BRANCH, filename, name.trim());
359+
const githubUrl = generateGitHubUrl(owner, repo, branch, filename, name.trim());
341360

342361
// Open the GitHub URL in a new tab
343362
window.open(githubUrl, '_blank', 'noopener,noreferrer');
@@ -473,7 +492,7 @@ export function PublishTemplateModal({
473492
before it&apos;s added to the library.
474493
</div>
475494
<a
476-
href={`https://github.com/${GITHUB_TEMPLATE_REPO}`}
495+
href={`https://github.com/${DEFAULT_GITHUB_TEMPLATE_REPO}`}
477496
target="_blank"
478497
rel="noopener noreferrer"
479498
className="text-primary hover:underline flex items-center gap-1"

frontend/src/features/templates/UseTemplateModal.tsx

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useState } from 'react';
1+
import { useState, useEffect } from 'react';
22
import {
33
Dialog,
44
DialogContent,
@@ -12,7 +12,7 @@ import { Input } from '@/components/ui/input';
1212
import { Label } from '@/components/ui/label';
1313
import { Badge } from '@/components/ui/badge';
1414
import { Loader2, AlertCircle, Eye, EyeOff, KeyRound } from 'lucide-react';
15-
import { useTemplateStore, type Template } from '@/store/templateStore';
15+
import { useUseTemplate, type Template } from '@/hooks/queries/useTemplateQueries';
1616

1717
interface UseTemplateModalProps {
1818
template: Template;
@@ -27,13 +27,24 @@ export function UseTemplateModal({
2727
onOpenChange,
2828
onSuccess,
2929
}: UseTemplateModalProps) {
30-
const { useTemplate, isLoading } = useTemplateStore();
30+
const useTemplateMutation = useUseTemplate();
31+
const isLoading = useTemplateMutation.isPending;
3132

3233
const [workflowName, setWorkflowName] = useState(`${template.name} - Copy`);
3334
const [secretMappings, setSecretMappings] = useState<Record<string, string>>({});
3435
const [showSecrets, setShowSecrets] = useState(false);
3536
const [error, setError] = useState<string | null>(null);
3637

38+
// Reset state when template or open changes to avoid stale data (#3)
39+
useEffect(() => {
40+
if (open) {
41+
setWorkflowName(`${template.name} - Copy`);
42+
setSecretMappings({});
43+
setShowSecrets(false);
44+
setError(null);
45+
}
46+
}, [template.id, open]);
47+
3748
// Initialize secret mappings with placeholder values
3849
const requiredSecrets = template.requiredSecrets || [];
3950

@@ -57,8 +68,12 @@ export function UseTemplateModal({
5768
}
5869

5970
try {
60-
const result = await useTemplate(template.id, workflowName, secretMappings);
61-
onSuccess(result.workflowId);
71+
const result = await useTemplateMutation.mutateAsync({
72+
templateId: template.id,
73+
workflowName,
74+
secretMappings: requiredSecrets.length > 0 ? secretMappings : undefined,
75+
});
76+
onSuccess(result.workflow?.id ?? result.workflowId);
6277
} catch (err) {
6378
setError(err instanceof Error ? err.message : 'Failed to create workflow from template');
6479
}

frontend/src/pages/TemplateLibraryPage.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -782,8 +782,8 @@ export function TemplateLibraryPage() {
782782
setSelectedTemplate(template);
783783
setIsUseModalOpen(true);
784784
track(Events.TemplateUseClicked, {
785-
templateId: template.id,
786-
templateName: template.name,
785+
template_id: template.id,
786+
template_name: template.name,
787787
category: template.category,
788788
});
789789
};

0 commit comments

Comments
 (0)