340 lines
9.4 KiB
PHP
340 lines
9.4 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use Illuminate\Support\Facades\File;
|
|
use Illuminate\Support\Facades\Storage;
|
|
use InvalidArgumentException;
|
|
use RuntimeException;
|
|
use Carbon\Carbon;
|
|
|
|
class EnvService
|
|
{
|
|
private string $projectsPath;
|
|
private string $envStoragePath;
|
|
private string $backupStoragePath;
|
|
|
|
public function __construct()
|
|
{
|
|
$this->projectsPath = '/home/tradewind/Projects';
|
|
$this->envStoragePath = storage_path('app/env');
|
|
$this->backupStoragePath = storage_path('app/env/backups');
|
|
|
|
// 确保env存储目录存在
|
|
if (!File::exists($this->envStoragePath)) {
|
|
File::makeDirectory($this->envStoragePath, 0755, true);
|
|
}
|
|
|
|
// 确保备份存储目录存在
|
|
if (!File::exists($this->backupStoragePath)) {
|
|
File::makeDirectory($this->backupStoragePath, 0755, true);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 获取所有项目列表
|
|
*/
|
|
public function getProjects(): array
|
|
{
|
|
if (!File::exists($this->projectsPath)) {
|
|
return [];
|
|
}
|
|
|
|
$projects = [];
|
|
$directories = File::directories($this->projectsPath);
|
|
|
|
foreach ($directories as $directory) {
|
|
$projectName = basename($directory);
|
|
$projects[] = [
|
|
'name' => $projectName,
|
|
'path' => $directory,
|
|
'has_env' => File::exists($directory . '/.env'),
|
|
'envs' => $this->getProjectEnvs($projectName)
|
|
];
|
|
}
|
|
|
|
return $projects;
|
|
}
|
|
|
|
/**
|
|
* 获取指定项目的所有环境配置
|
|
*/
|
|
public function getProjectEnvs(string $projectName): array
|
|
{
|
|
$projectEnvPath = $this->envStoragePath . '/' . $projectName;
|
|
|
|
if (!File::exists($projectEnvPath)) {
|
|
return [];
|
|
}
|
|
|
|
$envs = [];
|
|
$files = File::files($projectEnvPath);
|
|
|
|
foreach ($files as $file) {
|
|
if ($file->getExtension() === 'env') {
|
|
$envName = $file->getFilenameWithoutExtension();
|
|
$envs[] = [
|
|
'name' => $envName,
|
|
'file_path' => $file->getPathname(),
|
|
'size' => $file->getSize(),
|
|
'modified_at' => Carbon::createFromTimestamp($file->getMTime())->format('Y-m-d H:i:s')
|
|
];
|
|
}
|
|
}
|
|
|
|
return $envs;
|
|
}
|
|
|
|
/**
|
|
* 保存环境配置文件
|
|
*/
|
|
public function saveEnv(string $projectName, string $env, string $content): bool
|
|
{
|
|
|
|
$projectEnvPath = $this->envStoragePath . '/' . $projectName;
|
|
|
|
if (!File::exists($projectEnvPath)) {
|
|
File::makeDirectory($projectEnvPath, 0755, true);
|
|
}
|
|
|
|
$filePath = $projectEnvPath . '/' . $env . '.env';
|
|
|
|
return File::put($filePath, $content) !== false;
|
|
}
|
|
|
|
/**
|
|
* 获取环境配置文件内容
|
|
*/
|
|
public function getEnvContent(string $projectName, string $env): string
|
|
{
|
|
|
|
$filePath = $this->envStoragePath . '/' . $projectName . '/' . $env . '.env';
|
|
|
|
if (!File::exists($filePath)) {
|
|
throw new InvalidArgumentException("环境配置文件不存在: {$env}");
|
|
}
|
|
|
|
return File::get($filePath);
|
|
}
|
|
|
|
/**
|
|
* 应用环境配置到项目
|
|
*/
|
|
public function applyEnv(string $projectName, string $env): bool
|
|
{
|
|
|
|
$projectPath = $this->projectsPath . '/' . $projectName;
|
|
|
|
if (!File::exists($projectPath)) {
|
|
throw new InvalidArgumentException("项目不存在: {$projectName}");
|
|
}
|
|
|
|
$envContent = $this->getEnvContent($projectName, $env);
|
|
$targetEnvPath = $projectPath . '/.env';
|
|
|
|
// 备份现有的.env文件到当前项目的storage目录
|
|
if (File::exists($targetEnvPath)) {
|
|
$this->backupCurrentEnv($projectName);
|
|
}
|
|
|
|
return File::put($targetEnvPath, $envContent) !== false;
|
|
}
|
|
|
|
/**
|
|
* 删除环境配置文件
|
|
*/
|
|
public function deleteEnv(string $projectName, string $env): bool
|
|
{
|
|
|
|
$filePath = $this->envStoragePath . '/' . $projectName . '/' . $env . '.env';
|
|
|
|
if (!File::exists($filePath)) {
|
|
return true; // 文件不存在,视为删除成功
|
|
}
|
|
|
|
return File::delete($filePath);
|
|
}
|
|
|
|
/**
|
|
* 从项目导入当前.env文件
|
|
*/
|
|
public function importFromProject(string $projectName, string $env): bool
|
|
{
|
|
|
|
$projectEnvPath = $this->projectsPath . '/' . $projectName . '/.env';
|
|
|
|
if (!File::exists($projectEnvPath)) {
|
|
throw new InvalidArgumentException("项目 {$projectName} 没有.env文件");
|
|
}
|
|
|
|
$content = File::get($projectEnvPath);
|
|
return $this->saveEnv($projectName, $env, $content);
|
|
}
|
|
|
|
/**
|
|
* 获取项目当前.env文件内容
|
|
*/
|
|
public function getCurrentProjectEnv(string $projectName): ?string
|
|
{
|
|
|
|
$projectEnvPath = $this->projectsPath . '/' . $projectName . '/.env';
|
|
|
|
if (!File::exists($projectEnvPath)) {
|
|
return null;
|
|
}
|
|
|
|
return File::get($projectEnvPath);
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* 获取存储路径
|
|
*/
|
|
public function getStoragePath(): string
|
|
{
|
|
return $this->envStoragePath;
|
|
}
|
|
|
|
/**
|
|
* 获取项目路径
|
|
*/
|
|
public function getProjectsPath(): string
|
|
{
|
|
return $this->projectsPath;
|
|
}
|
|
|
|
/**
|
|
* 创建新的环境配置文件
|
|
*/
|
|
public function createNewEnv(string $projectName, string $envName): bool
|
|
{
|
|
$projectEnvPath = $this->envStoragePath . '/' . $projectName;
|
|
|
|
if (!File::exists($projectEnvPath)) {
|
|
File::makeDirectory($projectEnvPath, 0755, true);
|
|
}
|
|
|
|
$filePath = $projectEnvPath . '/' . $envName . '.env';
|
|
|
|
// 检查文件是否已存在
|
|
if (File::exists($filePath)) {
|
|
throw new InvalidArgumentException("环境配置文件已存在: {$envName}");
|
|
}
|
|
|
|
// 创建空的配置文件
|
|
$defaultContent = "# {$envName} 环境配置\n# 创建时间: " . Carbon::now()->format('Y-m-d H:i:s') . "\n\n";
|
|
|
|
return File::put($filePath, $defaultContent) !== false;
|
|
}
|
|
|
|
/**
|
|
* 备份项目当前的.env文件到storage目录
|
|
*/
|
|
private function backupCurrentEnv(string $projectName): bool
|
|
{
|
|
$projectPath = $this->projectsPath . '/' . $projectName;
|
|
$currentEnvPath = $projectPath . '/.env';
|
|
|
|
if (!File::exists($currentEnvPath)) {
|
|
return true; // 没有.env文件,无需备份
|
|
}
|
|
|
|
// 创建项目备份目录
|
|
$projectBackupPath = $this->backupStoragePath . '/' . $projectName;
|
|
if (!File::exists($projectBackupPath)) {
|
|
File::makeDirectory($projectBackupPath, 0755, true);
|
|
}
|
|
|
|
// 生成备份文件名
|
|
$timestamp = Carbon::now()->format('Y-m-d-H-i-s');
|
|
$backupFileName = "env-backup-{$timestamp}.env";
|
|
$backupPath = $projectBackupPath . '/' . $backupFileName;
|
|
|
|
return File::copy($currentEnvPath, $backupPath);
|
|
}
|
|
|
|
/**
|
|
* 获取项目的所有备份文件
|
|
*/
|
|
public function getProjectBackups(string $projectName): array
|
|
{
|
|
$projectBackupPath = $this->backupStoragePath . '/' . $projectName;
|
|
|
|
if (!File::exists($projectBackupPath)) {
|
|
return [];
|
|
}
|
|
|
|
$backups = [];
|
|
$files = File::files($projectBackupPath);
|
|
|
|
foreach ($files as $file) {
|
|
if ($file->getExtension() === 'env' && str_starts_with($file->getFilename(), 'env-backup-')) {
|
|
$backups[] = [
|
|
'name' => $file->getFilenameWithoutExtension(),
|
|
'file_path' => $file->getPathname(),
|
|
'size' => $file->getSize(),
|
|
'created_at' => Carbon::createFromTimestamp($file->getMTime())->format('Y-m-d H:i:s')
|
|
];
|
|
}
|
|
}
|
|
|
|
// 按创建时间倒序排列
|
|
usort($backups, function ($a, $b) {
|
|
return strcmp($b['created_at'], $a['created_at']);
|
|
});
|
|
|
|
return $backups;
|
|
}
|
|
|
|
/**
|
|
* 恢复备份文件到项目
|
|
*/
|
|
public function restoreBackup(string $projectName, string $backupName): bool
|
|
{
|
|
$projectPath = $this->projectsPath . '/' . $projectName;
|
|
|
|
if (!File::exists($projectPath)) {
|
|
throw new InvalidArgumentException("项目不存在: {$projectName}");
|
|
}
|
|
|
|
$backupPath = $this->backupStoragePath . '/' . $projectName . '/' . $backupName . '.env';
|
|
|
|
if (!File::exists($backupPath)) {
|
|
throw new InvalidArgumentException("备份文件不存在: {$backupName}");
|
|
}
|
|
|
|
$targetEnvPath = $projectPath . '/.env';
|
|
|
|
// 在恢复前先备份当前的.env文件
|
|
if (File::exists($targetEnvPath)) {
|
|
$this->backupCurrentEnv($projectName);
|
|
}
|
|
|
|
$backupContent = File::get($backupPath);
|
|
return File::put($targetEnvPath, $backupContent) !== false;
|
|
}
|
|
|
|
/**
|
|
* 删除备份文件
|
|
*/
|
|
public function deleteBackup(string $projectName, string $backupName): bool
|
|
{
|
|
$backupPath = $this->backupStoragePath . '/' . $projectName . '/' . $backupName . '.env';
|
|
|
|
if (!File::exists($backupPath)) {
|
|
return true; // 文件不存在,视为删除成功
|
|
}
|
|
|
|
return File::delete($backupPath);
|
|
}
|
|
|
|
/**
|
|
* 获取备份存储路径
|
|
*/
|
|
public function getBackupStoragePath(): string
|
|
{
|
|
return $this->backupStoragePath;
|
|
}
|
|
}
|