mirror of
https://github.com/acepanel/panel.git
synced 2026-02-04 03:07:20 +08:00
feat: 支持更多项目类型
This commit is contained in:
@@ -122,8 +122,8 @@ func (r *environmentRepo) InstalledVersion(typ, slug string) string {
|
||||
// go version go1.21.0 linux/amd64 -> 1.21.0
|
||||
version, err = shell.Exec(filepath.Join(basePath, "bin", "go") + " version | awk '{print $3}' | sed 's/go//'")
|
||||
case "java":
|
||||
// openjdk version "17.0.8" 2023-07-18 LTS -> 17.0.8
|
||||
version, err = shell.Exec(filepath.Join(basePath, "bin", "java") + " -version 2>&1 | head -n 1 | awk -F'\"' '{print $2}'")
|
||||
// OpenJDK Runtime Environment Corretto-21.0.9.11.1 (build 21.0.9+11-LTS) -> 21.0.9.11.1
|
||||
version, err = shell.Exec(filepath.Join(basePath, "bin", "java") + ` -version 2>&1 | sed -n 's/.*Corretto-\([0-9.]*\).*/\1/p' | head -n 1`)
|
||||
case "nodejs":
|
||||
// v20.10.0 -> 20.10.0
|
||||
version, err = shell.Exec(filepath.Join(basePath, "bin", "node") + " -v | sed 's/v//'")
|
||||
|
||||
@@ -225,14 +225,44 @@ func (s *HomeService) InstalledEnvironment(w http.ResponseWriter, r *http.Reques
|
||||
mysqlInstalled, _ := s.appRepo.IsInstalled("slug IN ?", []string{"mysql", "mariadb", "percona"})
|
||||
postgresqlInstalled, _ := s.appRepo.IsInstalled("slug = ?", "postgresql")
|
||||
|
||||
// Go 版本
|
||||
var goData []types.LV
|
||||
for _, slug := range s.environmentRepo.InstalledSlugs("go") {
|
||||
ver := s.environmentRepo.InstalledVersion("go", slug)
|
||||
goData = append(goData, types.LV{Value: slug, Label: fmt.Sprintf("Go %s", ver)})
|
||||
}
|
||||
|
||||
// Java 版本
|
||||
var javaData []types.LV
|
||||
for _, slug := range s.environmentRepo.InstalledSlugs("java") {
|
||||
ver := s.environmentRepo.InstalledVersion("java", slug)
|
||||
javaData = append(javaData, types.LV{Value: slug, Label: fmt.Sprintf("Java %s", ver)})
|
||||
}
|
||||
|
||||
// Node.js 版本
|
||||
var nodejsData []types.LV
|
||||
for _, slug := range s.environmentRepo.InstalledSlugs("nodejs") {
|
||||
ver := s.environmentRepo.InstalledVersion("nodejs", slug)
|
||||
nodejsData = append(nodejsData, types.LV{Value: slug, Label: fmt.Sprintf("Node.js %s", ver)})
|
||||
}
|
||||
|
||||
// PHP 版本
|
||||
var phpData []types.LVInt
|
||||
var dbData []types.LV
|
||||
dbData = append(dbData, types.LV{Value: "0", Label: s.t.Get("Not used")})
|
||||
for _, slug := range s.environmentRepo.InstalledSlugs("php") {
|
||||
ver := s.environmentRepo.InstalledVersion("php", slug)
|
||||
phpData = append(phpData, types.LVInt{Value: cast.ToInt(slug), Label: fmt.Sprintf("PHP %s", ver)})
|
||||
}
|
||||
|
||||
// Python 版本
|
||||
var pythonData []types.LV
|
||||
for _, slug := range s.environmentRepo.InstalledSlugs("python") {
|
||||
ver := s.environmentRepo.InstalledVersion("python", slug)
|
||||
pythonData = append(pythonData, types.LV{Value: slug, Label: fmt.Sprintf("Python %s", ver)})
|
||||
}
|
||||
|
||||
// 数据库
|
||||
var dbData []types.LV
|
||||
dbData = append(dbData, types.LV{Value: "0", Label: s.t.Get("Not used")})
|
||||
if mysqlInstalled {
|
||||
dbData = append(dbData, types.LV{Value: "mysql", Label: "MySQL"})
|
||||
}
|
||||
@@ -243,7 +273,11 @@ func (s *HomeService) InstalledEnvironment(w http.ResponseWriter, r *http.Reques
|
||||
webserver, _ := s.settingRepo.Get(biz.SettingKeyWebserver)
|
||||
Success(w, chix.M{
|
||||
"webserver": webserver,
|
||||
"go": goData,
|
||||
"java": javaData,
|
||||
"nodejs": nodejsData,
|
||||
"php": phpData,
|
||||
"python": pythonData,
|
||||
"db": dbData,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -10,6 +10,36 @@ const type = defineModel<string>('type', { type: String, required: true })
|
||||
|
||||
const { $gettext } = useGettext()
|
||||
|
||||
// Go 运行模式
|
||||
const goModes = [
|
||||
{ label: $gettext('Source Code'), value: 'source' },
|
||||
{ label: $gettext('Binary'), value: 'binary' }
|
||||
]
|
||||
|
||||
// Java 框架预设
|
||||
const javaFrameworks = [
|
||||
{ label: $gettext('Custom'), value: 'custom', command: '' },
|
||||
{ label: 'Spring Boot (JAR)', value: 'spring-boot-jar', command: '-jar app.jar' },
|
||||
{ label: 'Spring Boot (WAR)', value: 'spring-boot-war', command: '-jar app.war' },
|
||||
{ label: 'Quarkus', value: 'quarkus', command: '-jar quarkus-run.jar' },
|
||||
{ label: 'Micronaut', value: 'micronaut', command: '-jar app.jar' },
|
||||
{ label: 'Vert.x', value: 'vertx', command: '-jar app.jar' },
|
||||
{ label: 'Dropwizard', value: 'dropwizard', command: 'server config.yml' }
|
||||
]
|
||||
|
||||
// Node.js 框架预设
|
||||
const nodejsFrameworks = [
|
||||
{ label: $gettext('Custom'), value: 'custom', command: '' },
|
||||
{ label: 'Express', value: 'express', command: 'app.js' },
|
||||
{ label: 'Koa', value: 'koa', command: 'app.js' },
|
||||
{ label: 'Fastify', value: 'fastify', command: 'app.js' },
|
||||
{ label: 'NestJS', value: 'nestjs', command: 'dist/main.js' },
|
||||
{ label: 'Next.js', value: 'nextjs', command: 'node_modules/.bin/next start' },
|
||||
{ label: 'Nuxt.js', value: 'nuxtjs', command: 'node_modules/.bin/nuxt start' },
|
||||
{ label: 'Hapi', value: 'hapi', command: 'server.js' },
|
||||
{ label: 'AdonisJS', value: 'adonisjs', command: 'server.js' }
|
||||
]
|
||||
|
||||
// PHP 框架预设
|
||||
const phpFrameworks = [
|
||||
{ label: $gettext('Custom'), value: 'custom', command: '' },
|
||||
@@ -22,6 +52,18 @@ const phpFrameworks = [
|
||||
{ label: 'RoadRunner', value: 'roadrunner', command: 'vendor/bin/rr serve' }
|
||||
]
|
||||
|
||||
// Python 框架预设
|
||||
const pythonFrameworks = [
|
||||
{ label: $gettext('Custom'), value: 'custom', command: '' },
|
||||
{ label: 'Django', value: 'django', command: 'manage.py runserver 0.0.0.0:8000' },
|
||||
{ label: 'Flask', value: 'flask', command: '-m flask run --host=0.0.0.0' },
|
||||
{ label: 'FastAPI (Uvicorn)', value: 'fastapi', command: '-m uvicorn main:app --host 0.0.0.0' },
|
||||
{ label: 'Tornado', value: 'tornado', command: 'app.py' },
|
||||
{ label: 'Sanic', value: 'sanic', command: '-m sanic server.app --host=0.0.0.0' },
|
||||
{ label: 'aiohttp', value: 'aiohttp', command: 'app.py' },
|
||||
{ label: 'Gunicorn', value: 'gunicorn', command: '-m gunicorn -w 4 app:app' }
|
||||
]
|
||||
|
||||
const createModel = ref({
|
||||
name: '',
|
||||
type: '',
|
||||
@@ -31,46 +73,163 @@ const createModel = ref({
|
||||
user: 'www'
|
||||
})
|
||||
|
||||
// Go 特有字段
|
||||
const goOptions = ref({
|
||||
mode: 'source' as string,
|
||||
version: '' as string,
|
||||
entryFile: 'main.go' as string
|
||||
})
|
||||
|
||||
// Java 特有字段
|
||||
const javaOptions = ref({
|
||||
version: '' as string,
|
||||
framework: 'custom'
|
||||
})
|
||||
|
||||
// Node.js 特有字段
|
||||
const nodejsOptions = ref({
|
||||
version: '' as string,
|
||||
framework: 'custom'
|
||||
})
|
||||
|
||||
// PHP 特有字段
|
||||
const phpOptions = ref({
|
||||
version: null as number | null,
|
||||
framework: 'custom'
|
||||
})
|
||||
|
||||
// Python 特有字段
|
||||
const pythonOptions = ref({
|
||||
version: '' as string,
|
||||
framework: 'custom'
|
||||
})
|
||||
|
||||
const showPathSelector = ref(false)
|
||||
const pathSelectorPath = ref('/opt/ace/projects')
|
||||
|
||||
const { data: installedEnvironment } = useRequest(home.installedEnvironment, {
|
||||
initialData: {
|
||||
php: []
|
||||
go: [],
|
||||
java: [],
|
||||
nodejs: [],
|
||||
php: [],
|
||||
python: []
|
||||
}
|
||||
})
|
||||
|
||||
// Go 版本选项
|
||||
const goVersionOptions = computed(() => {
|
||||
return installedEnvironment.value?.go || []
|
||||
})
|
||||
|
||||
// Java 版本选项
|
||||
const javaVersionOptions = computed(() => {
|
||||
return installedEnvironment.value?.java || []
|
||||
})
|
||||
|
||||
// Node.js 版本选项
|
||||
const nodejsVersionOptions = computed(() => {
|
||||
return installedEnvironment.value?.nodejs || []
|
||||
})
|
||||
|
||||
// PHP 版本选项
|
||||
const phpVersionOptions = computed(() => {
|
||||
return installedEnvironment.value?.php || []
|
||||
})
|
||||
|
||||
// 根据 PHP 版本和框架生成启动命令
|
||||
// Python 版本选项
|
||||
const pythonVersionOptions = computed(() => {
|
||||
return installedEnvironment.value?.python || []
|
||||
})
|
||||
|
||||
// 根据语言版本和框架生成启动命令
|
||||
const generateCommand = () => {
|
||||
if (type.value !== 'php' || !phpOptions.value.version) {
|
||||
return
|
||||
switch (type.value) {
|
||||
case 'go': {
|
||||
if (goOptions.value.mode === 'source') {
|
||||
// 源码模式
|
||||
if (!goOptions.value.version || !goOptions.value.entryFile) return
|
||||
const goBin = `go${goOptions.value.version}`
|
||||
createModel.value.exec_start = `${goBin} run ${goOptions.value.entryFile}`
|
||||
} else {
|
||||
// 二进制模式
|
||||
const rootDir = createModel.value.root_dir || `/opt/ace/projects/${createModel.value.name || 'project'}`
|
||||
createModel.value.exec_start = `${rootDir}/main`
|
||||
}
|
||||
break
|
||||
}
|
||||
case 'java': {
|
||||
if (!javaOptions.value.version) return
|
||||
const framework = javaFrameworks.find((f) => f.value === javaOptions.value.framework)
|
||||
if (!framework || framework.value === 'custom') return
|
||||
const javaBin = `java${javaOptions.value.version}`
|
||||
createModel.value.exec_start = `${javaBin} ${framework.command}`
|
||||
break
|
||||
}
|
||||
case 'nodejs': {
|
||||
if (!nodejsOptions.value.version) return
|
||||
const framework = nodejsFrameworks.find((f) => f.value === nodejsOptions.value.framework)
|
||||
if (!framework || framework.value === 'custom') return
|
||||
const nodeBin = `node${nodejsOptions.value.version}`
|
||||
createModel.value.exec_start = `${nodeBin} ${framework.command}`
|
||||
break
|
||||
}
|
||||
case 'php': {
|
||||
if (!phpOptions.value.version) return
|
||||
const framework = phpFrameworks.find((f) => f.value === phpOptions.value.framework)
|
||||
if (!framework || framework.value === 'custom') return
|
||||
const phpBin = `php${phpOptions.value.version}`
|
||||
createModel.value.exec_start = `${phpBin} ${framework.command}`
|
||||
break
|
||||
}
|
||||
case 'python': {
|
||||
if (!pythonOptions.value.version) return
|
||||
const framework = pythonFrameworks.find((f) => f.value === pythonOptions.value.framework)
|
||||
if (!framework || framework.value === 'custom') return
|
||||
const pythonBin = `python${pythonOptions.value.version}`
|
||||
createModel.value.exec_start = `${pythonBin} ${framework.command}`
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
const framework = phpFrameworks.find((f) => f.value === phpOptions.value.framework)
|
||||
if (!framework || framework.value === 'custom') {
|
||||
return
|
||||
}
|
||||
|
||||
const phpBin = `php${phpOptions.value.version}`
|
||||
createModel.value.exec_start = `${phpBin} ${framework.command}`
|
||||
}
|
||||
|
||||
// 监听 Go 选项变化
|
||||
watch(
|
||||
() => [goOptions.value.mode, goOptions.value.version, goOptions.value.entryFile, createModel.value.root_dir, createModel.value.name],
|
||||
() => {
|
||||
if (type.value === 'go') generateCommand()
|
||||
}
|
||||
)
|
||||
|
||||
// 监听 Java 版本和框架变化
|
||||
watch(
|
||||
() => [javaOptions.value.version, javaOptions.value.framework],
|
||||
() => {
|
||||
if (type.value === 'java') generateCommand()
|
||||
}
|
||||
)
|
||||
|
||||
// 监听 Node.js 版本和框架变化
|
||||
watch(
|
||||
() => [nodejsOptions.value.version, nodejsOptions.value.framework],
|
||||
() => {
|
||||
if (type.value === 'nodejs') generateCommand()
|
||||
}
|
||||
)
|
||||
|
||||
// 监听 PHP 版本和框架变化
|
||||
watch(
|
||||
() => [phpOptions.value.version, phpOptions.value.framework],
|
||||
() => {
|
||||
generateCommand()
|
||||
if (type.value === 'php') generateCommand()
|
||||
}
|
||||
)
|
||||
|
||||
// 监听 Python 版本和框架变化
|
||||
watch(
|
||||
() => [pythonOptions.value.version, pythonOptions.value.framework],
|
||||
() => {
|
||||
if (type.value === 'python') generateCommand()
|
||||
}
|
||||
)
|
||||
|
||||
@@ -103,10 +262,27 @@ const handleCreate = async () => {
|
||||
exec_start: '',
|
||||
user: 'www'
|
||||
}
|
||||
goOptions.value = {
|
||||
mode: 'source',
|
||||
version: '',
|
||||
entryFile: 'main.go'
|
||||
}
|
||||
javaOptions.value = {
|
||||
version: '',
|
||||
framework: 'custom'
|
||||
}
|
||||
nodejsOptions.value = {
|
||||
version: '',
|
||||
framework: 'custom'
|
||||
}
|
||||
phpOptions.value = {
|
||||
version: null,
|
||||
framework: 'custom'
|
||||
}
|
||||
pythonOptions.value = {
|
||||
version: '',
|
||||
framework: 'custom'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -114,7 +290,11 @@ const handleCreate = async () => {
|
||||
const modalTitle = computed(() => {
|
||||
const titles: Record<string, string> = {
|
||||
general: $gettext('Create General Project'),
|
||||
php: $gettext('Create PHP Project')
|
||||
go: $gettext('Create Go Project'),
|
||||
java: $gettext('Create Java Project'),
|
||||
nodejs: $gettext('Create Node.js Project'),
|
||||
php: $gettext('Create PHP Project'),
|
||||
python: $gettext('Create Python Project')
|
||||
}
|
||||
return titles[type.value] || $gettext('Create Project')
|
||||
})
|
||||
@@ -161,6 +341,99 @@ const modalTitle = computed(() => {
|
||||
</n-input-group>
|
||||
</n-form-item>
|
||||
|
||||
<!-- Go 类型特有字段 -->
|
||||
<template v-if="type === 'go'">
|
||||
<n-form-item :label="$gettext('Run Mode')">
|
||||
<n-radio-group v-model:value="goOptions.mode">
|
||||
<n-radio-button
|
||||
v-for="mode in goModes"
|
||||
:key="mode.value"
|
||||
:value="mode.value"
|
||||
:label="mode.label"
|
||||
/>
|
||||
</n-radio-group>
|
||||
</n-form-item>
|
||||
|
||||
<!-- 源码模式 -->
|
||||
<template v-if="goOptions.mode === 'source'">
|
||||
<n-row :gutter="[24, 0]">
|
||||
<n-col :span="12">
|
||||
<n-form-item :label="$gettext('Go Version')">
|
||||
<n-select
|
||||
v-model:value="goOptions.version"
|
||||
:options="goVersionOptions"
|
||||
:placeholder="$gettext('Select Go Version')"
|
||||
@keydown.enter.prevent
|
||||
/>
|
||||
</n-form-item>
|
||||
</n-col>
|
||||
<n-col :span="12">
|
||||
<n-form-item :label="$gettext('Entry File')">
|
||||
<n-input
|
||||
v-model:value="goOptions.entryFile"
|
||||
type="text"
|
||||
@keydown.enter.prevent
|
||||
:placeholder="$gettext('e.g., main.go, cmd/server/main.go')"
|
||||
/>
|
||||
</n-form-item>
|
||||
</n-col>
|
||||
</n-row>
|
||||
</template>
|
||||
|
||||
</template>
|
||||
|
||||
<!-- Java 类型特有字段 -->
|
||||
<template v-if="type === 'java'">
|
||||
<n-row :gutter="[24, 0]">
|
||||
<n-col :span="12">
|
||||
<n-form-item :label="$gettext('Java Version')">
|
||||
<n-select
|
||||
v-model:value="javaOptions.version"
|
||||
:options="javaVersionOptions"
|
||||
:placeholder="$gettext('Select Java Version')"
|
||||
@keydown.enter.prevent
|
||||
/>
|
||||
</n-form-item>
|
||||
</n-col>
|
||||
<n-col :span="12">
|
||||
<n-form-item :label="$gettext('Framework')">
|
||||
<n-select
|
||||
v-model:value="javaOptions.framework"
|
||||
:options="javaFrameworks"
|
||||
:placeholder="$gettext('Select Framework')"
|
||||
@keydown.enter.prevent
|
||||
/>
|
||||
</n-form-item>
|
||||
</n-col>
|
||||
</n-row>
|
||||
</template>
|
||||
|
||||
<!-- Node.js 类型特有字段 -->
|
||||
<template v-if="type === 'nodejs'">
|
||||
<n-row :gutter="[24, 0]">
|
||||
<n-col :span="12">
|
||||
<n-form-item :label="$gettext('Node.js Version')">
|
||||
<n-select
|
||||
v-model:value="nodejsOptions.version"
|
||||
:options="nodejsVersionOptions"
|
||||
:placeholder="$gettext('Select Node.js Version')"
|
||||
@keydown.enter.prevent
|
||||
/>
|
||||
</n-form-item>
|
||||
</n-col>
|
||||
<n-col :span="12">
|
||||
<n-form-item :label="$gettext('Framework')">
|
||||
<n-select
|
||||
v-model:value="nodejsOptions.framework"
|
||||
:options="nodejsFrameworks"
|
||||
:placeholder="$gettext('Select Framework')"
|
||||
@keydown.enter.prevent
|
||||
/>
|
||||
</n-form-item>
|
||||
</n-col>
|
||||
</n-row>
|
||||
</template>
|
||||
|
||||
<!-- PHP 类型特有字段 -->
|
||||
<template v-if="type === 'php'">
|
||||
<n-row :gutter="[24, 0]">
|
||||
@@ -187,6 +460,32 @@ const modalTitle = computed(() => {
|
||||
</n-row>
|
||||
</template>
|
||||
|
||||
<!-- Python 类型特有字段 -->
|
||||
<template v-if="type === 'python'">
|
||||
<n-row :gutter="[24, 0]">
|
||||
<n-col :span="12">
|
||||
<n-form-item :label="$gettext('Python Version')">
|
||||
<n-select
|
||||
v-model:value="pythonOptions.version"
|
||||
:options="pythonVersionOptions"
|
||||
:placeholder="$gettext('Select Python Version')"
|
||||
@keydown.enter.prevent
|
||||
/>
|
||||
</n-form-item>
|
||||
</n-col>
|
||||
<n-col :span="12">
|
||||
<n-form-item :label="$gettext('Framework')">
|
||||
<n-select
|
||||
v-model:value="pythonOptions.framework"
|
||||
:options="pythonFrameworks"
|
||||
:placeholder="$gettext('Select Framework')"
|
||||
@keydown.enter.prevent
|
||||
/>
|
||||
</n-form-item>
|
||||
</n-col>
|
||||
</n-row>
|
||||
</template>
|
||||
|
||||
<n-form-item path="user" :label="$gettext('Run User')">
|
||||
<n-select
|
||||
v-model:value="createModel.user"
|
||||
|
||||
@@ -20,11 +20,11 @@ const editId = ref(0)
|
||||
<n-tabs v-model:value="currentTab" animated>
|
||||
<n-tab name="all" :tab="$gettext('All')" />
|
||||
<n-tab name="general" :tab="$gettext('General')" />
|
||||
<n-tab name="php" :tab="$gettext('PHP')" />
|
||||
<n-tab name="java" :tab="$gettext('Java')" />
|
||||
<n-tab name="go" :tab="$gettext('Go')" />
|
||||
<n-tab name="python" :tab="$gettext('Python')" />
|
||||
<n-tab name="java" :tab="$gettext('Java')" />
|
||||
<n-tab name="nodejs" :tab="$gettext('Node.js')" />
|
||||
<n-tab name="php" :tab="$gettext('PHP')" />
|
||||
<n-tab name="python" :tab="$gettext('Python')" />
|
||||
</n-tabs>
|
||||
</template>
|
||||
<list-view
|
||||
|
||||
Reference in New Issue
Block a user