feat: implement project management and service orchestration architecture
This commit is contained in:
@@ -0,0 +1,10 @@
|
|||||||
|
Alias /{{HOST}} "{{PATH}}"
|
||||||
|
<Directory "{{PATH}}">
|
||||||
|
Options Indexes FollowSymLinks
|
||||||
|
AllowOverride All
|
||||||
|
Require all granted
|
||||||
|
|
||||||
|
<FilesMatch \.php$>
|
||||||
|
SetHandler "proxy:fcgi://127.0.0.1:{{PHP_PORT}}"
|
||||||
|
</FilesMatch>
|
||||||
|
</Directory>
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
Define SRVROOT "{{SRVROOT}}"
|
||||||
|
|
||||||
|
Listen {{LISTEN_ADDRESS}}:{{PORT}}
|
||||||
|
|
||||||
|
LoadModule mpm_winnt_module modules/mod_mpm_winnt.so
|
||||||
|
LoadModule authz_core_module modules/mod_authz_core.so
|
||||||
|
LoadModule authz_host_module modules/mod_authz_host.so
|
||||||
|
LoadModule dir_module modules/mod_dir.so
|
||||||
|
LoadModule mime_module modules/mod_mime.so
|
||||||
|
LoadModule alias_module modules/mod_alias.so
|
||||||
|
LoadModule log_config_module modules/mod_log_config.so
|
||||||
|
LoadModule rewrite_module modules/mod_rewrite.so
|
||||||
|
LoadModule proxy_module modules/mod_proxy.so
|
||||||
|
LoadModule proxy_fcgi_module modules/mod_proxy_fcgi.so
|
||||||
|
|
||||||
|
ServerAdmin admin@localhost
|
||||||
|
ServerName localhost:{{PORT}}
|
||||||
|
|
||||||
|
DocumentRoot "{{ROOT}}"
|
||||||
|
<Directory "{{ROOT}}">
|
||||||
|
Options Indexes FollowSymLinks
|
||||||
|
AllowOverride All
|
||||||
|
Require all granted
|
||||||
|
</Directory>
|
||||||
|
|
||||||
|
ErrorLog "{{LOG_DIR}}/apache_error.log"
|
||||||
|
LogLevel warn
|
||||||
|
|
||||||
|
<IfModule log_config_module>
|
||||||
|
LogFormat "%h %l %u %t \"%r\" %>s %b" common
|
||||||
|
CustomLog "{{LOG_DIR}}/apache_access.log" common
|
||||||
|
</IfModule>
|
||||||
|
|
||||||
|
<IfModule dir_module>
|
||||||
|
DirectoryIndex index.php index.html index.htm
|
||||||
|
</IfModule>
|
||||||
|
|
||||||
|
<Files ".ht*">
|
||||||
|
Require all denied
|
||||||
|
</Files>
|
||||||
|
|
||||||
|
# phpMyAdmin configuration
|
||||||
|
Alias /phpmyadmin "{{PHPMYADMIN_DIR}}"
|
||||||
|
<Directory "{{PHPMYADMIN_DIR}}">
|
||||||
|
Options Indexes FollowSymLinks
|
||||||
|
AllowOverride All
|
||||||
|
Require all granted
|
||||||
|
|
||||||
|
<FilesMatch \.php$>
|
||||||
|
SetHandler "proxy:fcgi://127.0.0.1:{{PHP_PORT}}"
|
||||||
|
</FilesMatch>
|
||||||
|
</Directory>
|
||||||
|
|
||||||
|
# Global PHP handler
|
||||||
|
<FilesMatch \.php$>
|
||||||
|
SetHandler "proxy:fcgi://127.0.0.1:{{PHP_PORT}}"
|
||||||
|
</FilesMatch>
|
||||||
|
|
||||||
|
# Project configurations
|
||||||
|
{{PROJECTS}}
|
||||||
+1
-1
@@ -84,9 +84,9 @@ app.whenReady().then(() => {
|
|||||||
optimizer.watchWindowShortcuts(window)
|
optimizer.watchWindowShortcuts(window)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
registerIpcHandlers()
|
||||||
createWindow()
|
createWindow()
|
||||||
createTray()
|
createTray()
|
||||||
registerIpcHandlers()
|
|
||||||
|
|
||||||
// Generate CLI aliases on start
|
// Generate CLI aliases on start
|
||||||
cliAliasService.generateAliases().catch(console.error)
|
cliAliasService.generateAliases().catch(console.error)
|
||||||
|
|||||||
+118
-6
@@ -7,6 +7,7 @@ import { projectService } from '../services/ProjectService'
|
|||||||
import { phpManagerService } from '../services/PhpManagerService'
|
import { phpManagerService } from '../services/PhpManagerService'
|
||||||
import { mariaDbManagerService } from '../services/MariaDbManagerService'
|
import { mariaDbManagerService } from '../services/MariaDbManagerService'
|
||||||
import { nginxManagerService } from '../services/NginxManagerService'
|
import { nginxManagerService } from '../services/NginxManagerService'
|
||||||
|
import { apacheManagerService } from '../services/ApacheManagerService'
|
||||||
import { phpMyAdminService } from '../services/PhpMyAdminService'
|
import { phpMyAdminService } from '../services/PhpMyAdminService'
|
||||||
import { portMappingService } from '../services/PortMappingService'
|
import { portMappingService } from '../services/PortMappingService'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
@@ -55,7 +56,8 @@ async function rebuildNginxConfig(forceProjects: boolean = false): Promise<{ bin
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Generate project configs
|
// Generate project configs
|
||||||
const projects = await projectService.getProjects()
|
const allProjects = await projectService.getProjects()
|
||||||
|
const projects = allProjects.filter(p => !p.serverType || p.serverType === 'nginx')
|
||||||
for (const p of projects) {
|
for (const p of projects) {
|
||||||
const projectConfPath = path.join(projectsConfDir, `${p.host}.conf`)
|
const projectConfPath = path.join(projectsConfDir, `${p.host}.conf`)
|
||||||
let shouldGenerate = forceProjects || !fs.existsSync(projectConfPath)
|
let shouldGenerate = forceProjects || !fs.existsSync(projectConfPath)
|
||||||
@@ -142,12 +144,88 @@ async function reloadNginx(): Promise<{ success: boolean; message: string }> {
|
|||||||
return { success: false, message: `Reload hatası: ${error.message}` }
|
return { success: false, message: `Reload hatası: ${error.message}` }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
async function rebuildApacheConfig(forceProjects: boolean = false): Promise<{ binPath: string; apacheDir: string; configPath: string }> {
|
||||||
|
const settings = configService.getSettings()
|
||||||
|
const binDir = path.join(configService.getBasePath(), 'bin')
|
||||||
|
const apacheDir = path.join(binDir, 'apache')
|
||||||
|
|
||||||
|
const isWindows = process.platform === 'win32';
|
||||||
|
const binPath = findExecutable(apacheDir, isWindows ? 'httpd.exe' : 'httpd')
|
||||||
|
if (!binPath) throw new Error('Apache executable bulunamadı.')
|
||||||
|
|
||||||
|
const apacheRoot = path.dirname(path.dirname(binPath)) // bin'in üstü
|
||||||
|
const logDir = path.join(configService.getBasePath(), 'logs')
|
||||||
|
const rootPath = path.join(app.getAppPath(), 'www')
|
||||||
|
|
||||||
|
// Ensure directories exist
|
||||||
|
const projectsConfDir = path.join(configService.getBasePath(), 'generated_configs', 'projects_apache')
|
||||||
|
if (!fs.existsSync(projectsConfDir)) fs.mkdirSync(projectsConfDir, { recursive: true })
|
||||||
|
|
||||||
|
// Generate project configs
|
||||||
|
const allProjects = await projectService.getProjects()
|
||||||
|
const projects = allProjects.filter(p => p.serverType === 'apache')
|
||||||
|
for (const p of projects) {
|
||||||
|
const projectConfPath = path.join(projectsConfDir, `${p.host}.conf`)
|
||||||
|
let shouldGenerate = forceProjects || !fs.existsSync(projectConfPath)
|
||||||
|
|
||||||
|
if (shouldGenerate) {
|
||||||
|
const phpPort = portMappingService.getPhpPort(p.phpVersion)
|
||||||
|
let normalizedPath = p.path.replace(/\\/g, '/')
|
||||||
|
if (!normalizedPath.endsWith('/')) normalizedPath += '/'
|
||||||
|
|
||||||
|
await configService.generateConfig('apache_project.conf.template', `projects_apache/${p.host}.conf`, {
|
||||||
|
HOST: p.host,
|
||||||
|
PATH: normalizedPath,
|
||||||
|
PHP_PORT: phpPort.toString()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const projectsInclusion = projects.length > 0
|
||||||
|
? `Include "${projectsConfDir.replace(/\\/g, '/')}/*.conf"`
|
||||||
|
: ''
|
||||||
|
|
||||||
|
const configPath = await configService.generateConfig('httpd.conf.template', 'httpd.conf', {
|
||||||
|
PORT: settings.apachePort || '8080',
|
||||||
|
LISTEN_ADDRESS: settings.allowRemoteAccess ? '0.0.0.0' : '127.0.0.1',
|
||||||
|
PHP_PORT: settings.phpPort,
|
||||||
|
ROOT: rootPath.replace(/\\/g, '/'),
|
||||||
|
SRVROOT: apacheRoot.replace(/\\/g, '/'),
|
||||||
|
LOG_DIR: logDir.replace(/\\/g, '/'),
|
||||||
|
PHPMYADMIN_DIR: phpMyAdminService.getPath().replace(/\\/g, '/'),
|
||||||
|
PROJECTS: projectsInclusion
|
||||||
|
})
|
||||||
|
|
||||||
|
return { binPath, apacheDir, configPath }
|
||||||
|
}
|
||||||
|
|
||||||
|
async function restartApache(): Promise<{ success: boolean; message: string }> {
|
||||||
|
try {
|
||||||
|
const { binPath, configPath } = await rebuildApacheConfig(false)
|
||||||
|
|
||||||
|
await processManager.forceKillAll('apache')
|
||||||
|
await processManager.stopService('apache')
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 1000))
|
||||||
|
|
||||||
|
const success = await processManager.startService('apache', binPath, ['-f', configPath], false)
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
return { success: true, message: 'Apache restart successful' }
|
||||||
|
} else {
|
||||||
|
const errorLogs = await processManager.getLastErrorLogs('apache')
|
||||||
|
return { success: false, message: `Apache restart failed:\n${errorLogs.join('\n')}` }
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
return { success: false, message: `Restart hatası: ${error.message}` }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function registerIpcHandlers(): void {
|
export function registerIpcHandlers(): void {
|
||||||
// Service management
|
// Service management
|
||||||
ipcMain.handle('services:reload', async (_event, serviceName: string) => {
|
ipcMain.handle('services:reload', async (_event, serviceName: string) => {
|
||||||
if (serviceName !== 'nginx') return { success: false, message: 'Yalnızca Nginx için reload desteklenmektedir.' }
|
if (serviceName === 'nginx') return await reloadNginx()
|
||||||
return await reloadNginx()
|
if (serviceName === 'apache') return await restartApache() // Apache doesn't have a simple reload here
|
||||||
|
return { success: false, message: 'Bu servis için reload desteklenmiyor.' }
|
||||||
})
|
})
|
||||||
|
|
||||||
ipcMain.handle('services:start', async (_event, serviceName: string) => {
|
ipcMain.handle('services:start', async (_event, serviceName: string) => {
|
||||||
@@ -157,17 +235,24 @@ export function registerIpcHandlers(): void {
|
|||||||
|
|
||||||
if (serviceName === 'nginx') {
|
if (serviceName === 'nginx') {
|
||||||
const { binPath, nginxPrefix, configPath } = await rebuildNginxConfig(false)
|
const { binPath, nginxPrefix, configPath } = await rebuildNginxConfig(false)
|
||||||
|
|
||||||
const success = await processManager.startService('nginx', binPath, ['-p', nginxPrefix, '-c', configPath], false)
|
const success = await processManager.startService('nginx', binPath, ['-p', nginxPrefix, '-c', configPath], false)
|
||||||
|
|
||||||
if (!success) {
|
if (!success) {
|
||||||
const errorLogs = await processManager.getLastErrorLogs('nginx')
|
const errorLogs = await processManager.getLastErrorLogs('nginx')
|
||||||
return { success: false, message: `Nginx başlatılamadı.\n\n${errorLogs.join('\n')}` }
|
return { success: false, message: `Nginx başlatılamadı.\n\n${errorLogs.join('\n')}` }
|
||||||
}
|
}
|
||||||
|
|
||||||
return { success, message: success ? 'Nginx başlatıldı.' : 'Nginx devreye alınamadı.' }
|
return { success, message: success ? 'Nginx başlatıldı.' : 'Nginx devreye alınamadı.' }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (serviceName === 'apache') {
|
||||||
|
const { binPath, configPath } = await rebuildApacheConfig(false)
|
||||||
|
const success = await processManager.startService('apache', binPath, ['-f', configPath], false)
|
||||||
|
if (!success) {
|
||||||
|
const errorLogs = await processManager.getLastErrorLogs('apache')
|
||||||
|
return { success: false, message: `Apache başlatılamadı.\n\n${errorLogs.join('\n')}` }
|
||||||
|
}
|
||||||
|
return { success, message: success ? 'Apache başlatıldı.' : 'Apache devreye alınamadı.' }
|
||||||
|
}
|
||||||
|
|
||||||
if (serviceName.startsWith('php')) {
|
if (serviceName.startsWith('php')) {
|
||||||
const versionMatch = serviceName.match(/^php:(.+)$/)
|
const versionMatch = serviceName.match(/^php:(.+)$/)
|
||||||
const phpVersion = versionMatch ? versionMatch[1] : null
|
const phpVersion = versionMatch ? versionMatch[1] : null
|
||||||
@@ -451,6 +536,33 @@ export function registerIpcHandlers(): void {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Apache Versions
|
||||||
|
ipcMain.handle('apache:versions', async () => {
|
||||||
|
return await apacheManagerService.getVersions()
|
||||||
|
})
|
||||||
|
|
||||||
|
ipcMain.handle('apache:download', async (event, id: string) => {
|
||||||
|
const versions = await apacheManagerService.getVersions()
|
||||||
|
const version = versions.find(v => v.id === id)
|
||||||
|
if (!version) return { success: false, message: 'Geçersiz Apache sürümü.' }
|
||||||
|
|
||||||
|
try {
|
||||||
|
apacheManagerService.updateProgress(id, 0, 'downloading')
|
||||||
|
event.sender.send('apache:progress', { id, progress: 0 })
|
||||||
|
|
||||||
|
await downloadService.downloadAndExtract(version.url, id, (p) => {
|
||||||
|
apacheManagerService.updateProgress(id, p)
|
||||||
|
event.sender.send('apache:progress', { id, progress: p })
|
||||||
|
})
|
||||||
|
|
||||||
|
apacheManagerService.updateProgress(id, 100, 'installed')
|
||||||
|
return { success: true, message: 'Apache indirme tamamlandı.' }
|
||||||
|
} catch (error: any) {
|
||||||
|
apacheManagerService.updateProgress(id, 0, 'available')
|
||||||
|
return { success: false, message: `Apache hatası: ${error.message}` }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
// Nginx Versions
|
// Nginx Versions
|
||||||
ipcMain.handle('nginx:versions', async () => {
|
ipcMain.handle('nginx:versions', async () => {
|
||||||
return await nginxManagerService.getVersions()
|
return await nginxManagerService.getVersions()
|
||||||
|
|||||||
@@ -0,0 +1,86 @@
|
|||||||
|
import fs from 'fs'
|
||||||
|
import path from 'path'
|
||||||
|
import { configService } from './ConfigService'
|
||||||
|
|
||||||
|
export interface ApacheVersion {
|
||||||
|
id: string
|
||||||
|
version: string
|
||||||
|
url: string
|
||||||
|
status: 'available' | 'downloading' | 'installed'
|
||||||
|
progress: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ApacheManagerService {
|
||||||
|
private versions: ApacheVersion[] = []
|
||||||
|
private binDir: string
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.binDir = path.join(configService.getBasePath(), 'bin')
|
||||||
|
this.init()
|
||||||
|
}
|
||||||
|
|
||||||
|
private async init() {
|
||||||
|
if (!fs.existsSync(this.binDir)) {
|
||||||
|
fs.mkdirSync(this.binDir, { recursive: true })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getVersions(): Promise<ApacheVersion[]> {
|
||||||
|
// Apache Lounge VS17 Win64 version
|
||||||
|
const stableVersion = '2.4.66'
|
||||||
|
const url = `https://www.apachelounge.com/download/VS18/binaries/httpd-2.4.66-260223-Win64-VS18.zip`
|
||||||
|
|
||||||
|
const id = 'apache'
|
||||||
|
const targetDir = path.join(this.binDir, id)
|
||||||
|
|
||||||
|
// Check if installed (look for httpd.exe anywhere in the zip extraction folder)
|
||||||
|
const isInstalled = this.isInstalled(targetDir)
|
||||||
|
|
||||||
|
const v: ApacheVersion = {
|
||||||
|
id,
|
||||||
|
version: stableVersion,
|
||||||
|
url,
|
||||||
|
status: isInstalled ? 'installed' : 'available',
|
||||||
|
progress: isInstalled ? 100 : 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Maintain internal state for progress updates
|
||||||
|
const existing = this.versions.find(ev => ev.id === id)
|
||||||
|
if (existing && existing.status === 'downloading') {
|
||||||
|
return [existing]
|
||||||
|
}
|
||||||
|
|
||||||
|
this.versions = [v]
|
||||||
|
return this.versions
|
||||||
|
}
|
||||||
|
|
||||||
|
private isInstalled(dir: string): boolean {
|
||||||
|
if (!fs.existsSync(dir)) return false
|
||||||
|
|
||||||
|
const findHttpd = (d: string): boolean => {
|
||||||
|
const files = fs.readdirSync(d)
|
||||||
|
for (const f of files) {
|
||||||
|
const p = path.join(d, f)
|
||||||
|
const s = fs.statSync(p)
|
||||||
|
if (s.isDirectory()) {
|
||||||
|
if (findHttpd(p)) return true
|
||||||
|
} else if (f.toLowerCase() === 'httpd.exe') {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return findHttpd(dir)
|
||||||
|
}
|
||||||
|
|
||||||
|
updateProgress(id: string, progress: number, status: ApacheVersion['status'] = 'downloading') {
|
||||||
|
const v = this.versions.find(ev => ev.id === id)
|
||||||
|
if (v) {
|
||||||
|
v.progress = progress
|
||||||
|
v.status = status
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const apacheManagerService = new ApacheManagerService()
|
||||||
@@ -8,6 +8,8 @@ export class ConfigService {
|
|||||||
private settingsPath: string
|
private settingsPath: string
|
||||||
private defaultSettings = {
|
private defaultSettings = {
|
||||||
nginxPort: '80',
|
nginxPort: '80',
|
||||||
|
apachePort: '8080',
|
||||||
|
serverType: 'nginx' as 'nginx' | 'apache',
|
||||||
phpPort: '9001',
|
phpPort: '9001',
|
||||||
mariaDbPort: '3306',
|
mariaDbPort: '3306',
|
||||||
mariaDbPorts: {} as Record<string, string>,
|
mariaDbPorts: {} as Record<string, string>,
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ export class ProcessManager {
|
|||||||
fs.mkdirSync(this.logDir, { recursive: true })
|
fs.mkdirSync(this.logDir, { recursive: true })
|
||||||
}
|
}
|
||||||
// Initialize with default logs
|
// Initialize with default logs
|
||||||
['nginx', 'php', 'mariadb'].forEach(s => {
|
['nginx', 'apache', 'php', 'mariadb'].forEach(s => {
|
||||||
if (!this.logs.has(s)) {
|
if (!this.logs.has(s)) {
|
||||||
this.logs.set(s, [`[${new Date().toLocaleTimeString()}] Sistem hazır. İşlem bekleniyor...`])
|
this.logs.set(s, [`[${new Date().toLocaleTimeString()}] Sistem hazır. İşlem bekleniyor...`])
|
||||||
}
|
}
|
||||||
@@ -209,12 +209,12 @@ export class ProcessManager {
|
|||||||
} else {
|
} else {
|
||||||
const isWindows = process.platform === 'win32';
|
const isWindows = process.platform === 'win32';
|
||||||
if (isWindows) {
|
if (isWindows) {
|
||||||
const executableName = name === 'nginx' ? 'nginx.exe' : name === 'php' ? 'php-cgi.exe' : 'mariadbd.exe'
|
const executableName = name === 'nginx' ? 'nginx.exe' : name === 'apache' ? 'httpd.exe' : name === 'php' ? 'php-cgi.exe' : 'mariadbd.exe'
|
||||||
spawn('taskkill', ['/F', '/IM', executableName, '/T'], { windowsHide: true })
|
spawn('taskkill', ['/F', '/IM', executableName, '/T'], { windowsHide: true })
|
||||||
.on('close', () => resolve(true))
|
.on('close', () => resolve(true))
|
||||||
.on('error', () => resolve(true))
|
.on('error', () => resolve(true))
|
||||||
} else {
|
} else {
|
||||||
const processName = name === 'nginx' ? 'nginx' : name === 'php' ? 'php-cgi' : 'mariadbd'
|
const processName = name === 'nginx' ? 'nginx' : name === 'apache' ? 'httpd' : name === 'php' ? 'php-cgi' : 'mariadbd'
|
||||||
spawn('pkill', ['-f', processName], { windowsHide: true })
|
spawn('pkill', ['-f', processName], { windowsHide: true })
|
||||||
.on('close', () => resolve(true))
|
.on('close', () => resolve(true))
|
||||||
.on('error', () => resolve(true))
|
.on('error', () => resolve(true))
|
||||||
@@ -227,7 +227,7 @@ export class ProcessManager {
|
|||||||
this.processes.delete(name)
|
this.processes.delete(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
const procName = name === 'nginx' ? 'nginx' : name === 'php' ? 'php-fpm' : 'mariadbd'
|
const procName = name === 'nginx' ? 'nginx' : name === 'apache' ? 'httpd' : name === 'php' ? 'php-fpm' : 'mariadbd'
|
||||||
spawn('pkill', ['-9', '-f', procName])
|
spawn('pkill', ['-9', '-f', procName])
|
||||||
.on('close', () => resolve(true))
|
.on('close', () => resolve(true))
|
||||||
.on('error', () => resolve(true))
|
.on('error', () => resolve(true))
|
||||||
@@ -287,6 +287,7 @@ export class ProcessManager {
|
|||||||
}
|
}
|
||||||
// Ensure defaults are present if stopped
|
// Ensure defaults are present if stopped
|
||||||
if (!statuses['nginx']) statuses['nginx'] = 'stopped'
|
if (!statuses['nginx']) statuses['nginx'] = 'stopped'
|
||||||
|
if (!statuses['apache']) statuses['apache'] = 'stopped'
|
||||||
if (!statuses['mariadb']) statuses['mariadb'] = 'stopped'
|
if (!statuses['mariadb']) statuses['mariadb'] = 'stopped'
|
||||||
return statuses
|
return statuses
|
||||||
}
|
}
|
||||||
@@ -295,6 +296,7 @@ export class ProcessManager {
|
|||||||
if (!this.logDir) return
|
if (!this.logDir) return
|
||||||
const logPaths: Record<string, string> = {
|
const logPaths: Record<string, string> = {
|
||||||
nginx: path.join(this.logDir, 'nginx_error.log'),
|
nginx: path.join(this.logDir, 'nginx_error.log'),
|
||||||
|
apache: path.join(this.logDir, 'apache_error.log'),
|
||||||
mariadb: path.join(this.logDir, 'mariadb_error.log')
|
mariadb: path.join(this.logDir, 'mariadb_error.log')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ export interface Project {
|
|||||||
phpVersion: string
|
phpVersion: string
|
||||||
mariaDbVersion: string
|
mariaDbVersion: string
|
||||||
host: string
|
host: string
|
||||||
|
serverType?: 'nginx' | 'apache'
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ProjectService {
|
export class ProjectService {
|
||||||
|
|||||||
@@ -25,6 +25,9 @@ const api = {
|
|||||||
getNginxVersions: () => electronAPI.ipcRenderer.invoke('nginx:versions'),
|
getNginxVersions: () => electronAPI.ipcRenderer.invoke('nginx:versions'),
|
||||||
downloadNginxVersion: (id: string) => electronAPI.ipcRenderer.invoke('nginx:download', id),
|
downloadNginxVersion: (id: string) => electronAPI.ipcRenderer.invoke('nginx:download', id),
|
||||||
onNginxProgress: (callback: any) => electronAPI.ipcRenderer.on('nginx:progress', (_event, data) => callback(data)),
|
onNginxProgress: (callback: any) => electronAPI.ipcRenderer.on('nginx:progress', (_event, data) => callback(data)),
|
||||||
|
getApacheVersions: () => electronAPI.ipcRenderer.invoke('apache:versions'),
|
||||||
|
downloadApacheVersion: (id: string) => electronAPI.ipcRenderer.invoke('apache:download', id),
|
||||||
|
onApacheProgress: (callback: any) => electronAPI.ipcRenderer.on('apache:progress', (_event, data) => callback(data)),
|
||||||
pmaStatus: () => electronAPI.ipcRenderer.invoke('pma:status'),
|
pmaStatus: () => electronAPI.ipcRenderer.invoke('pma:status'),
|
||||||
pmaSetup: (mariaDbPort: string) => electronAPI.ipcRenderer.invoke('pma:setup', mariaDbPort),
|
pmaSetup: (mariaDbPort: string) => electronAPI.ipcRenderer.invoke('pma:setup', mariaDbPort),
|
||||||
onPmaProgress: (callback: any) => electronAPI.ipcRenderer.on('pma:progress', (_event, data) => callback(data)),
|
onPmaProgress: (callback: any) => electronAPI.ipcRenderer.on('pma:progress', (_event, data) => callback(data)),
|
||||||
|
|||||||
+154
-10
@@ -91,6 +91,8 @@ interface ServiceStatus {
|
|||||||
|
|
||||||
interface AppSettings {
|
interface AppSettings {
|
||||||
nginxPort: string
|
nginxPort: string
|
||||||
|
apachePort: string
|
||||||
|
serverType: 'nginx' | 'apache'
|
||||||
phpPort: string
|
phpPort: string
|
||||||
mariaDbPort: string
|
mariaDbPort: string
|
||||||
mariaDbPorts?: Record<string, string>
|
mariaDbPorts?: Record<string, string>
|
||||||
@@ -102,6 +104,7 @@ function App(): JSX.Element {
|
|||||||
const { t, i18n } = useTranslation()
|
const { t, i18n } = useTranslation()
|
||||||
const [status, setStatus] = useState<ServiceStatus>({
|
const [status, setStatus] = useState<ServiceStatus>({
|
||||||
nginx: 'stopped',
|
nginx: 'stopped',
|
||||||
|
apache: 'stopped',
|
||||||
php: 'stopped',
|
php: 'stopped',
|
||||||
mariadb: 'stopped'
|
mariadb: 'stopped'
|
||||||
})
|
})
|
||||||
@@ -112,11 +115,14 @@ function App(): JSX.Element {
|
|||||||
const [phpVersions, setPhpVersions] = useState<any[]>([])
|
const [phpVersions, setPhpVersions] = useState<any[]>([])
|
||||||
const [mariaDbVersions, setMariaDbVersions] = useState<any[]>([])
|
const [mariaDbVersions, setMariaDbVersions] = useState<any[]>([])
|
||||||
const [nginxVersions, setNginxVersions] = useState<any[]>([])
|
const [nginxVersions, setNginxVersions] = useState<any[]>([])
|
||||||
|
const [apacheVersions, setApacheVersions] = useState<any[]>([])
|
||||||
const [isProjectDialogOpen, setIsProjectDialogOpen] = useState(false)
|
const [isProjectDialogOpen, setIsProjectDialogOpen] = useState(false)
|
||||||
const slugify = (text: string) => text.toLowerCase().trim().replace(/[^\w\s-]/g, '').replace(/[\s_-]+/g, '-').replace(/^-+|-+$/g, '');
|
const slugify = (text: string) => text.toLowerCase().trim().replace(/[^\w\s-]/g, '').replace(/[\s_-]+/g, '-').replace(/^-+|-+$/g, '');
|
||||||
const [newProject, setNewProject] = useState<any>({ name: '', path: '', phpVersion: '', mariaDbVersion: '', host: 'localhost' })
|
const [newProject, setNewProject] = useState<any>({ name: '', path: '', phpVersion: '', mariaDbVersion: '', host: 'localhost', serverType: 'nginx' })
|
||||||
const [settings, setSettings] = useState<AppSettings>({
|
const [settings, setSettings] = useState<AppSettings>({
|
||||||
nginxPort: '80',
|
nginxPort: '80',
|
||||||
|
apachePort: '8080',
|
||||||
|
serverType: 'nginx',
|
||||||
phpPort: '9001',
|
phpPort: '9001',
|
||||||
mariaDbPort: '3306',
|
mariaDbPort: '3306',
|
||||||
allowRemoteAccess: false,
|
allowRemoteAccess: false,
|
||||||
@@ -151,7 +157,7 @@ function App(): JSX.Element {
|
|||||||
const filePath = match[1].replace(/\\/g, '/')
|
const filePath = match[1].replace(/\\/g, '/')
|
||||||
const line = parseInt(match[2])
|
const line = parseInt(match[2])
|
||||||
const fileName = filePath.split('/').pop() || ''
|
const fileName = filePath.split('/').pop() || ''
|
||||||
const isProjectConfig = filePath.includes('/generated_configs/projects/')
|
const isProjectConfig = filePath.includes('/generated_configs/projects/') || filePath.includes('/generated_configs/projects_apache/')
|
||||||
const host = isProjectConfig ? fileName.replace('.conf', '') : null
|
const host = isProjectConfig ? fileName.replace('.conf', '') : null
|
||||||
return { filePath, line, host }
|
return { filePath, line, host }
|
||||||
}
|
}
|
||||||
@@ -198,6 +204,12 @@ function App(): JSX.Element {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const fetchApacheVersions = async () => {
|
||||||
|
if (!window.api || !window.api.getApacheVersions) return
|
||||||
|
const v = await window.api.getApacheVersions()
|
||||||
|
setApacheVersions(v || [])
|
||||||
|
}
|
||||||
|
|
||||||
const fetchLogs = async (service: string) => {
|
const fetchLogs = async (service: string) => {
|
||||||
if (window.api && window.api.getServiceLogs) {
|
if (window.api && window.api.getServiceLogs) {
|
||||||
const list = await window.api.getServiceLogs(service)
|
const list = await window.api.getServiceLogs(service)
|
||||||
@@ -238,6 +250,7 @@ function App(): JSX.Element {
|
|||||||
fetchPhpVersions()
|
fetchPhpVersions()
|
||||||
fetchMariaDbVersions()
|
fetchMariaDbVersions()
|
||||||
fetchNginxVersions()
|
fetchNginxVersions()
|
||||||
|
fetchApacheVersions()
|
||||||
fetchSettings()
|
fetchSettings()
|
||||||
fetchPmaStatus()
|
fetchPmaStatus()
|
||||||
|
|
||||||
@@ -259,6 +272,12 @@ function App(): JSX.Element {
|
|||||||
setNginxVersions(prev => prev.map(v => v.id === id ? { ...v, progress, status: progress === 100 ? 'installed' : 'downloading' } : v))
|
setNginxVersions(prev => prev.map(v => v.id === id ? { ...v, progress, status: progress === 100 ? 'installed' : 'downloading' } : v))
|
||||||
}) : null
|
}) : null
|
||||||
|
|
||||||
|
const removeApacheListener = window.api.onApacheProgress ? window.api.onApacheProgress((data) => {
|
||||||
|
if (!data) return
|
||||||
|
const { id, progress } = data
|
||||||
|
setApacheVersions(prev => prev.map(v => v.id === id ? { ...v, progress, status: progress === 100 ? 'installed' : 'downloading' } : v))
|
||||||
|
}) : null
|
||||||
|
|
||||||
const removePmaListener = window.api.onPmaProgress ? window.api.onPmaProgress((p: number) => {
|
const removePmaListener = window.api.onPmaProgress ? window.api.onPmaProgress((p: number) => {
|
||||||
setPmaProgress(p)
|
setPmaProgress(p)
|
||||||
}) : null
|
}) : null
|
||||||
@@ -284,6 +303,7 @@ function App(): JSX.Element {
|
|||||||
if (typeof removePhpListener === 'function') removePhpListener()
|
if (typeof removePhpListener === 'function') removePhpListener()
|
||||||
if (typeof removeMariaDbListener === 'function') removeMariaDbListener()
|
if (typeof removeMariaDbListener === 'function') removeMariaDbListener()
|
||||||
if (typeof removeNginxListener === 'function') removeNginxListener()
|
if (typeof removeNginxListener === 'function') removeNginxListener()
|
||||||
|
if (typeof removeApacheListener === 'function') removeApacheListener()
|
||||||
if (typeof removePmaListener === 'function') removePmaListener()
|
if (typeof removePmaListener === 'function') removePmaListener()
|
||||||
}
|
}
|
||||||
}, [logDialogOpen, selectedService])
|
}, [logDialogOpen, selectedService])
|
||||||
@@ -392,7 +412,7 @@ function App(): JSX.Element {
|
|||||||
|
|
||||||
const handleStartAll = async () => {
|
const handleStartAll = async () => {
|
||||||
if (!window.api) return
|
if (!window.api) return
|
||||||
const criticalServices = ['nginx']
|
const criticalServices = [settings.serverType]
|
||||||
for (const s of criticalServices) {
|
for (const s of criticalServices) {
|
||||||
if (status[s] === 'stopped') setStatus(prev => ({ ...prev, [s]: 'starting' }))
|
if (status[s] === 'stopped') setStatus(prev => ({ ...prev, [s]: 'starting' }))
|
||||||
}
|
}
|
||||||
@@ -419,7 +439,7 @@ function App(): JSX.Element {
|
|||||||
|
|
||||||
const handleStopAll = async () => {
|
const handleStopAll = async () => {
|
||||||
if (!window.api) return
|
if (!window.api) return
|
||||||
const criticalServices = ['nginx']
|
const criticalServices = ['nginx', 'apache']
|
||||||
const phpToStop = Object.keys(status).filter(s => s.startsWith('php:') && (status[s] === 'running' || status[s] === 'starting'))
|
const phpToStop = Object.keys(status).filter(s => s.startsWith('php:') && (status[s] === 'running' || status[s] === 'starting'))
|
||||||
const mariaDbToStop = Object.keys(status).filter(s => (s.startsWith('mariadb:') || s.startsWith('mysql:')) && (status[s] === 'running' || status[s] === 'starting'))
|
const mariaDbToStop = Object.keys(status).filter(s => (s.startsWith('mariadb:') || s.startsWith('mysql:')) && (status[s] === 'running' || status[s] === 'starting'))
|
||||||
|
|
||||||
@@ -587,6 +607,17 @@ function App(): JSX.Element {
|
|||||||
fetchNginxVersions()
|
fetchNginxVersions()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleDownloadApache = async (id: string) => {
|
||||||
|
if (!window.api) return
|
||||||
|
const result = await window.api.downloadApacheVersion(id)
|
||||||
|
setNotification({
|
||||||
|
open: true,
|
||||||
|
message: result.message,
|
||||||
|
severity: result.success ? 'success' : 'error'
|
||||||
|
})
|
||||||
|
fetchApacheVersions()
|
||||||
|
}
|
||||||
|
|
||||||
const handleSetupPma = async () => {
|
const handleSetupPma = async () => {
|
||||||
if (!window.api) return
|
if (!window.api) return
|
||||||
setPmaLoading(true)
|
setPmaLoading(true)
|
||||||
@@ -605,7 +636,8 @@ function App(): JSX.Element {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const handleOpenPma = () => {
|
const handleOpenPma = () => {
|
||||||
window.open(`http://localhost:${settings.nginxPort}/phpmyadmin`, '_blank')
|
const port = settings.serverType === 'apache' ? settings.apachePort : settings.nginxPort
|
||||||
|
window.open(`http://localhost:${port}/phpmyadmin`, '_blank')
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleOpenPhpExtensions = (version: string) => {
|
const handleOpenPhpExtensions = (version: string) => {
|
||||||
@@ -712,6 +744,7 @@ function App(): JSX.Element {
|
|||||||
<Tab label="PHP" icon={<PhpIcon />} iconPosition="start" />
|
<Tab label="PHP" icon={<PhpIcon />} iconPosition="start" />
|
||||||
<Tab label="MariaDB" icon={<DbIcon />} iconPosition="start" />
|
<Tab label="MariaDB" icon={<DbIcon />} iconPosition="start" />
|
||||||
<Tab label="Nginx" icon={<WebIcon />} iconPosition="start" />
|
<Tab label="Nginx" icon={<WebIcon />} iconPosition="start" />
|
||||||
|
<Tab label="Apache" icon={<ServerIcon />} iconPosition="start" />
|
||||||
<Tab label="Sunucu Ayarları" icon={<ServerIcon />} iconPosition="start" />
|
<Tab label="Sunucu Ayarları" icon={<ServerIcon />} iconPosition="start" />
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</Box>
|
</Box>
|
||||||
@@ -865,10 +898,58 @@ function App(): JSX.Element {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{settingsTab === 3 && (
|
{settingsTab === 3 && (
|
||||||
|
<Paper sx={{ bgcolor: 'rgba(255, 255, 255, 0.03)', borderRadius: 2 }}>
|
||||||
|
<Box sx={{ p: 2, borderBottom: '1px solid rgba(255, 255, 255, 0.05)' }}>
|
||||||
|
<Typography variant="subtitle1" sx={{ fontWeight: 'bold' }}>Apache Web Sunucusu</Typography>
|
||||||
|
<Typography variant="body2" color="text.secondary">Apache HTTP Server binary dosyalarını buradan yönetebilirsiniz.</Typography>
|
||||||
|
</Box>
|
||||||
|
<List>
|
||||||
|
{apacheVersions.map((v) => (
|
||||||
|
<ListItem key={v.id} sx={{ py: 2 }}>
|
||||||
|
<ListItemIcon>
|
||||||
|
{v.status === 'installed' ? <InstalledIcon color="success" /> : <AvailableIcon color="action" />}
|
||||||
|
</ListItemIcon>
|
||||||
|
<ListItemText
|
||||||
|
primary={`Apache ${v.version}`}
|
||||||
|
secondary={v.status === 'downloading' ? `İndiriliyor... %${Math.round(v.progress)}` : v.status === 'installed' ? 'Kurulu' : 'İndirilebilir'}
|
||||||
|
sx={{ flexGrow: 1 }}
|
||||||
|
/>
|
||||||
|
<Box sx={{ minWidth: 120, textAlign: 'right' }}>
|
||||||
|
{v.status === 'available' && (
|
||||||
|
<Button variant="outlined" size="small" startIcon={<DownloadIcon />} onClick={() => handleDownloadApache(v.id)}>
|
||||||
|
İNDİR
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
{v.status === 'downloading' && (
|
||||||
|
<Box sx={{ width: '100%', mt: 1 }}>
|
||||||
|
<LinearProgress variant="determinate" value={v.progress} sx={{ height: 8, borderRadius: 5 }} />
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
{v.status === 'installed' && (
|
||||||
|
<Chip label="HAZIR" color="success" variant="outlined" size="small" />
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
</ListItem>
|
||||||
|
))}
|
||||||
|
</List>
|
||||||
|
</Paper>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{settingsTab === 4 && (
|
||||||
<Box component={Stack} spacing={3}>
|
<Box component={Stack} spacing={3}>
|
||||||
<Paper sx={{ p: 3, bgcolor: 'rgba(255, 255, 255, 0.03)', borderRadius: 2 }}>
|
<Paper sx={{ p: 3, bgcolor: 'rgba(255, 255, 255, 0.03)', borderRadius: 2 }}>
|
||||||
<Typography variant="h6" gutterBottom>Port Ayarları</Typography>
|
<Typography variant="h6" gutterBottom>Port Ayarları</Typography>
|
||||||
<Box sx={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 3, mt: 2 }}>
|
<Box sx={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 3, mt: 2 }}>
|
||||||
|
<FormControl variant="filled" size="small" fullWidth>
|
||||||
|
<InputLabel>Varsayılan Web Sunucusu</InputLabel>
|
||||||
|
<Select
|
||||||
|
value={settings.serverType || 'nginx'}
|
||||||
|
onChange={(e) => setSettings({ ...settings, serverType: e.target.value as any })}
|
||||||
|
>
|
||||||
|
<MenuItem value="nginx">Nginx (Performans)</MenuItem>
|
||||||
|
<MenuItem value="apache">Apache (htaccess Desteği)</MenuItem>
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
<TextField
|
<TextField
|
||||||
label="Nginx Portu"
|
label="Nginx Portu"
|
||||||
value={settings.nginxPort}
|
value={settings.nginxPort}
|
||||||
@@ -877,6 +958,14 @@ function App(): JSX.Element {
|
|||||||
size="small"
|
size="small"
|
||||||
helperText="Varsayılan: 80"
|
helperText="Varsayılan: 80"
|
||||||
/>
|
/>
|
||||||
|
<TextField
|
||||||
|
label="Apache Portu"
|
||||||
|
value={settings.apachePort}
|
||||||
|
onChange={(e) => setSettings({ ...settings, apachePort: e.target.value })}
|
||||||
|
variant="filled"
|
||||||
|
size="small"
|
||||||
|
helperText="Varsayılan: 8080"
|
||||||
|
/>
|
||||||
<TextField
|
<TextField
|
||||||
label="PHP FastCGI Portu"
|
label="PHP FastCGI Portu"
|
||||||
value={settings.phpPort}
|
value={settings.phpPort}
|
||||||
@@ -931,6 +1020,7 @@ function App(): JSX.Element {
|
|||||||
<Tabs value={dashboardTab} onChange={(_e, v) => setDashboardTab(v)} textColor="primary" indicatorColor="primary">
|
<Tabs value={dashboardTab} onChange={(_e, v) => setDashboardTab(v)} textColor="primary" indicatorColor="primary">
|
||||||
<Tab label={t('common.all')} />
|
<Tab label={t('common.all')} />
|
||||||
<Tab label="Nginx" />
|
<Tab label="Nginx" />
|
||||||
|
<Tab label="Apache" />
|
||||||
<Tab label="PHP" />
|
<Tab label="PHP" />
|
||||||
<Tab label="MariaDB" />
|
<Tab label="MariaDB" />
|
||||||
</Tabs>
|
</Tabs>
|
||||||
@@ -991,8 +1081,41 @@ function App(): JSX.Element {
|
|||||||
</Grid>
|
</Grid>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Apache Box */}
|
||||||
|
{(dashboardTab === 0 || dashboardTab === 2) && (
|
||||||
|
<Grid item xs={12} sm={6} md={4}>
|
||||||
|
<Paper elevation={4} sx={{
|
||||||
|
p: 3, borderRadius: 3,
|
||||||
|
bgcolor: 'rgba(255, 255, 255, 0.03)',
|
||||||
|
backdropFilter: 'blur(10px)',
|
||||||
|
border: '1px solid rgba(255, 255, 255, 0.05)',
|
||||||
|
transition: 'transform 0.2s',
|
||||||
|
'&:hover': { transform: 'scale(1.02)' }
|
||||||
|
}}>
|
||||||
|
<Box sx={{ display: 'flex', justifyContent: 'space-between', mb: 2 }}>
|
||||||
|
<ServerIcon color="primary" sx={{ fontSize: 40 }} />
|
||||||
|
{getStatusChip(status.apache)}
|
||||||
|
</Box>
|
||||||
|
<Typography variant="h6" sx={{ fontWeight: 'bold' }}>Apache</Typography>
|
||||||
|
<Typography variant="body2" color="text.secondary" sx={{ mb: 2 }}>Port: {settings.apachePort}</Typography>
|
||||||
|
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||||
|
<Box sx={{ display: 'flex', gap: 1 }}>
|
||||||
|
<IconButton onClick={() => handleOpenLogs('apache')} title="Hata Logları" size="small" sx={{ bgcolor: 'rgba(255,255,255,0.05)' }}>
|
||||||
|
<TerminalIcon fontSize="small" />
|
||||||
|
</IconButton>
|
||||||
|
</Box>
|
||||||
|
<Switch
|
||||||
|
checked={status.apache === 'running' || status.apache === 'starting'}
|
||||||
|
onChange={() => handleToggleService('apache')}
|
||||||
|
disabled={status.apache === 'starting'}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Paper>
|
||||||
|
</Grid>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* PHP Boxes */}
|
{/* PHP Boxes */}
|
||||||
{(dashboardTab === 0 || dashboardTab === 2) && installedPhpVersions.map(v => (
|
{(dashboardTab === 0 || dashboardTab === 3) && installedPhpVersions.map(v => (
|
||||||
<Grid item xs={12} sm={6} md={4} key={v.id}>
|
<Grid item xs={12} sm={6} md={4} key={v.id}>
|
||||||
<Paper elevation={4} sx={{
|
<Paper elevation={4} sx={{
|
||||||
p: 3, borderRadius: 3,
|
p: 3, borderRadius: 3,
|
||||||
@@ -1023,7 +1146,7 @@ function App(): JSX.Element {
|
|||||||
))}
|
))}
|
||||||
|
|
||||||
{/* MariaDB Boxes */}
|
{/* MariaDB Boxes */}
|
||||||
{(dashboardTab === 0 || dashboardTab === 3) && mariaDbVersions.filter(v => v.status === 'installed').map(v => (
|
{(dashboardTab === 0 || dashboardTab === 4) && mariaDbVersions.filter(v => v.status === 'installed').map(v => (
|
||||||
<Grid item xs={12} sm={6} md={4} key={v.id}>
|
<Grid item xs={12} sm={6} md={4} key={v.id}>
|
||||||
<Paper elevation={4} sx={{
|
<Paper elevation={4} sx={{
|
||||||
p: 3, borderRadius: 3,
|
p: 3, borderRadius: 3,
|
||||||
@@ -1255,6 +1378,12 @@ function App(): JSX.Element {
|
|||||||
label={`PHP ${project.phpVersion}`}
|
label={`PHP ${project.phpVersion}`}
|
||||||
sx={{ bgcolor: 'rgba(71, 74, 255, 0.1)', color: '#777bb4', fontWeight: 600, border: '1px solid rgba(71, 74, 255, 0.2)' }}
|
sx={{ bgcolor: 'rgba(71, 74, 255, 0.1)', color: '#777bb4', fontWeight: 600, border: '1px solid rgba(71, 74, 255, 0.2)' }}
|
||||||
/>
|
/>
|
||||||
|
<Chip
|
||||||
|
size="small"
|
||||||
|
icon={project.serverType === 'apache' ? <ServerIcon sx={{ fontSize: '1rem !important' }} /> : <WebIcon sx={{ fontSize: '1rem !important' }} />}
|
||||||
|
label={project.serverType === 'apache' ? 'Apache' : 'Nginx'}
|
||||||
|
sx={{ bgcolor: 'rgba(255, 255, 255, 0.05)', color: '#fff', fontWeight: 600 }}
|
||||||
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Paper variant="outlined" sx={{
|
<Paper variant="outlined" sx={{
|
||||||
@@ -1275,7 +1404,10 @@ function App(): JSX.Element {
|
|||||||
<Button
|
<Button
|
||||||
size="small"
|
size="small"
|
||||||
endIcon={<LaunchIcon />}
|
endIcon={<LaunchIcon />}
|
||||||
onClick={() => window.open(`http://localhost:${settings.nginxPort}/${project.host}/`, '_blank')}
|
onClick={() => {
|
||||||
|
const port = project.serverType === 'apache' ? settings.apachePort : settings.nginxPort
|
||||||
|
window.open(`http://localhost:${port}/${project.host}/`, '_blank')
|
||||||
|
}}
|
||||||
sx={{
|
sx={{
|
||||||
color: '#39A7FF',
|
color: '#39A7FF',
|
||||||
textTransform: 'none',
|
textTransform: 'none',
|
||||||
@@ -1422,7 +1554,7 @@ function App(): JSX.Element {
|
|||||||
|
|
||||||
<Dialog open={isProjectDialogOpen} onClose={() => {
|
<Dialog open={isProjectDialogOpen} onClose={() => {
|
||||||
setIsProjectDialogOpen(false)
|
setIsProjectDialogOpen(false)
|
||||||
setNewProject({ name: '', path: '', phpVersion: '', mariaDbVersion: '', host: 'localhost' })
|
setNewProject({ name: '', path: '', phpVersion: '', mariaDbVersion: '', host: 'localhost', serverType: 'nginx' })
|
||||||
}} fullWidth maxWidth="sm">
|
}} fullWidth maxWidth="sm">
|
||||||
<DialogTitle>{newProject.id ? 'Proje Düzenle' : 'Yeni Proje Ekle'}</DialogTitle>
|
<DialogTitle>{newProject.id ? 'Proje Düzenle' : 'Yeni Proje Ekle'}</DialogTitle>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
@@ -1471,12 +1603,24 @@ function App(): JSX.Element {
|
|||||||
)}
|
)}
|
||||||
</Select>
|
</Select>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
<FormControl fullWidth>
|
||||||
|
<InputLabel id="server-type-label">Web Sunucusu</InputLabel>
|
||||||
|
<Select
|
||||||
|
labelId="server-type-label"
|
||||||
|
value={newProject.serverType || 'nginx'}
|
||||||
|
label="Web Sunucusu"
|
||||||
|
onChange={(e) => setNewProject({ ...newProject, serverType: e.target.value as any })}
|
||||||
|
>
|
||||||
|
<MenuItem value="nginx">Nginx</MenuItem>
|
||||||
|
<MenuItem value="apache">Apache (htaccess desteği)</MenuItem>
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
</Box>
|
</Box>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
<Button onClick={() => {
|
<Button onClick={() => {
|
||||||
setIsProjectDialogOpen(false)
|
setIsProjectDialogOpen(false)
|
||||||
setNewProject({ name: '', path: '', phpVersion: '', mariaDbVersion: '', host: 'localhost' })
|
setNewProject({ name: '', path: '', phpVersion: '', mariaDbVersion: '', host: 'localhost', serverType: 'nginx' })
|
||||||
}}>İptal</Button>
|
}}>İptal</Button>
|
||||||
<Button
|
<Button
|
||||||
onClick={handleAddProject}
|
onClick={handleAddProject}
|
||||||
|
|||||||
Reference in New Issue
Block a user