DudDashboard.psm1
class DUDHotReloadPath { [DUDHotReloadPathItem]$Root [DUDHotReloadPathItem]$Rules } class DUDHotReloadPathItem { [String]$Path [String]$Filter [switch]$Recurse [DashboardAction]$Action [ScriptBlock]$CustomAction [int]$Delay } Function Get-DUDEndpoints(){ $Endpoints = @() Get-ChildItem "$($Cache:dud.Paths.CurrentDashboardFolderFullPath)\Endpoints\*.ps1" -Recurse | % {$Endpoints += (& $_.FullName)} return $Endpoints } function Get-DUDFolders() { # Stylesheets $Output = @{ Pages = @() Stylesheets = @() Theme = & "$($Cache:dud.Paths.Root)\src\Theme.ps1" Scripts = @() } $Functions = Get-ChildItem -Path "$($Cache:dud.Paths.CurrentDashboardFolderFullPath)\Functions" -Filter '*.ps1' -Recurse $Functions | % { . $_.FullName } $FunctionsNames = $Functions | % { [System.IO.Path]::GetFileNameWithoutExtension($_.FullName) } Get-ChildItem "$($Cache:dud.Paths.CurrentDashboardFolderFullPath)\Pages\*.ps1" -Recurse | Sort FullName | % { $Output.Pages += (& $_.FullName) } $ScriptsPath = "$($Cache:dud.Paths.CurrentDashboardFolderFullPath)\Scripts" $ScriptsPath | Copy-Item -Destination "$($Cache:dud.Paths.Root)\client" -Recurse -Force $ScriptsPath = Get-ChildItem "$($Cache:dud.Paths.CurrentDashboardFolderFullPath)\Scripts\*.*" | Sort FullName $ScriptsPath.Name | % { $Output.Scripts += "/scripts/$_" } $StylesPath = "$($Cache:dud.Paths.CurrentDashboardFolderFullPath)\Styles" $StylesPath | Copy-Item -Destination "$($Cache:dud.Paths.Root)\client" -Container -Recurse -Force $StylesPath = Get-ChildItem "$($Cache:dud.Paths.CurrentDashboardFolderFullPath)\Styles\*.*" | Sort FullName $StylesPath.Name | % { $Output.Stylesheets += "/Styles/$_" } return $Output } Function Start-DUDDFilewatcher([DUDHotReloadPath]$Path) { $fileSystemWatcher = New-Object -TypeName 'System.IO.FileSystemWatcher' -ArgumentList $Path.Root.Path, $Path.Root.Filter -Property @{ NotifyFilter = [IO.NotifyFilters]::LastWrite EnableRaisingEvents = $true IncludeSubdirectories = $Path.Root.Recurse } $WatchAction = { try { $Global:DashboardActionDelay.Stop() [DUDHotReloadPathItem]$Private:RuleUsed = $event.MessageData.Root Foreach ($Rule in $event.MessageData.Rules) { if ($event.SourceEventArgs.FullPath -like $Rule.Path ) { $Private:RuleUsed = $Rule break } } if ($Private:RuleUsed.CustomAction -ne $null) {$Global:DashboardActionRulesQueue+= $Private:RuleUsed} $Global:DashboardAction = $Global:DashboardAction -bor $Private:RuleUsed.Action } catch { Write-Host $_.Exception -ForegroundColor Red } Finally { $Global:DashboardActionDelay.Start() } Register-ObjectEvent $fileSystemWatcher Changed -SourceIdentifier $Path.Name -Action $WatchAction -MessageData @{ AppPool = $AppPool Rules = $Path } } } function New-DUDDashboard { [CmdletBinding()] Param([Hashtable]$EndpointInit, [Hashtable]$ExtraParameters) try { $GetSetting = { Param($MySetting, $ParamName) if ($MySetting -ne $null) { $DashboardParams."$ParamName" = $MySetting } } if ($Cache.dud -eq $null) { $Cache:dud.Data = @{ } } $DashboardParams = @{ } $GetSetting.Invoke($Cache:dud.Settings.UDConfig.DashboardTitle, 'Title') $GetSetting.Invoke($Cache:LoginPage, 'LoginPage') $GetSetting.Invoke($Cache:Footer, 'Footer') $GetSetting.Invoke($Cache:Navigation, 'Navigation') $GetSetting.Invoke($Cache:dud.Settings.UDConfig.IdleTimeout, 'IdleTimeout') $Functions = Get-ChildItem -Path "$($Cache:dud.Paths.CurrentDashboardFolderFullPath)\Functions" -Filter '*.ps1' -Recurse $Functions | % { . $_.FullName } $FunctionsNames = $Functions | % { [System.IO.Path]::GetFileNameWithoutExtension($_.FullName) } $DataSourcePath = "$($Cache:dud.Paths.CurrentDashboardFolderFullPath)\Data\$($Cache:dud.Settings.UDConfig.DataSource)" if (Test-Path -Path $DataSourcePath ) { Get-ChildItem -Path $DataSourcePath -Filter '*.ps1' | % { . $_.FullName } } $EIParams = @{ } if ($PSBoundParameters.ContainsKey('EndpointInit')) { $EIParams = $PSBoundParameters.Item('EndpointInit') if ($null -eq $EIParams.Module) { $EIParams.remove('Module') } if ($null -eq $EIParams.Function) { $EIParams.remove('Function') } if ($null -eq $EIParams.Variable) { $EIParams.remove('Variable') } } if ($null -ne $FunctionsNames) { if ($null -ne $EIParams.Function) { $EIParams.function = $EIParams.Function + $FunctionsNames } else { $EIParams.Function = $FunctionsNames } } $EI = New-UDEndpointInitialization @EIParams $Params = Get-DUDFolders $Cache:dud.Params = $Params $DataSourcePath = "$($Cache:dud.Paths.CurrentDashboardFolderFullPath)\Data\$($Cache:dud.Settings.UDConfig.DataSource)" if (Test-Path -Path $DataSourcePath ) { Get-ChildItem -Path $DataSourcePath -Filter '*.ps1' | % { . $_.FullName } } if ($null -eq $Params) { $Params = @{ } } if ($null -eq $ExtraParameters) { $ExtraParameters = @{ } } if ($Cache:dud.Settings.Authentication -ne $null -and ($Cache:LoginPage -eq $null -or $Cache:LoginPage.Gettype().name -ne 'LoginPage')) { throw 'Login page not found' } $Dash = New-UDDashboard @DashboardParams @Params @ExtraParameters -EndpointInitialization $EI $Dash.Design = $Cache:dud.Settings.UDConfig.Design return $Dash } catch { if ($Cache:dud.Settings.UDConfig.Design -eq $true) { $MyError = $_ New-UDDashboard -Title 'Error' -Content { New-UDCard -Title '' -Text ($MyError | format-list -force | Out-String) New-UDCard -Title 'DashboardParams' -Text ($DashboardParams | Out-String) New-UDCard -Title 'Params' -Text ($Params | Out-String) New-UDCard -Title 'ExtraParameters' -Text ($ExtraParameters | Out-String) } } } } #111111 # New-UDFooter Function New-DUDHotReloaderWatchPath([DUDHotReloadPathItem]$Root,[DUDHotReloadPathItem[]]$Rules) { return New-Object -TypeName 'DUDHotReloadPath' -Property @{'Root'=$Root;'Rules'=$Rules} } Function New-DUDHotReloaderWatchPathItem($Path,$Filter,[Switch]$Recurse,[DashboardAction]$Action,[Scriptblock]$CustomAction,[int]$Delay) { return New-Object -TypeName 'DUDHotReloadPathItem' -Property $PSBoundParameters } function Publish-DudDashboard { [CmdletBinding()] param ( [Switch]$Force, [Parameter(Mandatory = $true)] [ValidateScript( { Test-Path $_ -PathType Container })] [String] $Path, [ValidateScript( { Test-Path $_ })] [String]$License ) begin { } process { $ClientPath = Join-Path -Path $Path -ChildPath 'client' $SrcPath = Join-Path -Path $Path -ChildPath 'src' if (-not (Test-Path $SrcPath)) { New-Item $SrcPath -ItemType Directory } $UDModuleLocation = (Get-Module UniversalDashboard -ListAvailable)[0] | Select Path | Split-Path -Parent $DUDModuleLocation = (Get-Module DUDDashboard -ListAvailable)[0] | Select Path | Split-Path -Parent $RootPath = Split-Path -Path $SrcPath -Parent Get-ChildItem $UDModuleLocation | % { Copy-Item -Path $_.FullName -Destination $Path -Container -Recurse -Force:$Force } Get-ChildItem "$DUDModuleLocation\Template" -Exclude 'root' | % { Copy-Item -Path $_.FullName -Destination $SrcPath -Container -Recurse -Force:$Force } Get-ChildItem "$DUDModuleLocation\Template\root" | % { Copy-Item -Path $_.FullName -Destination $RootPath -Container -Recurse -Force:$Force } $AppSettings = Get-content -Path "$RootPath\AppSettings.json" -Raw | ConvertFrom-Json $AppSettings.UDConfig.UpdateToken = New-Guid $AppSettings | ConvertTo-Json | Set-Content "$RootPath\AppSettings.json" #[Void](New-Item -Path "$SrcPath\scripts" -ItemType Directory) #[Void](New-Item -Path "$SrcPath\styles" -ItemType Directory) # [Void](New-Item -Path "$ClientPath\scripts" -ItemType SymbolicLink -Value "$SrcPath\scripts" -Force:$Force) # [Void](New-Item -Path "$ClientPath\styles" -ItemType SymbolicLink -Value "$SrcPath\styles" -Force:$Force) if ($PSBoundParameters.ContainsKey('License')) { Copy-Item -Path $License -Destination (Join-Path -Path $Path -ChildPath "net472\") Copy-Item -Path $License -Destination (Join-Path -Path $Path -ChildPath "netstandard2.0\") } } end { } } Function Set-DUDSettingsCache($Path) { if ($cache:dud -eq $null) { $cache:dud = @{ } $Cache:dud.Paths = @{ } $Cache:dud.Paths.Root = (Get-location).Path } $SettingsPath = if ($path -ne $null) { $Path }else { $Cache:dud.Paths.Root } $Cache:dud.Settings = Get-Content "$SettingsPath\appsettings.json" | ConvertFrom-Json $Cache:dud.Paths = @{ Root = $Cache:dud.Paths.Root CurrentDashboardFolderFullPath = '' CurrentDashboardFullPath = '' CurrentDashboard = '' } $Cache:dud.Paths.CurrentDashboardFolderFullPath = "$($Cache:dud.Paths.Root)\src" $Cache:dud.Paths.CurrentDashboardFullPath = "$($Cache:dud.Paths.Root)\src\Root.ps1" } function Start-DUDDashboard { [CmdletBinding()] param ( [INT]$Port, [Switch]$Wait, $Parameters ) Set-DUDSettingsCache $LoginFilePath = "$($Cache:dud.Paths.Root)\src\Login.ps1" if (Test-Path -Path $LoginFilePath) { $Cache:LoginPage = & $LoginFilePath } $FooterFilePath = "$($Cache:dud.Paths.Root)\src\Footer.ps1" if (Test-Path -Path $FooterFilePath) { $Cache:Footer = & $FooterFilePath } $NavigationFilePath = "$($Cache:dud.Paths.Root)\src\Navigation.ps1" if (Test-Path -Path $NavigationFilePath) { $Cache:Navigation = & $NavigationFilePath } $Endpoints = Get-DUDEndpoints $Params = Get-DUDFolders $Cache:dud.Params = $Params $DashboardStartParams = @{ } if ([String]::IsNullOrWhiteSpace($Cache:dud.Settings.UDConfig.SSLCertificatePath) -eq $false) { $DashboardStartParams.Certificate = Get-ChildItem -Path $Cache:dud.Settings.UDConfig.SSLCertificatePath } $GetSetting = { Param($MySetting, $ParamName) if ($MySetting -ne $null) { $DashboardStartParams."$ParamName" = $MySetting } } $GetSetting.Invoke($Cache:dud.Settings.UDConfig.UpdateToken, 'UpdateToken') $GetSetting.Invoke($Cache:dud.Paths.CurrentDashboardFullPath, 'FilePath') $GetSetting.Invoke($Cache:dud.Settings.UDConfig.Design, 'Design') $DashboardStartParams.Endpoint = $Endpoints if ($null -eq $Parameters) { $Parameters = @{ } } if ($cache:dud.Settings.PublishedFolders -ne $null) { $PublishedFolders = [system.collections.generic.list[psobject]]::new() Foreach ($i in ($cache:dud.Settings.PublishedFolders | Get-Member -MemberType NoteProperty).Name) { $PublishedFolders.Add((Publish-UDFolder -Path $cache:dud.Settings.PublishedFolders."$i" -RequestPath $i)) } if ($PublishedFolders.Count -gt 0) { $DashboardStartParams.Add('PublishedFolder', $PublishedFolders) } } Start-UDDashboard @Parameters @PSBoundParameters @DashboardStartParams } function Start-DUDHotReloader { param( [Parameter(Mandatory = $true)] $Root, [Parameter(Mandatory)] $UpdateToken, [Parameter(Mandatory)] $Url, $AppPool, [int]$UpdateDelay = 750, [String]$DashboardPath, [System.Collections.Generic.List[DUDHotReloadPath]]$AdditionalPaths ) Process { $fileInfo = [System.IO.FileInfo]::new($Root) $fileSystemWatcher = [System.IO.FileSystemWatcher]::new($fileInfo.DirectoryName, "*.*") $fileSystemWatcher.NotifyFilter = [IO.NotifyFilters]::LastWrite $fileSystemWatcher.EnableRaisingEvents = $true $fileSystemWatcher.IncludeSubdirectories = $true $Global:DashboardAction = [DashboardAction]::Undefined $Global:DashboardActionRulesQueue = @() $Global:DashboardActionDelay = New-Object -TypeName 'System.Timers.Timer' -Property @{ AutoReset = $true Interval = $UpdateDelay } Register-ObjectEvent $Global:DashboardActionDelay elapsed -SourceIdentifier 'ActionDelay' -Action { try { $Operation = "" $Token = $event.MessageData.UpdateToken $Url = $event.MessageData.Url switch ($Global:DashboardAction) { { $_ -band [DashboardAction]::Restart } { $Operation = 'Apppool recycled' Import-Module WebAdministration Restart-WebAppPool -Name $event.MessageData.AppPool break } { $_ -band [DashboardAction]::Update } { $Operation = 'Dashboard updated' Update-UDDashboard -Url $Url -UpdateToken $Token -FilePath $event.MessageData.DashboardPath } } if ($Global:DashboardAction -ne [DashboardAction]::Undefined) { Write-Host "$(get-date) $Operation - $Url " -ForegroundColor Cyan } } catch { Write-Host $_.Exception -ForegroundColor Red } Finally { $Global:DashboardAction = [DashboardAction]::Undefined $Global:DashboardActionDelay.Stop() } } -MessageData @{ Url = $Url UpdateToken = $UpdateToken DashboardPath = $DashboardPath AppPool = $AppPool } $WatchAction = { try { $Global:DashboardActionDelay.Stop() $Global:DashboardActionDelay.Start() $CanRestart = -not [String]::IsNullOrWhiteSpace($event.MessageData.AppPool) $RestartAction = [DashboardAction]::Restart if (-not $CanRestart) { $RestartAction = [DashboardAction]::Update } $Private:Root = $event.MessageData.Root $Private:Paths = New-Object -TypeName 'System.Collections.Generic.Dictionary[String,DashboardAction]' $Private:Paths.Add("$($Private:Root)\Dashboard.ps1", $RestartAction) $Private:Paths.Add("$($Private:Root)\AppSettings.json", $RestartAction) $Private:Paths.Add("$($Private:Root)\*\Endpoints\*.ps1", $RestartAction) $Private:Paths.Add("$($Private:Root)\*\Footer.ps1", $RestartAction) $Private:Paths.Add("$($Private:Root)\src\Login.ps1", $RestartAction) $Private:Paths.Add("$($Private:Root)\src\Navigation.ps1", $RestartAction) $Private:Paths.Add("$($Private:Root)\*.ps1", [DashboardAction]::Update) $Private:Paths.Add("$($Private:Root)\src\*.css", [DashboardAction]::Update) $Private:Paths.Add("$($Private:Root)\src\*.js", [DashboardAction]::Update) Foreach ($key in $Private:Paths.Keys) { if ($event.SourceEventArgs.FullPath -like $key) { Write-Host $key -ForegroundColor Red $Global:DashboardAction = $Global:DashboardAction -bor $Private:Paths[$key] break } } } catch { Write-Host $_.Exception -ForegroundColor Red } } Register-ObjectEvent $fileSystemWatcher Changed -SourceIdentifier FileChanged -Action $WatchAction -MessageData @{ Root = $Root EndpointsPath = $EndpointsPath AppPool = $AppPool } } } |