Skip to content

Commit 0c01c29

Browse files
committed
feat(模板): 扩展模板类型支持并改进模板创建流程
重构模板类型系统,新增预定义模板、自定义模板和Compose模板类型 更新i18n翻译和前端界面以支持新的模板类型 修改模板创建逻辑,默认使用预设模板类型
1 parent ed48ee1 commit 0c01c29

3 files changed

Lines changed: 132 additions & 36 deletions

File tree

app_templates_registry.go

Lines changed: 74 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -598,12 +598,15 @@ func (a *App) CreateLocalTemplate(name string, scaffold string) error {
598598
func scaffoldFiles(scaffold string, name string) map[string]string {
599599
baseName := filepath.Base(name)
600600

601-
caseJSON := fmt.Sprintf(`{
601+
makeCaseJSON := func(tmplType string) string {
602+
return fmt.Sprintf(`{
602603
"name": "%s",
603604
"description": "",
604605
"version": "1.0.0",
605-
"user": ""
606-
}`, baseName)
606+
"user": "",
607+
"template": "%s"
608+
}`, baseName, tmplType)
609+
}
607610

608611
mainTF := `# =============================================================================
609612
# Terraform Main Configuration
@@ -687,17 +690,81 @@ func scaffoldFiles(scaffold string, name string) map[string]string {
687690
`
688691

689692
switch scaffold {
690-
case "with-userdata":
693+
case "preset":
694+
return map[string]string{
695+
"case.json": makeCaseJSON("preset"),
696+
"main.tf": mainTF,
697+
"variables.tf": variablesTF,
698+
"outputs.tf": outputsTF,
699+
}
700+
case "preset-userdata":
691701
return map[string]string{
692-
"case.json": caseJSON,
702+
"case.json": makeCaseJSON("preset"),
693703
"main.tf": mainTF,
694704
"variables.tf": variablesTF,
695705
"outputs.tf": outputsTF,
696706
"userdata": "#!/bin/bash\n# Add your initialization script here\necho \"Hello from redc\"\n",
697707
}
698-
default: // "blank"
708+
case "base":
709+
return map[string]string{
710+
"case.json": makeCaseJSON("base"),
711+
"main.tf": mainTF,
712+
"variables.tf": variablesTF,
713+
"outputs.tf": outputsTF,
714+
}
715+
case "userdata":
716+
userdataCaseJSON := fmt.Sprintf(`{
717+
"name": "%s",
718+
"description": "",
719+
"version": "1.0.0",
720+
"user": "",
721+
"type": "bash",
722+
"category": "custom",
723+
"template": "userdata"
724+
}`, baseName)
725+
return map[string]string{
726+
"case.json": userdataCaseJSON,
727+
"userdata": "#!/bin/bash\n# Add your initialization script here\necho \"Hello from redc\"\n",
728+
}
729+
case "compose":
730+
composeCaseJSON := fmt.Sprintf(`{
731+
"name": "%s",
732+
"description": "",
733+
"version": "1.0.0",
734+
"user": "",
735+
"template": "compose"
736+
}`, baseName)
737+
composeYAML := `version: "3.9"
738+
739+
# =============================================================================
740+
# Redc Compose - 多云编排配置
741+
# =============================================================================
742+
#
743+
# 使用 redc compose up redc-compose.yaml 启动编排
744+
# 使用 redc compose down -f redc-compose.yaml 销毁环境
745+
# 使用 redc compose config redc-compose.yaml 预览配置
746+
#
747+
748+
services:
749+
750+
# 示例服务 - 修改 image 为你的模板路径 (如 aliyun/ecs)
751+
# my_server:
752+
# image: aliyun/ecs
753+
# container_name: my_ecs_instance
754+
# environment:
755+
# - instance_type=ecs.e-c1m2.large
756+
# - password=YourPassword123
757+
# command: |
758+
# echo "实例初始化完成"
759+
# uptime
760+
`
761+
return map[string]string{
762+
"case.json": composeCaseJSON,
763+
"redc-compose.yaml": composeYAML,
764+
}
765+
default: // backward compat — treat as preset
699766
return map[string]string{
700-
"case.json": caseJSON,
767+
"case.json": makeCaseJSON("preset"),
701768
"main.tf": mainTF,
702769
"variables.tf": variablesTF,
703770
"outputs.tf": outputsTF,

frontend/src/components/LocalTemplates/LocalTemplates.svelte

Lines changed: 46 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -286,17 +286,19 @@
286286
* Determine template type based on path and properties
287287
*/
288288
function getTemplateType(tmpl) {
289-
const name = tmpl.name || '';
290-
291-
if (name.includes('base-templates/')) {
292-
return 'custom';
293-
}
294-
if (name.includes('userdata-templates/')) {
295-
return 'userdata';
296-
}
297-
if (name.includes('compose-templates/')) {
298-
return 'compose';
289+
// Prefer the template type field from case.json
290+
if (tmpl.template) {
291+
const t2 = tmpl.template;
292+
if (t2 === 'base') return 'custom';
293+
if (t2 === 'userdata') return 'userdata';
294+
if (t2 === 'compose') return 'compose';
295+
if (t2 === 'preset') return 'preset';
299296
}
297+
// Fallback: detect from path name
298+
const name = tmpl.name || '';
299+
if (name.includes('base-templates/')) return 'custom';
300+
if (name.includes('userdata-templates/')) return 'userdata';
301+
if (name.includes('compose-templates/')) return 'compose';
300302
return 'preset';
301303
}
302304
@@ -334,7 +336,7 @@
334336
let exportMessage = $state('');
335337
336338
// Create template dialog state
337-
let createTemplateDialog = $state({ show: false, name: '', scaffold: 'blank', loading: false, error: '' });
339+
let createTemplateDialog = $state({ show: false, name: '', scaffold: 'preset', loading: false, error: '' });
338340
339341
// File add inline input state (in templateEditor sidebar)
340342
let addingFile = $state({ show: false, name: '' });
@@ -410,11 +412,11 @@
410412
// ============================================================================
411413
412414
function showCreateTemplateDialog() {
413-
createTemplateDialog = { show: true, name: '', scaffold: 'blank', loading: false, error: '' };
415+
createTemplateDialog = { show: true, name: '', scaffold: 'preset', loading: false, error: '' };
414416
}
415417
416418
function cancelCreateTemplate() {
417-
createTemplateDialog = { show: false, name: '', scaffold: 'blank', loading: false, error: '' };
419+
createTemplateDialog = { show: false, name: '', scaffold: 'preset', loading: false, error: '' };
418420
}
419421
420422
async function confirmCreateTemplate() {
@@ -435,7 +437,7 @@
435437
createTemplateDialog = { ...createTemplateDialog, loading: true, error: '' };
436438
try {
437439
await CreateLocalTemplate(name, createTemplateDialog.scaffold);
438-
createTemplateDialog = { show: false, name: '', scaffold: 'blank', loading: false, error: '' };
440+
createTemplateDialog = { show: false, name: '', scaffold: 'preset', loading: false, error: '' };
439441
await loadLocalTemplates();
440442
// Auto-open editor for the new template
441443
const newTmpl = localTemplates.find(t => t.name === name);
@@ -1157,21 +1159,42 @@
11571159
onkeydown={(e) => { if (e.key === 'Enter') confirmCreateTemplate(); }}
11581160
/>
11591161

1160-
<label class="block text-[12px] font-medium text-gray-500 mb-2">{t.scaffoldType || '脚手架类型'}</label>
1162+
<label class="block text-[12px] font-medium text-gray-500 mb-2">{t.scaffoldType || '模板类型'}</label>
11611163
<div class="grid grid-cols-2 gap-2">
11621164
<button
1163-
class="px-3 py-2.5 text-left rounded-lg border transition-colors {createTemplateDialog.scaffold === 'blank' ? 'border-emerald-500 bg-emerald-50 text-emerald-700' : 'border-gray-200 text-gray-600 hover:bg-gray-50'}"
1164-
onclick={() => createTemplateDialog = { ...createTemplateDialog, scaffold: 'blank' }}
1165+
class="px-3 py-2.5 text-left rounded-lg border transition-colors {createTemplateDialog.scaffold === 'preset' ? 'border-emerald-500 bg-emerald-50 text-emerald-700' : 'border-gray-200 text-gray-600 hover:bg-gray-50'}"
1166+
onclick={() => createTemplateDialog = { ...createTemplateDialog, scaffold: 'preset' }}
1167+
>
1168+
<div class="text-[13px] font-medium">{t.scaffoldPreset || '预定义模板'}</div>
1169+
<div class="text-[11px] mt-0.5 opacity-70">{t.scaffoldPresetDesc || 'Terraform 基础骨架'}</div>
1170+
</button>
1171+
<button
1172+
class="px-3 py-2.5 text-left rounded-lg border transition-colors {createTemplateDialog.scaffold === 'preset-userdata' ? 'border-emerald-500 bg-emerald-50 text-emerald-700' : 'border-gray-200 text-gray-600 hover:bg-gray-50'}"
1173+
onclick={() => createTemplateDialog = { ...createTemplateDialog, scaffold: 'preset-userdata' }}
1174+
>
1175+
<div class="text-[13px] font-medium">{t.scaffoldPresetUserdata || '预定义 + Userdata'}</div>
1176+
<div class="text-[11px] mt-0.5 opacity-70">{t.scaffoldPresetUserdataDesc || '含初始化脚本文件'}</div>
1177+
</button>
1178+
<button
1179+
class="px-3 py-2.5 text-left rounded-lg border transition-colors {createTemplateDialog.scaffold === 'base' ? 'border-emerald-500 bg-emerald-50 text-emerald-700' : 'border-gray-200 text-gray-600 hover:bg-gray-50'}"
1180+
onclick={() => createTemplateDialog = { ...createTemplateDialog, scaffold: 'base' }}
1181+
>
1182+
<div class="text-[13px] font-medium">{t.scaffoldBase || '自定义模板'}</div>
1183+
<div class="text-[11px] mt-0.5 opacity-70">{t.scaffoldBaseDesc || '自定义部署场景'}</div>
1184+
</button>
1185+
<button
1186+
class="px-3 py-2.5 text-left rounded-lg border transition-colors {createTemplateDialog.scaffold === 'userdata' ? 'border-emerald-500 bg-emerald-50 text-emerald-700' : 'border-gray-200 text-gray-600 hover:bg-gray-50'}"
1187+
onclick={() => createTemplateDialog = { ...createTemplateDialog, scaffold: 'userdata' }}
11651188
>
1166-
<div class="text-[13px] font-medium">{t.scaffoldBlank || '空白模板'}</div>
1167-
<div class="text-[11px] mt-0.5 opacity-70">{t.scaffoldBlankDesc || '基础注释,自由编写'}</div>
1189+
<div class="text-[13px] font-medium">{t.scaffoldUserdata || 'Userdata 模板'}</div>
1190+
<div class="text-[11px] mt-0.5 opacity-70">{t.scaffoldUserdataDesc || '仅含初始化脚本'}</div>
11681191
</button>
11691192
<button
1170-
class="px-3 py-2.5 text-left rounded-lg border transition-colors {createTemplateDialog.scaffold === 'with-userdata' ? 'border-emerald-500 bg-emerald-50 text-emerald-700' : 'border-gray-200 text-gray-600 hover:bg-gray-50'}"
1171-
onclick={() => createTemplateDialog = { ...createTemplateDialog, scaffold: 'with-userdata' }}
1193+
class="px-3 py-2.5 text-left rounded-lg border transition-colors col-span-2 {createTemplateDialog.scaffold === 'compose' ? 'border-emerald-500 bg-emerald-50 text-emerald-700' : 'border-gray-200 text-gray-600 hover:bg-gray-50'}"
1194+
onclick={() => createTemplateDialog = { ...createTemplateDialog, scaffold: 'compose' }}
11721195
>
1173-
<div class="text-[13px] font-medium">{t.scaffoldUserdata || '含 Userdata'}</div>
1174-
<div class="text-[11px] mt-0.5 opacity-70">{t.scaffoldUserdataDesc || '包含初始化脚本文件'}</div>
1196+
<div class="text-[13px] font-medium">{t.scaffoldCompose || 'Compose 模板'}</div>
1197+
<div class="text-[11px] mt-0.5 opacity-70">{t.scaffoldComposeDesc || '多云编排部署'}</div>
11751198
</button>
11761199
</div>
11771200

frontend/src/lib/i18n.js

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -115,9 +115,12 @@ export const i18n = {
115115
createTemplate: '新建模板', createTemplateHint: '创建一个新的 Terraform 模板',
116116
templateName: '模板名称', templateNamePlaceholder: '例如: my-template 或 myteam/ecs',
117117
templateNameRequired: '请输入模板名称', templateNameInvalid: '名称只能包含字母、数字、-、_、/',
118-
scaffoldType: '脚手架类型',
119-
scaffoldBlank: '空白模板', scaffoldBlankDesc: '基础注释,自由编写',
120-
scaffoldUserdata: '含 Userdata', scaffoldUserdataDesc: '包含初始化脚本文件',
118+
scaffoldType: '模板类型',
119+
scaffoldPreset: '预定义模板', scaffoldPresetDesc: 'Terraform 基础骨架',
120+
scaffoldPresetUserdata: '预定义 + Userdata', scaffoldPresetUserdataDesc: '含初始化脚本文件',
121+
scaffoldBase: '自定义模板', scaffoldBaseDesc: '自定义部署场景',
122+
scaffoldUserdata: 'Userdata 模板', scaffoldUserdataDesc: '仅含初始化脚本',
123+
scaffoldCompose: 'Compose 模板', scaffoldComposeDesc: '多云编排部署',
121124
create: '创建',
122125
addFile: '新建文件', newFileName: '文件名(如 main.tf)', confirm: '确认',
123126
deleteFile: '删除文件', deleteFileTitle: '删除文件', deleteFileHint: '确定要删除此文件吗?此操作不可撤销。',
@@ -457,9 +460,12 @@ export const i18n = {
457460
createTemplate: 'New Template', createTemplateHint: 'Create a new Terraform template',
458461
templateName: 'Template Name', templateNamePlaceholder: 'e.g. my-template or myteam/ecs',
459462
templateNameRequired: 'Please enter a template name', templateNameInvalid: 'Name can only contain letters, digits, -, _, /',
460-
scaffoldType: 'Scaffold Type',
461-
scaffoldBlank: 'Blank', scaffoldBlankDesc: 'Comments only, write freely',
462-
scaffoldUserdata: 'With Userdata', scaffoldUserdataDesc: 'Includes init script file',
463+
scaffoldType: 'Template Type',
464+
scaffoldPreset: 'Preset', scaffoldPresetDesc: 'Terraform scaffold',
465+
scaffoldPresetUserdata: 'Preset + Userdata', scaffoldPresetUserdataDesc: 'With init script file',
466+
scaffoldBase: 'Custom', scaffoldBaseDesc: 'Custom deployment scenario',
467+
scaffoldUserdata: 'Userdata', scaffoldUserdataDesc: 'Init script only',
468+
scaffoldCompose: 'Compose', scaffoldComposeDesc: 'Multi-cloud orchestration',
463469
create: 'Create',
464470
addFile: 'New File', newFileName: 'Filename (e.g. main.tf)', confirm: 'Confirm',
465471
deleteFile: 'Delete File', deleteFileTitle: 'Delete File', deleteFileHint: 'Are you sure? This action cannot be undone.',

0 commit comments

Comments
 (0)