load template workflow via URL query param (#6546)

## Summary

Adds support for loading templates via URL query parameters. Users can
now share direct links to templates.

To test:

1. checkout this branch
2. start dev server on port 5173
3. go to http://localhost:5173/?template=image_qwen_image_edit_2509

**Examples:**
- `/?template=default` - Loads template with default source
- `/?template=flux_simple&source=custom` - Loads from custom source

Includes error handling with toast notifications for invalid templates
and comprehensive test coverage.

---------

Co-authored-by: Christian Byrne <c.byrne@comfy.org>
This commit is contained in:
Christian Byrne
2025-11-02 21:23:56 -08:00
committed by GitHub
parent 4810b5728a
commit 9cd7a39f09
5 changed files with 326 additions and 6 deletions

View File

@@ -0,0 +1,96 @@
import { useToast } from 'primevue/usetoast'
import { useI18n } from 'vue-i18n'
import { useRoute } from 'vue-router'
import { useTemplateWorkflows } from './useTemplateWorkflows'
/**
* Composable for loading templates from URL query parameters
*
* Supports URLs like:
* - /?template=flux_simple (loads with default source)
* - /?template=flux_simple&source=custom (loads from custom source)
*
* Input validation:
* - Template and source parameters must match: ^[a-zA-Z0-9_-]+$
* - Invalid formats are rejected with console warnings
*/
export function useTemplateUrlLoader() {
const route = useRoute()
const { t } = useI18n()
const toast = useToast()
const templateWorkflows = useTemplateWorkflows()
/**
* Validates parameter format to prevent path traversal and injection attacks
*/
const isValidParameter = (param: string): boolean => {
return /^[a-zA-Z0-9_-]+$/.test(param)
}
/**
* Loads template from URL query parameters if present
* Handles errors internally and shows appropriate user feedback
*/
const loadTemplateFromUrl = async () => {
const templateParam = route.query.template
if (!templateParam || typeof templateParam !== 'string') {
return
}
// Validate template name format
if (!isValidParameter(templateParam)) {
console.warn(
`[useTemplateUrlLoader] Invalid template parameter format: ${templateParam}`
)
return
}
const sourceParam = (route.query.source as string | undefined) || 'default'
// Validate source parameter format
if (!isValidParameter(sourceParam)) {
console.warn(
`[useTemplateUrlLoader] Invalid source parameter format: ${sourceParam}`
)
return
}
// Load template with error handling
try {
await templateWorkflows.loadTemplates()
const success = await templateWorkflows.loadWorkflowTemplate(
templateParam,
sourceParam
)
if (!success) {
toast.add({
severity: 'error',
summary: t('g.error'),
detail: t('templateWorkflows.error.templateNotFound', {
templateName: templateParam
}),
life: 3000
})
}
} catch (error) {
console.error(
'[useTemplateUrlLoader] Failed to load template from URL:',
error
)
toast.add({
severity: 'error',
summary: t('g.error'),
detail: t('g.errorLoadingTemplate'),
life: 3000
})
}
}
return {
loadTemplateFromUrl
}
}