- Code\rebar_pipeline, review_pipeline, pier-program을 정션에서 실제 폴더로 전환 - Code\rhino_mcp 및 CLAUDE_rhino배경.md 추가, .mcp.json은 PYTHONPATH로 새 위치 사용 - pier-program 내부 .git 제거(이력은 backup\pier-program_20261028.bundle에 보관) - worktree 가이드 반영(CLAUDE.local.md, HANDOFF.md) - D:\Rhino-MCP 경로 참조를 새 위치로 치환 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
82 lines
4.5 KiB
PowerShell
82 lines
4.5 KiB
PowerShell
$ErrorActionPreference = 'Stop'
|
|
$admin = 'http://localhost:2019'
|
|
$id = 'drawing-model-box-sample-deployment'
|
|
$public = 'http://10.14.12.22/box-sample/'
|
|
$created = $false
|
|
function Test-StaticFallback($routes) {
|
|
if (@($routes).Count -eq 0) { return $false }
|
|
foreach ($r in $routes) {
|
|
if (@($r.handle).Count -eq 0) { return $false }
|
|
foreach ($h in $r.handle) {
|
|
if ($h.handler -eq 'static_response') { continue }
|
|
if ($h.handler -eq 'subroute' -and (Test-StaticFallback $h.routes)) { continue }
|
|
return $false
|
|
}
|
|
}
|
|
return $true
|
|
}
|
|
try {
|
|
$response = Invoke-WebRequest -UseBasicParsing "$admin/config/"
|
|
$config = $response.Content | ConvertFrom-Json
|
|
if (-not $response.Headers['ETag']) { throw 'Caddy configuration ETag is required.' }
|
|
$servers = @($config.apps.http.servers.PSObject.Properties | Where-Object {
|
|
@($_.Value.listen | Where-Object { $_ -match '(^|:)80$' }).Count -gt 0
|
|
})
|
|
if ($servers.Count -ne 1) { throw 'Cannot uniquely identify the existing HTTP port 80 server.' }
|
|
$server = $servers[0]
|
|
$routes = @($server.Value.routes)
|
|
$own = @($routes | Where-Object { $_.'@id' -eq $id })
|
|
$route = @{
|
|
'@id' = $id
|
|
match = @(@{host=@('10.14.12.22'); path=@('/box-sample','/box-sample/*')})
|
|
handle = @(@{handler='reverse_proxy'; upstreams=@(@{dial='127.0.0.1:5092'})})
|
|
terminal = $true
|
|
}
|
|
if ($own.Count -gt 1) { throw 'Duplicate deployment route.' }
|
|
if ($own.Count -eq 1) {
|
|
if (@($own[0].handle).Count -ne 1 -or $own[0].handle[0].handler -ne 'reverse_proxy' -or
|
|
@($own[0].handle[0].upstreams).Count -ne 1 -or $own[0].handle[0].upstreams[0].dial -ne '127.0.0.1:5092' -or
|
|
(@($own[0].match[0].path) -join ',') -ne '/box-sample,/box-sample/*' -or
|
|
(@($own[0].match[0].host) -join ',') -ne '10.14.12.22' -or -not $own[0].terminal) {
|
|
throw 'Existing deployment route differs; refusing to overwrite it.'
|
|
}
|
|
} else {
|
|
# Preserve every middleware, maintenance and service route in its original
|
|
# order. Insert only before a proven static-only final fallback.
|
|
$insertIndex = $routes.Count - 1
|
|
if ($insertIndex -lt 0 -or $routes[$insertIndex].match -or
|
|
-not (Test-StaticFallback @($routes[$insertIndex]))) {
|
|
throw 'Expected a final unmatched static-only fallback; no settings changed.'
|
|
}
|
|
foreach ($existing in $routes) {
|
|
$json = $existing | ConvertTo-Json -Depth 100 -Compress
|
|
# Do not bypass a shared authentication policy or shadow an existing mount.
|
|
if ($json -match 'authentication|http_basic|forward_auth|/box-sample') {
|
|
throw 'Existing route/authentication needs administrator review; nothing was replaced.'
|
|
}
|
|
}
|
|
$backup = Join-Path $PSScriptRoot ('caddy-before-box-sample-' + (Get-Date -Format 'yyyyMMdd-HHmmss-fff') + '.json')
|
|
[IO.File]::WriteAllText($backup, $response.Content)
|
|
$name = [Uri]::EscapeDataString($server.Name)
|
|
if ($null -eq $server.Value.routes) { throw 'HTTP routes array missing; no automatic replacement allowed.' }
|
|
$body = $route | ConvertTo-Json -Depth 30 -Compress
|
|
Invoke-WebRequest -UseBasicParsing -Method Put -Uri "$admin/config/apps/http/servers/$name/routes/$insertIndex" -Headers @{'If-Match'=$response.Headers['ETag']} -ContentType 'application/json' -Body $body | Out-Null
|
|
$created = $true
|
|
}
|
|
foreach ($path in @('', 'api/status', 'src/app.js')) {
|
|
$check = Invoke-WebRequest -UseBasicParsing -Uri ($public+$path) -TimeoutSec 30
|
|
if ($check.StatusCode -ne 200 -or $check.Headers['X-DMS-Deployment'] -ne 'box-sample') { throw "Public route validation failed: $path" }
|
|
if ($path -eq 'api/status' -and -not ($check.Content | ConvertFrom-Json).ok) { throw 'API status failed.' }
|
|
if ($path -eq 'src/app.js' -and $check.Content -notmatch '/box-sample/api/status') { throw 'Frontend API prefix missing.' }
|
|
}
|
|
Write-Host "VERIFIED: $public (page, API, JavaScript)."
|
|
Write-Host 'Caddy API changes are autosaved; reboot persistence requires Caddy to resume that configuration.'
|
|
} catch {
|
|
if ($created) {
|
|
# Remove only this newly inserted route; never restore over unrelated changes.
|
|
try { Invoke-WebRequest -UseBasicParsing -Method Delete -Uri "$admin/id/$id" | Out-Null } catch { Write-Warning 'Could not roll back this deployment route.' }
|
|
}
|
|
Write-Error $_
|
|
exit 1
|
|
}
|