feat: implement Nginx and Apache configuration management with project-based virtual host generation in IPC layer
This commit is contained in:
@@ -1050,7 +1050,8 @@ export function registerIpcHandlers(): void {
|
||||
enabled: settings.cliEnvEnabled !== false,
|
||||
defaultPhp: settings.defaultPhpVersion,
|
||||
defaultMariaDb: settings.defaultMariaDbVersion,
|
||||
binDir: cliBinService.getBinDir()
|
||||
binDir: cliBinService.getBinDir(),
|
||||
aliases: cliBinService.getAllAliases()
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -10,14 +10,19 @@ import { configService } from './ConfigService'
|
||||
const execAsync = promisify(exec)
|
||||
|
||||
export class CliBinService {
|
||||
private binDir: string
|
||||
private _binDir: string | null = null
|
||||
private aliases: { command: string, target: string }[] = []
|
||||
|
||||
constructor() {
|
||||
// We store shims in the app's roaming/user path to avoid permission issues
|
||||
this.binDir = path.join(app.getPath('userData'), 'cli-bin')
|
||||
if (!fs.existsSync(this.binDir)) {
|
||||
fs.mkdirSync(this.binDir, { recursive: true })
|
||||
constructor() {}
|
||||
|
||||
private getBinDirInternal(): string {
|
||||
if (!this._binDir) {
|
||||
this._binDir = path.join(app.getPath('userData'), 'cli-bin')
|
||||
if (!fs.existsSync(this._binDir)) {
|
||||
fs.mkdirSync(this._binDir, { recursive: true })
|
||||
}
|
||||
}
|
||||
return this._binDir
|
||||
}
|
||||
|
||||
async sync() {
|
||||
@@ -27,11 +32,13 @@ export class CliBinService {
|
||||
|
||||
const installedPhp = phpVersions.filter(v => v.status === 'installed')
|
||||
const installedMariaDb = mariaDbVersions.filter(v => v.status === 'installed')
|
||||
this.aliases = []
|
||||
|
||||
// Clean current shims
|
||||
const files = fs.readdirSync(this.binDir)
|
||||
const binDir = this.getBinDirInternal()
|
||||
const files = fs.readdirSync(binDir)
|
||||
for (const file of files) {
|
||||
fs.unlinkSync(path.join(this.binDir, file))
|
||||
fs.unlinkSync(path.join(binDir, file))
|
||||
}
|
||||
|
||||
// Generate PHP shims
|
||||
@@ -113,25 +120,55 @@ export class CliBinService {
|
||||
// Create a small .cmd proxy for each command
|
||||
// Using "@%* " to pass all arguments correctly
|
||||
const content = `@echo off\n"${targetPath}" %*\n`
|
||||
fs.writeFileSync(path.join(this.binDir, `${name}.cmd`), content)
|
||||
const binDir = this.getBinDirInternal()
|
||||
fs.writeFileSync(path.join(binDir, `${name}.cmd`), content)
|
||||
|
||||
// Also create a .bat just in case
|
||||
fs.writeFileSync(path.join(this.binDir, `${name}.bat`), content)
|
||||
fs.writeFileSync(path.join(binDir, `${name}.bat`), content)
|
||||
|
||||
// Track alias
|
||||
if (!this.aliases.some(a => a.command === name)) {
|
||||
this.aliases.push({ command: name, target: targetPath })
|
||||
}
|
||||
}
|
||||
|
||||
async ensureInPath() {
|
||||
if (process.platform !== 'win32') return
|
||||
|
||||
try {
|
||||
// Get current User PATH
|
||||
const { stdout } = await execAsync('powershell -Command "[Environment]::GetEnvironmentVariable(\'Path\', \'User\')"')
|
||||
const currentPath = stdout.trim()
|
||||
// Use PowerShell to manage PATH safely.
|
||||
// 1. Get current User PATH
|
||||
// 2. Add binDir if not exists
|
||||
// 3. Set new PATH
|
||||
// 4. Broadcast WM_SETTINGCHANGE to notify system
|
||||
const binDir = this.getBinDirInternal()
|
||||
|
||||
if (!currentPath.includes(this.binDir)) {
|
||||
const newPath = `${currentPath}${currentPath.endsWith(';') ? '' : ';'}${this.binDir}`
|
||||
// Update User PATH safely using PowerShell
|
||||
await execAsync(`powershell -Command "[Environment]::SetEnvironmentVariable('Path', '${newPath}', 'User')"`)
|
||||
console.log(`Added Multi-PHP bin to User PATH: ${this.binDir}`)
|
||||
const psScript = `
|
||||
$currentPath = [Environment]::GetEnvironmentVariable('Path', 'User')
|
||||
if ($currentPath -notlike '*${binDir}*') {
|
||||
$newPath = $currentPath
|
||||
if ($newPath -and -not $newPath.EndsWith(';')) { $newPath += ';' }
|
||||
$newPath += '${binDir}'
|
||||
[Environment]::SetEnvironmentVariable('Path', $newPath, 'User')
|
||||
|
||||
# Notify Windows of the change (WM_SETTINGCHANGE)
|
||||
$signature = @'
|
||||
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
|
||||
public static extern IntPtr SendMessageTimeout(IntPtr hWnd, uint Msg, IntPtr wParam, string lParam, uint fuFlags, uint uTimeout, out IntPtr lpdwResult);
|
||||
'@
|
||||
$type = Add-Type -MemberDefinition $signature -Name "Win32" -Namespace "Native" -PassThru -ErrorAction SilentlyContinue
|
||||
if ($type) {
|
||||
$type::SendMessageTimeout(0xFFFF, 0x001A, [IntPtr]::Zero, "Environment", 0x0002, 1000, [ref][IntPtr]::Zero)
|
||||
}
|
||||
Write-Output "PATH_UPDATED"
|
||||
} else {
|
||||
Write-Output "ALREADY_IN_PATH"
|
||||
}
|
||||
`
|
||||
|
||||
const { stdout } = await execAsync(`powershell -Command "${psScript.replace(/\n/g, ' ')}"`)
|
||||
if (stdout.includes('PATH_UPDATED')) {
|
||||
console.log(`Successfully added Multi-PHP bin to User PATH: ${binDir}`)
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Failed to update PATH:', e)
|
||||
@@ -139,7 +176,11 @@ export class CliBinService {
|
||||
}
|
||||
|
||||
getBinDir() {
|
||||
return this.binDir
|
||||
return this.getBinDirInternal()
|
||||
}
|
||||
|
||||
getAllAliases() {
|
||||
return this.aliases
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -107,6 +107,9 @@ interface AppSettings {
|
||||
mariaDbPorts?: Record<string, string>
|
||||
allowRemoteAccess: boolean
|
||||
language: string
|
||||
cliEnvEnabled: boolean
|
||||
defaultPhpVersion: string
|
||||
defaultMariaDbVersion: string
|
||||
}
|
||||
|
||||
function App(): JSX.Element {
|
||||
@@ -137,7 +140,10 @@ function App(): JSX.Element {
|
||||
phpPort: '9001',
|
||||
mariaDbPort: '3306',
|
||||
allowRemoteAccess: false,
|
||||
language: 'tr'
|
||||
language: 'tr',
|
||||
cliEnvEnabled: true,
|
||||
defaultPhpVersion: '',
|
||||
defaultMariaDbVersion: ''
|
||||
})
|
||||
const [notification, setNotification] = useState<{ open: boolean, message: string, severity: 'success' | 'error' }>({
|
||||
open: false,
|
||||
@@ -1317,11 +1323,34 @@ function App(): JSX.Element {
|
||||
onClick={async () => {
|
||||
await window.api.invoke('cli:sync')
|
||||
setNotification({ open: true, message: t('settings.cli_sync_success'), severity: 'success' })
|
||||
fetchCliConfig()
|
||||
}}
|
||||
>
|
||||
{t('settings.cli_sync_btn')}
|
||||
</Button>
|
||||
</Box>
|
||||
|
||||
{cliConfig.aliases && cliConfig.aliases.length > 0 && (
|
||||
<Box sx={{ mt: 2 }}>
|
||||
<Typography variant="subtitle2" sx={{ mb: 1, fontWeight: 600, color: 'primary.main' }}>
|
||||
{t('settings.cli_aliases_title')}
|
||||
</Typography>
|
||||
<Paper variant="outlined" sx={{ bgcolor: 'rgba(0,0,0,0.1)', borderColor: 'rgba(255,255,255,0.05)', maxHeight: 200, overflow: 'auto' }}>
|
||||
<List dense disablePadding>
|
||||
{cliConfig.aliases.map((a: any) => (
|
||||
<ListItem key={a.command} divider sx={{ py: 0.5, borderColor: 'rgba(255,255,255,0.05)' }}>
|
||||
<ListItemText
|
||||
primary={<code>{a.command}</code>}
|
||||
secondary={a.target}
|
||||
primaryTypographyProps={{ variant: 'body2', fontWeight: 'bold' }}
|
||||
secondaryTypographyProps={{ variant: 'caption', sx: { opacity: 0.5 } }}
|
||||
/>
|
||||
</ListItem>
|
||||
))}
|
||||
</List>
|
||||
</Paper>
|
||||
</Box>
|
||||
)}
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
|
||||
@@ -109,10 +109,11 @@
|
||||
"cli_default_php_desc": "Choose which version will be used when you run the 'php' command.",
|
||||
"cli_default_mariadb": "Default MariaDB Version (mysql)",
|
||||
"cli_default_mariadb_desc": "Default version for 'mysql' or 'mariadb' commands.",
|
||||
"cli_sync_btn": "SYNC SETTINGS",
|
||||
"cli_sync_btn": "Sync Commands",
|
||||
"cli_path_found": "PATH Found:",
|
||||
"cli_path_not_found": "PATH Not Set Yet",
|
||||
"cli_sync_success": "CLI settings and PATH integration updated successfully.",
|
||||
"cli_sync_success": "CLI commands updated successfully.",
|
||||
"cli_aliases_title": "Generated Commands (Aliases)",
|
||||
"server_settings": "Server Settings"
|
||||
},
|
||||
"server": {
|
||||
|
||||
@@ -110,10 +110,11 @@
|
||||
"cli_default_php_desc": "'php' komutunu çalıştırdığınızda hangi sürümün kullanılacağını seçin.",
|
||||
"cli_default_mariadb": "Varsayılan MariaDB Sürümü (mysql)",
|
||||
"cli_default_mariadb_desc": "'mysql' veya 'mariadb' komutu için varsayılan sürüm.",
|
||||
"cli_sync_btn": "AYARLARI SENKRONİZE ET",
|
||||
"cli_sync_btn": "Komutları Eşitle",
|
||||
"cli_path_found": "PATH Ayarı Bulundu:",
|
||||
"cli_path_not_found": "PATH Ayarı Henüz Yapılmamış",
|
||||
"cli_sync_success": "CLI ayarları ve PATH entegrasyonu başarıyla güncellendi."
|
||||
"cli_sync_success": "CLI komutları başarıyla güncellendi.",
|
||||
"cli_aliases_title": "Oluşturulan Komutlar (Takma Adlar)"
|
||||
},
|
||||
"server": {
|
||||
"nginx": {
|
||||
|
||||
Reference in New Issue
Block a user