diff --git a/app/Module/Apps/Apps.php b/app/Module/Apps/Apps.php index 8deb1fda3..24dcc7f88 100644 --- a/app/Module/Apps/Apps.php +++ b/app/Module/Apps/Apps.php @@ -9,6 +9,24 @@ use Symfony\Component\Yaml\Exception\ParseException; class Apps { + // 受保护的服务名称列表 + protected static array $protectedServiceNames = [ + 'php', + 'nginx', + 'redis', + 'mariadb', + 'office', + 'fileview', + 'drawio-webapp', + 'drawio-expont', + 'minder', + 'approve', + 'ai', + 'okr', + 'face', + 'search', + ]; + /** * 执行docker-compose up命令 * @param string $appName @@ -18,6 +36,8 @@ class Apps */ public static function dockerComposeUp(string $appName, string $version = 'latest', string $command = 'up'): array { + $result = []; + // 获取版本信息 $versions = self::getAvailableVersions($appName); if (empty($versions)) { @@ -37,32 +57,41 @@ class Apps file_put_contents($versionInfo['base_dir'] . '/latest', $versionInfo['version']); // 生成docker-compose.yml文件 - $result = self::generateDockerComposeYml($versionInfo['compose_file'], [ + $res = self::generateDockerComposeYml($versionInfo['compose_file'], [ 'PROXY_PORT' => '33062', // todo 参数自定义 ]); - if (!$result) { - return Base::retError('生成docker-compose.yml失败'); + if (Base::isError($res)) { + return $res; } + $result['generate'] = $res['data']; // 执行docker-compose命令 - $url = "http://host.docker.internal:" . env("APPS_PORT") . "/apps/{$command}/{$appName}"; - $extra = [ - 'Content-Type' => 'application/json', - 'Authorization' => 'Bearer ' . env('APP_KEY'), - ]; - $res = Ihttp::ihttp_request($url, [], $extra); + $res = self::curl("apps/{$command}/{$appName}"); if (Base::isError($res)) { - return Base::retError("请求错误", $res); + return $res; } + $result['compose'] = $res['data']; - // 解析响应 - $resData = Base::json2array($res['data']); - if ($resData['code'] != 200) { - return Base::retError("请求失败", $resData); + // nginx配置文件处理 + $nginxFile = $versionInfo['path'] . '/nginx.conf'; + $nginxTarget = base_path('docker/nginx/apps/' . $appName . '.conf'); + if (file_exists($nginxTarget)) { + unlink($nginxTarget); + } + if (file_exists($nginxFile)) { + if ($command === 'up') { + copy($nginxFile, $nginxTarget); + } + // 重启nginx + $res = self::curl("nginx/reload"); + if (Base::isError($res)) { + return $res; + } + $result['nginx'] = $res['data']; } // 返回结果 - return Base::retSuccess("success", $resData['data']); + return Base::retSuccess("success", $result); } /** @@ -78,18 +107,17 @@ class Apps /** * 生成docker-compose.yml文件配置 - * * @param string $filePath docker-compose.yml文件路径 * @param array $params 可选参数,替换docker-compose.yml中的${XXX}变量 - * @return bool 是否生成成功 + * @return array */ - private static function generateDockerComposeYml(string $filePath, array $params = []): bool + private static function generateDockerComposeYml(string $filePath, array $params = []): array { // 应用名称 $appName = basename(dirname($filePath, 2)); // 服务名称 - $serviceName = 'dootask-app-' . strtolower(preg_replace('/(? true]; + // 检查服务名称是否被保护 + foreach ($content['services'] as $name => $service) { + if (in_array($name, self::$protectedServiceNames)) { + return Base::retError('服务名称 "' . $name . '" 被保护,不能使用'); + } + } + foreach ($content['services'] as &$service) { // 确保所有服务都有网络配置 $service['networks'] = [$networkName]; @@ -137,15 +172,17 @@ class Apps }, $yamlContent); // 保存文件 - file_put_contents($savePath, $yamlContent); + if (file_put_contents($savePath, $yamlContent) === false) { + return Base::retError('无法写入配置文件'); + } - return true; - } catch (ParseException) { + // 返回成功 + return Base::retSuccess('success', [ + 'file' => $savePath, + ]); + } catch (ParseException $e) { // YAML解析错误 - return false; - } catch (\Exception) { - // 其他错误 - return false; + return Base::retError('YAML parsing error:' . $e->getMessage()); } } @@ -228,4 +265,33 @@ class Apps return $versions; } + + /** + * 执行curl请求 + * @param $path + * @return array + */ + private static function curl($path): array + { + $url = "http://host.docker.internal:" . env("APPS_PORT") . "/{$path}"; + $extra = [ + 'Content-Type' => 'application/json', + 'Authorization' => 'Bearer ' . env('APP_KEY'), + ]; + + // 执行请求 + $res = Ihttp::ihttp_request($url, [], $extra); + if (Base::isError($res)) { + return Base::retError("请求错误", $res); + } + + // 解析响应 + $resData = Base::json2array($res['data']); + if ($resData['code'] != 200) { + return Base::retError("请求失败", $resData); + } + + // 返回结果 + return Base::retSuccess("success", $resData['data']); + } } diff --git a/docker/nginx/apps/.gitignore b/docker/nginx/apps/.gitignore new file mode 100644 index 000000000..d6b7ef32c --- /dev/null +++ b/docker/nginx/apps/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/docker/nginx/default.conf b/docker/nginx/default.conf index 2f9e8fe65..50dd58c58 100644 --- a/docker/nginx/default.conf +++ b/docker/nginx/default.conf @@ -79,6 +79,7 @@ server { } include /etc/nginx/conf.d/location/*.conf; + include /etc/nginx/conf.d/apps/*.conf; } include /etc/nginx/conf.d/conf.d/*.conf; diff --git a/language/original-api.txt b/language/original-api.txt index d34b20251..1bf736316 100644 --- a/language/original-api.txt +++ b/language/original-api.txt @@ -848,3 +848,11 @@ AI机器人不存在 「(*)」移动至「(*)」 设备不存在或已被删除 删除附件:(*) + +Docker compose文件缺少services配置 +服务名称 "(*)" 被保护,不能使用 +无法写入配置文件 +请求错误 +请求失败 +没有可用的版本 +没有找到版本(*)