Full codebase including API server (32 modules + routes), dashboard frontend, DashCA certificate distribution, installer script, and deployment skills.
206 lines
7.2 KiB
PowerShell
206 lines
7.2 KiB
PowerShell
# Caddy Configuration Manager for Windows
|
|
# This script adds new service configurations to your Caddyfile
|
|
|
|
param(
|
|
[string]$Config,
|
|
[string]$Subdomain,
|
|
[string]$CaddyfilePath = "C:\caddy\Caddyfile",
|
|
[bool]$ReloadCaddy = $true
|
|
)
|
|
|
|
# Function to write JSON response
|
|
function Write-JsonResponse {
|
|
param($Status, $Message, $Data = $null)
|
|
|
|
$response = @{
|
|
status = $Status
|
|
message = $Message
|
|
timestamp = (Get-Date).ToString("yyyy-MM-dd HH:mm:ss")
|
|
}
|
|
|
|
if ($Data) {
|
|
$response.data = $Data
|
|
}
|
|
|
|
return $response | ConvertTo-Json
|
|
}
|
|
|
|
# Function to extract CA names from Caddyfile
|
|
function Get-CaddyfileCAs {
|
|
param([string]$CaddyfilePath)
|
|
|
|
try {
|
|
Write-Host "DEBUG: Checking file path: $CaddyfilePath"
|
|
|
|
if (-not (Test-Path $CaddyfilePath)) {
|
|
Write-Host "DEBUG: File not found"
|
|
return @()
|
|
}
|
|
|
|
$content = Get-Content $CaddyfilePath -Raw
|
|
Write-Host "DEBUG: File content length: $($content.Length)"
|
|
Write-Host "DEBUG: First 200 chars: $($content.Substring(0, [Math]::Min(200, $content.Length)))"
|
|
|
|
$caNames = @()
|
|
|
|
# Pattern 1: PKI block with CA definitions - pki { ca ca_name { name "Friendly Name" } }
|
|
$pkiPattern = 'pki\s*\{[^}]*?ca\s+([^\s\{]+)\s*\{([^}]*?)\}'
|
|
Write-Host "DEBUG: Searching for PKI pattern: $pkiPattern"
|
|
|
|
$pkiMatches = [regex]::Matches($content, $pkiPattern, [System.Text.RegularExpressions.RegexOptions]::IgnoreCase -bor [System.Text.RegularExpressions.RegexOptions]::Singleline)
|
|
Write-Host "DEBUG: PKI matches found: $($pkiMatches.Count)"
|
|
|
|
foreach ($match in $pkiMatches) {
|
|
$caId = $match.Groups[1].Value
|
|
$caBlock = $match.Groups[2].Value
|
|
Write-Host "DEBUG: Found CA ID: $caId"
|
|
Write-Host "DEBUG: CA Block: $caBlock"
|
|
|
|
# Try to extract the friendly name from within the CA block
|
|
$nameMatch = [regex]::Match($caBlock, 'name\s+"([^"]+)"', [System.Text.RegularExpressions.RegexOptions]::IgnoreCase)
|
|
if ($nameMatch.Success) {
|
|
$friendlyName = $nameMatch.Groups[1].Value
|
|
Write-Host "DEBUG: Found friendly name: $friendlyName"
|
|
$caNames += "$caId ($friendlyName)"
|
|
} else {
|
|
Write-Host "DEBUG: No friendly name found, using ID only"
|
|
$caNames += $caId
|
|
}
|
|
}
|
|
|
|
# Pattern 2: tls { ca ca_name }
|
|
$matches1 = [regex]::Matches($content, 'tls\s*\{\s*ca\s+([^\s\}]+)', [System.Text.RegularExpressions.RegexOptions]::IgnoreCase)
|
|
Write-Host "DEBUG: TLS block matches: $($matches1.Count)"
|
|
foreach ($match in $matches1) {
|
|
$caNames += $match.Groups[1].Value
|
|
}
|
|
|
|
# Pattern 3: tls ca_name (direct)
|
|
$matches2 = [regex]::Matches($content, 'tls\s+([^\s\{]+)', [System.Text.RegularExpressions.RegexOptions]::IgnoreCase)
|
|
Write-Host "DEBUG: Direct TLS matches: $($matches2.Count)"
|
|
foreach ($match in $matches2) {
|
|
$caName = $match.Groups[1].Value
|
|
if ($caName -ne "internal" -and $caName -ne "off") {
|
|
$caNames += $caName
|
|
}
|
|
}
|
|
|
|
Write-Host "DEBUG: Total CAs found: $($caNames.Count)"
|
|
Write-Host "DEBUG: CA list: $($caNames -join ', ')"
|
|
|
|
# Remove duplicates and return
|
|
return $caNames | Sort-Object | Get-Unique
|
|
}
|
|
catch {
|
|
Write-Host "DEBUG: Exception in Get-CaddyfileCAs: $($_.Exception.Message)"
|
|
Write-Host "Error reading CAs from Caddyfile: $($_.Exception.Message)"
|
|
return @()
|
|
}
|
|
}
|
|
|
|
# Handle get-cas command
|
|
if ($args[0] -eq "get-cas") {
|
|
$CaddyfilePath = if ($args[1]) { $args[1] } else { "C:\caddy\Caddyfile" }
|
|
|
|
Write-Host "DEBUG: get-cas command received"
|
|
Write-Host "DEBUG: Caddyfile path: $CaddyfilePath"
|
|
Write-Host "DEBUG: File exists: $(Test-Path $CaddyfilePath)"
|
|
|
|
try {
|
|
$cas = Get-CaddyfileCAs -CaddyfilePath $CaddyfilePath
|
|
|
|
Write-Host "DEBUG: CAs found: $($cas -join ', ')"
|
|
|
|
$response = @{
|
|
status = "success"
|
|
message = "CAs retrieved successfully"
|
|
data = @{
|
|
cas = $cas
|
|
count = $cas.Count
|
|
caddyfilePath = $CaddyfilePath
|
|
}
|
|
timestamp = (Get-Date).ToString("yyyy-MM-dd HH:mm:ss")
|
|
}
|
|
|
|
Write-Output ($response | ConvertTo-Json)
|
|
exit 0
|
|
}
|
|
catch {
|
|
Write-Host "DEBUG: Error occurred: $($_.Exception.Message)"
|
|
|
|
$response = @{
|
|
status = "error"
|
|
message = "Failed to retrieve CAs: $($_.Exception.Message)"
|
|
timestamp = (Get-Date).ToString("yyyy-MM-dd HH:mm:ss")
|
|
}
|
|
|
|
Write-Output ($response | ConvertTo-Json)
|
|
exit 1
|
|
}
|
|
}
|
|
|
|
# Main configuration addition logic
|
|
try {
|
|
# Validate required parameters for config addition
|
|
if (-not $Config -or -not $Subdomain) {
|
|
Write-Output (Write-JsonResponse "error" "Config and Subdomain parameters are required")
|
|
exit 1
|
|
}
|
|
|
|
# Check if Caddyfile exists
|
|
if (-not (Test-Path $CaddyfilePath)) {
|
|
Write-Output (Write-JsonResponse "error" "Caddyfile not found at: $CaddyfilePath")
|
|
exit 1
|
|
}
|
|
|
|
# Read existing Caddyfile
|
|
$existingConfig = Get-Content $CaddyfilePath -Raw -ErrorAction Stop
|
|
|
|
# Check if subdomain already exists
|
|
if ($existingConfig -match "$Subdomain\.sami\s*\{") {
|
|
Write-Output (Write-JsonResponse "error" "Subdomain '$Subdomain.sami' already exists in Caddyfile")
|
|
exit 1
|
|
}
|
|
|
|
# Create backup
|
|
$backupPath = "$CaddyfilePath.backup.$(Get-Date -Format 'yyyyMMdd-HHmmss')"
|
|
Copy-Item $CaddyfilePath $backupPath -ErrorAction Stop
|
|
Write-Host "Backup created: $backupPath"
|
|
|
|
# Append new configuration
|
|
$newContent = $existingConfig.TrimEnd() + "`n`n" + $Config.TrimEnd() + "`n"
|
|
Set-Content -Path $CaddyfilePath -Value $newContent -NoNewline -ErrorAction Stop
|
|
|
|
Write-Host "Configuration added successfully"
|
|
|
|
# Reload Caddy if requested
|
|
if ($ReloadCaddy) {
|
|
Write-Host "Reloading Caddy..."
|
|
|
|
# Try to reload Caddy
|
|
$reloadResult = & caddy reload --config $CaddyfilePath 2>&1
|
|
|
|
if ($LASTEXITCODE -eq 0) {
|
|
Write-Host "Caddy reloaded successfully"
|
|
Write-Output (Write-JsonResponse "success" "Configuration added and Caddy reloaded successfully" @{
|
|
backup = $backupPath
|
|
subdomain = "$Subdomain.sami"
|
|
})
|
|
} else {
|
|
Write-Host "Caddy reload failed: $reloadResult"
|
|
# Restore backup if reload failed
|
|
Copy-Item $backupPath $CaddyfilePath -Force
|
|
Write-Output (Write-JsonResponse "error" "Caddy reload failed. Configuration rolled back. Error: $reloadResult")
|
|
exit 1
|
|
}
|
|
} else {
|
|
Write-Output (Write-JsonResponse "success" "Configuration added successfully (Caddy not reloaded)" @{
|
|
backup = $backupPath
|
|
subdomain = "$Subdomain.sami"
|
|
})
|
|
}
|
|
|
|
} catch {
|
|
Write-Output (Write-JsonResponse "error" "Error: $($_.Exception.Message)")
|
|
exit 1
|
|
} |