mirror of
https://github.com/kuaifan/dootask.git
synced 2025-12-14 21:02:49 +00:00
no message
This commit is contained in:
parent
428db42140
commit
18ffad5de5
@ -304,15 +304,18 @@ class AppsController extends AbstractController
|
||||
* @apiGroup apps
|
||||
* @apiName logs
|
||||
*
|
||||
* @apiParam {String} app_name 应用名称
|
||||
* @apiParam {Number} [lines=50] 获取日志行数,默认50行
|
||||
* @apiParam {String} app_name 应用名称
|
||||
* @apiParam {Number} [lines=50] 获取日志行数,默认50行
|
||||
* @apiParam {Boolean} [simple=false] 是否只返回日志,默认false
|
||||
*
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
* @apiSuccess {Object} data 返回数据
|
||||
* @apiSuccess {String} data.name 应用名称
|
||||
* @apiSuccess {Object} data.config 应用配置信息
|
||||
* @apiSuccess {String} data.log 日志内容
|
||||
* @apiSuccess {String} data.name 应用名称
|
||||
* @apiSuccess {Object} data.config 应用配置信息
|
||||
* @apiSuccess {Object} data.versions 可用版本列表
|
||||
* @apiSuccess {Boolean} data.upgradeable 是否可升级
|
||||
* @apiSuccess {String} data.log 日志内容
|
||||
*/
|
||||
public function logs()
|
||||
{
|
||||
@ -320,13 +323,22 @@ class AppsController extends AbstractController
|
||||
//
|
||||
$appName = Request::input('app_name');
|
||||
$lines = intval(Request::input('lines', 50));
|
||||
$simple = Request::input('simple', false);
|
||||
|
||||
$logContent = implode("\n", Apps::getAppLog($appName, $lines));
|
||||
|
||||
return Base::retSuccess('success', [
|
||||
$data = [
|
||||
'name' => $appName,
|
||||
'config' => Apps::getAppConfig($appName),
|
||||
'log' => trim($logContent)
|
||||
]);
|
||||
];
|
||||
if (!$simple) {
|
||||
$config = Apps::getAppConfig($appName);
|
||||
$versions = Apps::getAvailableVersions($appName);
|
||||
$data['config'] = $config;
|
||||
$data['versions'] = $versions;
|
||||
$data['upgradeable'] = Apps::isUpgradeable($config, $versions);
|
||||
}
|
||||
|
||||
return Base::retSuccess('success', $data);
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,8 +8,11 @@ use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
class Apps
|
||||
{
|
||||
// 软件源列表URL
|
||||
const SOURCES_URL = 'https://appstore.dootask.com/';
|
||||
|
||||
// 受保护的服务名称列表
|
||||
protected static array $protectedServiceNames = [
|
||||
const PROTECTED_NAMES = [
|
||||
'php',
|
||||
'nginx',
|
||||
'redis',
|
||||
@ -18,13 +21,11 @@ class Apps
|
||||
'appstore',
|
||||
];
|
||||
|
||||
// 软件源列表URL
|
||||
protected static string $sourcesUrl = 'https://appstore.dootask.com/sources.list';
|
||||
|
||||
// 缓存键集合
|
||||
protected static array $cacheKeys = [
|
||||
'app_list' => 'appstore_app_list',
|
||||
'menu_items' => 'appstore_menu_items',
|
||||
const CACHE_KEYS = [
|
||||
'list' => 'appstore_app_list', // 应用列表缓存
|
||||
'menu' => 'appstore_menu_items', // 应用菜单缓存
|
||||
'installed' => 'appstore_installed', // 已安装应用缓存
|
||||
];
|
||||
|
||||
/**
|
||||
@ -36,22 +37,25 @@ class Apps
|
||||
public static function appList(bool $cache = true): array
|
||||
{
|
||||
if ($cache === false) {
|
||||
Cache::forget(self::$cacheKeys['app_list']);
|
||||
Cache::forget(self::CACHE_KEYS['list']);
|
||||
}
|
||||
$apps = Cache::remember(self::$cacheKeys['app_list'], now()->addHour(), function () {
|
||||
$apps = Cache::remember(self::CACHE_KEYS['list'], now()->addHour(), function () {
|
||||
$apps = [];
|
||||
$baseDir = base_path('docker/appstore/apps');
|
||||
$dirs = scandir($baseDir);
|
||||
foreach ($dirs as $dir) {
|
||||
// 跳过当前目录、父目录和隐藏文件
|
||||
if ($dir === '.' || $dir === '..' || str_starts_with($dir, '.')) {
|
||||
foreach ($dirs as $appName) {
|
||||
if ($appName === '.' || $appName === '..' || str_starts_with($appName, '.')) {
|
||||
continue;
|
||||
}
|
||||
$info = self::getAppInfo($appName);
|
||||
$config = self::getAppConfig($appName);
|
||||
$versions = self::getAvailableVersions($appName);
|
||||
$apps[] = [
|
||||
'name' => $dir,
|
||||
'info' => self::getAppInfo($dir),
|
||||
'config' => self::getAppConfig($dir),
|
||||
'versions' => self::getAvailableVersions($dir),
|
||||
'name' => $appName,
|
||||
'info' => $info,
|
||||
'config' => $config,
|
||||
'versions' => $versions,
|
||||
'upgradeable' => self::isUpgradeable($config, $versions),
|
||||
];
|
||||
}
|
||||
return $apps;
|
||||
@ -67,11 +71,15 @@ class Apps
|
||||
*/
|
||||
public static function appInfo(string $appName): array
|
||||
{
|
||||
$info = self::getAppInfo($appName);
|
||||
$config = self::getAppConfig($appName);
|
||||
$versions = self::getAvailableVersions($appName);
|
||||
return Base::retSuccess("success", [
|
||||
'name' => $appName,
|
||||
'info' => self::getAppInfo($appName),
|
||||
'config' => self::getAppConfig($appName),
|
||||
'versions' => self::getAvailableVersions($appName),
|
||||
'info' => $info,
|
||||
'config' => $config,
|
||||
'versions' => $versions,
|
||||
'upgradeable' => self::isUpgradeable($config, $versions),
|
||||
'document' => self::getAppDocument($appName),
|
||||
]);
|
||||
}
|
||||
@ -185,11 +193,6 @@ class Apps
|
||||
*/
|
||||
public static function dockerComposeFinalize(string $appName, string $status): array
|
||||
{
|
||||
// 清理缓存
|
||||
foreach (self::$cacheKeys as $key) {
|
||||
Cache::forget($key);
|
||||
}
|
||||
|
||||
// 获取当前应用信息
|
||||
$appInfo = self::getAppConfig($appName);
|
||||
|
||||
@ -362,13 +365,13 @@ class Apps
|
||||
public static function getAppMenuItems(bool $cache = true): array
|
||||
{
|
||||
if ($cache === false) {
|
||||
Cache::forget(self::$cacheKeys['menu_items']);
|
||||
Cache::forget(self::CACHE_KEYS['menu']);
|
||||
}
|
||||
$res = Cache::remember(self::$cacheKeys['menu_items'], now()->addHour(), function () {
|
||||
$res = Cache::remember(self::CACHE_KEYS['menu'], now()->addHour(), function () {
|
||||
return self::menuGetAll();
|
||||
});
|
||||
if (Base::isError($res)) {
|
||||
Cache::forget(self::$cacheKeys['menu_items']);
|
||||
Cache::forget(self::CACHE_KEYS['menu']);
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
@ -419,17 +422,30 @@ class Apps
|
||||
return Base::retSuccess("success", $allMenuItems);
|
||||
}
|
||||
|
||||
// 获取所有已安装的应用配置
|
||||
$installedApps = [];
|
||||
$dirs = scandir($baseDir);
|
||||
foreach ($dirs as $dir) {
|
||||
if ($dir === '.' || $dir === '..' || str_starts_with($dir, '.')) {
|
||||
foreach ($dirs as $appName) {
|
||||
if ($appName === '.' || $appName === '..' || str_starts_with($appName, '.')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!self::isInstalled($dir)) {
|
||||
$appConfig = self::getAppConfig($appName);
|
||||
if ($appConfig['status'] !== 'installed') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$appMenuItems = self::menuGetSingle($dir);
|
||||
$installedApps[$appName] = strtotime($appConfig['install_at'] ?? '');
|
||||
}
|
||||
|
||||
// 按安装时间排序应用
|
||||
uasort($installedApps, function ($timeA, $timeB) {
|
||||
return $timeB <=> $timeA; // 降序排列
|
||||
});
|
||||
|
||||
// 按排序后的顺序获取菜单
|
||||
foreach ($installedApps as $appName => $installAt) {
|
||||
$appMenuItems = self::menuGetSingle($appName);
|
||||
if (Base::isSuccess($appMenuItems)) {
|
||||
$allMenuItems = array_merge($allMenuItems, $appMenuItems['data']);
|
||||
}
|
||||
@ -570,6 +586,9 @@ class Apps
|
||||
mkdir($baseDir, 0755, true);
|
||||
}
|
||||
|
||||
// 清理缓存
|
||||
self::clearCache();
|
||||
|
||||
// 写入文件
|
||||
return (bool)file_put_contents(
|
||||
$configFile,
|
||||
@ -618,8 +637,16 @@ class Apps
|
||||
*/
|
||||
public static function isInstalled(string $appName): bool
|
||||
{
|
||||
$array = Base::json2array(Cache::get(self::CACHE_KEYS['installed'], []));
|
||||
if (isset($array[$appName])) {
|
||||
return $array[$appName];
|
||||
}
|
||||
|
||||
$appConfig = self::getAppConfig($appName);
|
||||
return $appConfig['status'] === 'installed';
|
||||
$array[$appName] = $appConfig['status'] === 'installed';
|
||||
Cache::put(self::CACHE_KEYS['installed'], Base::array2json($array));
|
||||
|
||||
return $array[$appName];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -630,21 +657,22 @@ class Apps
|
||||
public static function appListUpdate(): array
|
||||
{
|
||||
// 检查是否正在更新
|
||||
$cacheKey = 'appstore_update_running';
|
||||
if (Cache::has($cacheKey)) {
|
||||
$cacheTmp = 'appstore_update_running';
|
||||
if (Cache::has($cacheTmp)) {
|
||||
return Base::retError('应用列表正在更新中,请稍后再试');
|
||||
}
|
||||
$onFailure = function (string $message) use ($cacheKey) {
|
||||
Cache::forget($cacheKey);
|
||||
$onFailure = function (string $message) use ($cacheTmp) {
|
||||
Cache::forget($cacheTmp);
|
||||
return Base::retError($message);
|
||||
};
|
||||
$onSuccess = function (string $message, array $data = []) use ($cacheKey) {
|
||||
Cache::forget($cacheKey);
|
||||
$onSuccess = function (string $message, array $data = []) use ($cacheTmp) {
|
||||
self::clearCache();
|
||||
Cache::forget($cacheTmp);
|
||||
return Base::retSuccess($message, $data);
|
||||
};
|
||||
|
||||
// 设置更新状态
|
||||
Cache::put($cacheKey, true, 180); // 3分钟有效期
|
||||
Cache::put($cacheTmp, true, 180); // 3分钟有效期
|
||||
|
||||
// 临时目录
|
||||
$tempDir = base_path('docker/appstore/temp/sources');
|
||||
@ -659,7 +687,7 @@ class Apps
|
||||
|
||||
try {
|
||||
// 下载源列表
|
||||
$res = Ihttp::ihttp_request(self::$sourcesUrl);
|
||||
$res = Ihttp::ihttp_request(self::SOURCES_URL);
|
||||
if (Base::isError($res)) {
|
||||
return $onFailure('下载源列表失败');
|
||||
}
|
||||
@ -758,21 +786,22 @@ class Apps
|
||||
public static function downloadApp(string $url): array
|
||||
{
|
||||
// 检查是否正在下载
|
||||
$cacheKey = 'appstore_download_running';
|
||||
if (Cache::has($cacheKey)) {
|
||||
$cacheTmp = 'appstore_download_running';
|
||||
if (Cache::has($cacheTmp)) {
|
||||
return Base::retError('应用正在下载中,请稍后再试');
|
||||
}
|
||||
$onFailure = function (string $message) use ($cacheKey) {
|
||||
Cache::forget($cacheKey);
|
||||
$onFailure = function (string $message) use ($cacheTmp) {
|
||||
Cache::forget($cacheTmp);
|
||||
return Base::retError($message);
|
||||
};
|
||||
$onSuccess = function (string $message, array $data = []) use ($cacheKey) {
|
||||
Cache::forget($cacheKey);
|
||||
$onSuccess = function (string $message, array $data = []) use ($cacheTmp) {
|
||||
self::clearCache();
|
||||
Cache::forget($cacheTmp);
|
||||
return Base::retSuccess($message, $data);
|
||||
};
|
||||
|
||||
// 设置下载状态
|
||||
Cache::put($cacheKey, true, 180); // 3分钟有效期
|
||||
Cache::put($cacheTmp, true, 180); // 3分钟有效期
|
||||
|
||||
// 验证URL格式
|
||||
if (!filter_var($url, FILTER_VALIDATE_URL)) {
|
||||
@ -845,7 +874,7 @@ class Apps
|
||||
|
||||
// 处理应用名称
|
||||
$appName = Base::camel2snake(Base::cn2pinyin($configData['name'], '_'));
|
||||
if (in_array($appName, self::$protectedServiceNames)) {
|
||||
if (in_array($appName, self::PROTECTED_NAMES)) {
|
||||
return Base::retError('服务名称 "' . $appName . '" 被保护,不能使用');
|
||||
}
|
||||
$targetDir = base_path('docker/appstore/apps/' . $appName);
|
||||
@ -920,23 +949,6 @@ class Apps
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查文件名是否匹配 README 模式
|
||||
*
|
||||
* @param string $fileName 文件名
|
||||
* @param array $patterns 正则模式数组
|
||||
* @return bool 是否匹配
|
||||
*/
|
||||
private static function matchReadmePattern(string $fileName, array $patterns): bool
|
||||
{
|
||||
foreach ($patterns as $pattern) {
|
||||
if (preg_match($pattern, $fileName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成docker-compose.yml文件配置
|
||||
*
|
||||
@ -989,7 +1001,7 @@ class Apps
|
||||
|
||||
// 检查服务名称是否被保护
|
||||
foreach ($content['services'] as $name => $service) {
|
||||
if (in_array($name, self::$protectedServiceNames)) {
|
||||
if (in_array($name, self::PROTECTED_NAMES)) {
|
||||
return Base::retError('服务名称 "' . $name . '" 被保护,不能使用');
|
||||
}
|
||||
}
|
||||
@ -1164,6 +1176,27 @@ class Apps
|
||||
return $field[$lang] ?? $field[array_key_first($field)];
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查应用是否可升级
|
||||
*
|
||||
* @param array $config 应用配置
|
||||
* @param array $versions 可用版本列表
|
||||
* @return bool 如果可升级返回 true,否则返回 false
|
||||
*/
|
||||
public static function isUpgradeable(array $config, array $versions): bool
|
||||
{
|
||||
$upgradeable = false;
|
||||
if ($config['status'] === 'installed' && !empty($versions)) {
|
||||
foreach ($versions as $version) {
|
||||
if (version_compare($config['install_version'], $version['version'], '<')) {
|
||||
$upgradeable = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $upgradeable;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理应用图标
|
||||
*
|
||||
@ -1216,7 +1249,7 @@ class Apps
|
||||
* @param string $appName 应用名称
|
||||
* @return array 按语义化版本排序(从新到旧)的版本号数组
|
||||
*/
|
||||
private static function getAvailableVersions(string $appName): array
|
||||
public static function getAvailableVersions(string $appName): array
|
||||
{
|
||||
$baseDir = base_path('docker/appstore/apps/' . $appName);
|
||||
$versions = [];
|
||||
@ -1347,6 +1380,18 @@ class Apps
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除缓存
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private static function clearCache(): void
|
||||
{
|
||||
foreach (self::CACHE_KEYS as $key) {
|
||||
Cache::forget($key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行curl请求
|
||||
*
|
||||
|
||||
@ -36,7 +36,7 @@ services:
|
||||
image: "nginx:alpine"
|
||||
ports:
|
||||
- "${APP_PORT}:80"
|
||||
- "${APP_SSL_PORT:-}:443"
|
||||
- "${APP_SSL_PORT:-0}:443"
|
||||
volumes:
|
||||
- ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf
|
||||
- ./:/var/www
|
||||
@ -100,7 +100,7 @@ services:
|
||||
appstore:
|
||||
container_name: "dootask-appstore-${APP_ID}"
|
||||
privileged: true
|
||||
image: "kuaifan/dootask-appstore:0.0.2"
|
||||
image: "kuaifan/dootask-appstore:0.0.3"
|
||||
volumes:
|
||||
- shared_data:/usr/share/dootask
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user