Skip to content

Commit cb3e339

Browse files
committed
fix: there were multiple bugs after reviewing like secret mapping ,env fixed them.
Signed-off-by: Krishna Mohan <krishanmohank974@gmail.com>
1 parent 4c22da9 commit cb3e339

11 files changed

Lines changed: 165 additions & 340 deletions

File tree

backend/.env.docker

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ LOG_KAFKA_BROKERS="redpanda:9092"
3333

3434

3535
# GitHub template repository configuration
36-
GITHUB_TEMPLATE_REPO=krishna9358/workflow-templates
36+
GITHUB_TEMPLATE_REPO=shipsecai/workflow-templates
3737
GITHUB_TEMPLATE_BRANCH=main
3838
# Optional: GitHub personal access token for higher rate limits (60/hr → 5000/hr)
3939
GITHUB_TEMPLATE_TOKEN=

backend/src/config/env.schema.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ export const backendEnvSchema = z
7979
GITHUB_TEMPLATE_REPO: z
8080
.string()
8181
.optional()
82-
.default('krishna9358/workflow-templates')
82+
.default('shipsecai/workflow-templates')
8383
.refine((v) => v.includes('/'), {
8484
message: 'GITHUB_TEMPLATE_REPO must be in owner/repo format',
8585
}),

backend/src/templates/ARCHITECTURE.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -405,9 +405,9 @@ curl -X POST -H "Authorization: Bearer {admin_token}" \
405405

406406
### Required Variables
407407

408-
| Variable | Description | Example |
409-
| ---------------------- | --------------------------------------------- | -------------------------------- |
410-
| `GITHUB_TEMPLATE_REPO` | Public GitHub repository containing templates | `krishna9358/workflow-templates` |
408+
| Variable | Description | Example |
409+
| ---------------------- | --------------------------------------------- | ------------------------------ |
410+
| `GITHUB_TEMPLATE_REPO` | Public GitHub repository containing templates | `shipsecai/workflow-templates` |
411411

412412
### Optional Variables
413413

@@ -421,7 +421,7 @@ curl -X POST -H "Authorization: Bearer {admin_token}" \
421421
# .env or .env.docker
422422

423423
# GitHub template repository (must be public)
424-
GITHUB_TEMPLATE_REPO=krishna9358/workflow-templates
424+
GITHUB_TEMPLATE_REPO=shipsecai/workflow-templates
425425
GITHUB_TEMPLATE_BRANCH=main
426426
```
427427

@@ -705,7 +705,7 @@ Common validation failures:
705705

706706
### Templates Not Showing
707707

708-
1. **Check `GITHUB_TEMPLATE_REPO`** in `.env` -- must match your actual public repo (e.g., `krishna9358/workflow-templates`)
708+
1. **Check `GITHUB_TEMPLATE_REPO`** in `.env` -- must match your actual public repo (e.g., `shipsecai/workflow-templates`)
709709
2. **Ensure the repo is public** -- the sync uses unauthenticated GitHub API calls
710710
3. Check branch name in `GITHUB_TEMPLATE_BRANCH`
711711
4. Review backend logs for sync errors (look for `GitHubSyncService` messages)

backend/src/templates/github-sync.service.ts

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -87,15 +87,15 @@ export class GitHubSyncService implements OnModuleInit, OnModuleDestroy {
8787
`GitHub API auth: ${hasToken ? 'token configured (5000 req/hr)' : 'unauthenticated (60 req/hr)'}`,
8888
);
8989
this.logger.log('Starting automatic template sync...');
90-
try {
91-
const result = await this.syncTemplates();
92-
this.logger.log(
93-
`Startup sync complete: ${result.synced.length} synced, ${result.failed.length} failed`,
94-
);
95-
} catch (err) {
96-
this.logger.error('Startup sync failed', err);
97-
// Don't throw - allow the application to start even if sync fails
98-
}
90+
this.syncTemplates()
91+
.then((result) => {
92+
this.logger.log(
93+
`Startup sync complete: ${result.synced.length} synced, ${result.failed.length} failed`,
94+
);
95+
})
96+
.catch((err) => {
97+
this.logger.error('Startup sync failed', err);
98+
});
9999

100100
// Schedule recurring sync every 30 minutes
101101
this.syncInterval = setInterval(() => {
@@ -150,7 +150,7 @@ export class GitHubSyncService implements OnModuleInit, OnModuleDestroy {
150150
private getRepoConfig(): { owner: string; repo: string; branch: string } {
151151
const repo = this.configService.get<string>(
152152
'GITHUB_TEMPLATE_REPO',
153-
'krishna9358/workflow-templates',
153+
'shipsecai/workflow-templates',
154154
);
155155
const branch = this.configService.get<string>('GITHUB_TEMPLATE_BRANCH', 'main');
156156
const [owner, repoName] = repo.split('/');
@@ -176,7 +176,7 @@ export class GitHubSyncService implements OnModuleInit, OnModuleDestroy {
176176
headers['If-None-Match'] = cached.etag;
177177
}
178178

179-
const response = await fetch(url, { headers });
179+
const response = await fetch(url, { headers, signal: AbortSignal.timeout(15_000) });
180180

181181
// 304 Not Modified — use cached data, zero rate limit cost
182182
if (response.status === 304 && cached) {
@@ -230,7 +230,7 @@ export class GitHubSyncService implements OnModuleInit, OnModuleDestroy {
230230
headers['If-None-Match'] = cached.etag;
231231
}
232232

233-
const response = await fetch(url, { headers });
233+
const response = await fetch(url, { headers, signal: AbortSignal.timeout(15_000) });
234234

235235
// 304 Not Modified — use cached content, zero rate limit cost
236236
if (response.status === 304 && cached) {
@@ -256,7 +256,7 @@ export class GitHubSyncService implements OnModuleInit, OnModuleDestroy {
256256
if (data.content && data.encoding === 'base64') {
257257
content = Buffer.from(data.content, 'base64').toString('utf-8');
258258
} else if (data.download_url) {
259-
const dlResponse = await fetch(data.download_url);
259+
const dlResponse = await fetch(data.download_url, { signal: AbortSignal.timeout(15_000) });
260260
content = await dlResponse.text();
261261
}
262262

backend/src/templates/templates.service.ts

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ export class TemplateService {
113113
// 3. Build the workflow graph from the template, overriding the name
114114
// Templates may lack node positions (stripped during publish to reduce size)
115115
// so we add default positions in a grid layout before schema validation.
116-
const graphData: Record<string, unknown> = {
116+
let graphData: Record<string, unknown> = {
117117
...template.graph,
118118
name: params.workflowName,
119119
};
@@ -127,6 +127,14 @@ export class TemplateService {
127127
});
128128
}
129129

130+
if (params.secretMappings && template.requiredSecrets && template.requiredSecrets.length > 0) {
131+
graphData = this.applySecretMappings(
132+
graphData,
133+
params.secretMappings,
134+
template.requiredSecrets,
135+
);
136+
}
137+
130138
// Parse through the WorkflowGraphSchema to ensure it conforms to the
131139
// expected shape (adds defaults for viewport, config, etc.)
132140
const workflowGraph = WorkflowGraphSchema.parse(graphData);
@@ -166,4 +174,73 @@ export class TemplateService {
166174
async getSubmissions(userId: string) {
167175
return await this.templatesRepository.findSubmissionsByUser(userId);
168176
}
177+
178+
private applySecretMappings(
179+
graph: Record<string, unknown>,
180+
secretMappings: Record<string, string>,
181+
requiredSecrets: { name: string; type: string; description?: string; placeholder?: string }[],
182+
): Record<string, unknown> {
183+
if (!secretMappings || Object.keys(secretMappings).length === 0) {
184+
return graph;
185+
}
186+
187+
const json = JSON.stringify(graph);
188+
189+
if (requiredSecrets.length === 1 && secretMappings[requiredSecrets[0].name]) {
190+
const replaced = json.replace(
191+
/\{\{SECRET_PLACEHOLDER\}\}/g,
192+
secretMappings[requiredSecrets[0].name],
193+
);
194+
return JSON.parse(replaced);
195+
}
196+
197+
const result = JSON.parse(json);
198+
this.traverseAndApplySecrets(result, secretMappings);
199+
return result;
200+
}
201+
202+
private traverseAndApplySecrets(obj: unknown, secretMappings: Record<string, string>): void {
203+
if (typeof obj !== 'object' || obj === null) return;
204+
205+
if (Array.isArray(obj)) {
206+
for (const item of obj) {
207+
this.traverseAndApplySecrets(item, secretMappings);
208+
}
209+
return;
210+
}
211+
212+
const record = obj as Record<string, unknown>;
213+
214+
const secretKeys = ['secretId', 'secret_name', 'secretName', 'secretRef', 'secret_ref'];
215+
for (const key of secretKeys) {
216+
if (record[key] === '{{SECRET_PLACEHOLDER}}') {
217+
const possibleNameKeys = ['label', 'name', 'key', 'displayName'];
218+
for (const nameKey of possibleNameKeys) {
219+
const nameValue = record[nameKey];
220+
if (typeof nameValue === 'string' && secretMappings[nameValue]) {
221+
record[key] = secretMappings[nameValue];
222+
break;
223+
}
224+
}
225+
226+
if (record[key] === '{{SECRET_PLACEHOLDER}}') {
227+
const firstAvailable = Object.values(secretMappings)[0];
228+
if (firstAvailable) {
229+
record[key] = firstAvailable;
230+
}
231+
}
232+
}
233+
}
234+
235+
for (const [key, value] of Object.entries(record)) {
236+
if (typeof value === 'string' && value.includes('{{SECRET_PLACEHOLDER}}')) {
237+
const firstAvailable = Object.values(secretMappings)[0];
238+
if (firstAvailable) {
239+
record[key] = value.replace(/\{\{SECRET_PLACEHOLDER\}\}/g, firstAvailable);
240+
}
241+
} else if (typeof value === 'object' && value !== null) {
242+
this.traverseAndApplySecrets(value, secretMappings);
243+
}
244+
}
245+
}
169246
}

frontend/.env.example

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ VITE_PUBLIC_POSTHOG_HOST=
1919
# VITE_DISABLE_ANALYTICS=true
2020

2121

22+
# GitHub Template Library (owner/repo format)
23+
VITE_GITHUB_TEMPLATE_REPO=shipsecai/workflow-templates
24+
VITE_GITHUB_TEMPLATE_BRANCH=main
25+
2226
# Logo.dev public key for brand logos
2327
VITE_LOGO_DEV_PUBLIC_KEY=
2428

frontend/src/features/templates/PublishTemplateModal.tsx

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

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';
43+
const DEFAULT_GITHUB_TEMPLATE_REPO =
44+
import.meta.env.VITE_GITHUB_TEMPLATE_REPO || 'shipsecai/workflow-templates';
45+
const DEFAULT_GITHUB_BRANCH = import.meta.env.VITE_GITHUB_TEMPLATE_BRANCH || 'main';
4646

4747
const TEMPLATE_CATEGORIES = [
4848
'Security',
@@ -274,6 +274,7 @@ export function PublishTemplateModal({
274274
const [success, setSuccess] = useState(false);
275275
const [generatedTemplateJson, setGeneratedTemplateJson] = useState<string>('');
276276
const [copied, setCopied] = useState(false);
277+
const [repoUrl, setRepoUrl] = useState(`https://github.com/${DEFAULT_GITHUB_TEMPLATE_REPO}`);
277278

278279
const handleSubmit = useCallback(
279280
async (e: React.FormEvent) => {
@@ -353,8 +354,11 @@ export function PublishTemplateModal({
353354
} catch {
354355
[owner, repo] = DEFAULT_GITHUB_TEMPLATE_REPO.split('/');
355356
branch = DEFAULT_GITHUB_BRANCH;
357+
setRepoUrl(`https://github.com/${DEFAULT_GITHUB_TEMPLATE_REPO}`);
356358
}
357359

360+
setRepoUrl(`https://github.com/${owner}/${repo}`);
361+
358362
// Generate GitHub URL without content (avoids long URL errors)
359363
const githubUrl = generateGitHubUrl(owner, repo, branch, filename, name.trim());
360364

@@ -405,6 +409,7 @@ export function PublishTemplateModal({
405409
setSuccess(false);
406410
setGeneratedTemplateJson('');
407411
setCopied(false);
412+
setRepoUrl(`https://github.com/${DEFAULT_GITHUB_TEMPLATE_REPO}`);
408413
}, 200);
409414
}
410415
};
@@ -492,7 +497,7 @@ export function PublishTemplateModal({
492497
before it&apos;s added to the library.
493498
</div>
494499
<a
495-
href={`https://github.com/${DEFAULT_GITHUB_TEMPLATE_REPO}`}
500+
href={repoUrl}
496501
target="_blank"
497502
rel="noopener noreferrer"
498503
className="text-primary hover:underline flex items-center gap-1"

frontend/src/hooks/queries/useTemplateQueries.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
22
import { api } from '@/services/api';
33
import { queryKeys } from '@/lib/queryKeys';
4-
import type { Template, TemplateCategory } from '@/store/templateStore';
4+
import type { Template, TemplateCategory } from '@/types/templates';
55

6-
export type { Template, TemplateCategory } from '@/store/templateStore';
6+
export type { Template, TemplateCategory } from '@/types/templates';
77

88
export function useTemplates(filters?: { category?: string; search?: string; tags?: string[] }) {
99
return useQuery<Template[]>({

frontend/src/pages/TemplateLibraryPage.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -944,7 +944,10 @@ export function TemplateLibraryPage() {
944944
<UseTemplateModal
945945
template={selectedTemplate}
946946
open={isUseModalOpen}
947-
onOpenChange={setIsUseModalOpen}
947+
onOpenChange={(open) => {
948+
setIsUseModalOpen(open);
949+
if (!open) setSelectedTemplate(null);
950+
}}
948951
onSuccess={handleTemplateUseSuccess}
949952
/>
950953
)}

0 commit comments

Comments
 (0)