Scaffold/dashboard.ps1

<#
.SYNOPSIS
    Main dashboard file
.DESCRIPTION
    This is the jumping-off point for the dashboard, and requires very little customisation
 
    When deploying to IIS, you will need to modify the web.config to include '-RunMode Service'.
.EXAMPLE
    PS \> dashboard.ps1
 
    Name Port Running DashboardService
    ---- ---- ------- ----------------
    Test3 8083 True UniversalDashboard.Services.DashboardService
.EXAMPLE
    For use with IIS web.config
 
    <aspNetCore
        processPath="C:\Program Files\PowerShell\7-preview\pwsh.exe"
        arguments=".\dashboard.ps1 -RunMode Service -UDModulePath .\UniversalDashboard.Community.psd1 -Log:$true"
        stdoutLogEnabled="true"
        stdoutLogFile="\\?\%home%\LogFiles\stdout"
        forwardWindowsAuthToken="false"
    />
#>

[CmdletBinding(DefaultParametersetname="ModuleEntOrCom")]
Param (
    #Determines if the dashboard is being called from command-line, or as a service/IIS app.
    [ValidateSet("Standalone","Service")]
    [string]$RunMode="Standalone",

    #Valid range between 1025 and 65535. Ignored if RunMode set to IIS.
    [ValidateRange(1025,65535)]
    [int]$Port=(Get-Random -Minimum 8080 -Maximum 8090),

    #Specify the module path here if required
    [Parameter(ParameterSetName="ModuleSpecified")]
    [ValidateScript({
        if(-Not ($_ | Test-Path) ){ throw "Module does not exist at location specified." }
        if(-Not ($_ | Test-Path -PathType Leaf) ){ throw "ModulePath must be a file." }
        if($_ -notmatch "(\.psm1$|\.psd1$)"){ throw "The file specified in the path argument must be either of type psm1 or psd1" }
        return $true
    })]
    [System.IO.FileInfo]$UDModulePath,

    #Specify whether you wish to use the Enterprise or Community UD module.
    [Parameter(ParameterSetName="ModuleEntOrCom")]
    [ValidateSet("Community","Enterprise")]
    [string]$UDModuleLicense="Community",

    #If logging is desired, the log will be created in the dashboard's 'logs' directory.
    [switch]$Log=$false
)

#######################
#region Initialisation#
#######################

$ErrorActionPreference = "Stop"

# Load the dbconfig.json
$ConfigurationFile = Get-Content (Join-Path -Path $PSScriptRoot -ChildPath config.json) | ConvertFrom-Json
Try {
    Import-Module (Join-Path $PSScriptRoot $ConfigurationFile.rootmodule)
}
Catch {
    Write-Warning "Valid function module not found. Generate one by running the 'New-UDProject' command."
    break;
}

# If a module path has been specified, then import from that location.
if($UDModulePath) {
    try {
        Import-Module $UDModulePath
    } catch {
        Write-Error $_
    }
} else {
    try {
        switch ($UDModuleLicense) {
            "Community" { Import-Module -Name UniversalDashboard.Community }
            "Enterprise" { Import-Module -Name UniversalDashboard }
        }
    } catch {
        Write-Error $_
    }
}

# Switch on logging if desired
if($Log) {
    $LogName = "UDLog_{0}.log" -f (Get-Date -Format "yyyyMMdd-HHmmss")
    Enable-UDLogging -FilePath (Join-Path -Path "$PSScriptRoot\logs" -ChildPath $LogName) -Level Debug
}

$ErrorActionPreference = "SilentlyContinue"
#endregion Initialisation

################
#region Imports#
################

# Each page in its own file for ease of management.
$Pages = Foreach ($Page in (Get-ChildItem (Join-Path $PSScriptRoot pages))) {
    . $Page.Fullname
}

# Go through the Endpoints directory, and import them
$Endpoints = Foreach ($Endpoint in (Get-ChildItem (Join-Path $PSScriptRoot endpoints))) {
    . $Endpoint.Fullname
}

# Import the functions for use outside of Endpoints.
# For use inside endpoints, functions are imported via the module in the endpoint initialization
# Best Practice: Files are named on Noun, with all the verb-noun pairs stored within.
Foreach ($Function in (Get-ChildItem (Join-Path $PSScriptRoot functions))) {
    . $Function.Fullname
}

# Get all the customisations.
# This includes:
# - Themes
# - Navigation
foreach ($Item in (Get-ChildItem (Join-Path $PSScriptRoot customisation))) {
    . $Item.FullName
}
#endregion Imports

###############
#region Launch#
###############

# To add additional modules, include them inside the psd1.
$Initialization = New-UDEndpointInitialization -Module @(Join-Path -Path $PSScriptRoot -ChildPath $ConfigurationFile.rootmodule) 

# Parameters to pass to New-UDDashboard
$DashboardParams = @{
    Title                  = $ConfigurationFile.title
    Theme                  = $Theme
    Pages                  = $Pages
    EndpointInitialization = $Initialization
    Navigation             = $Navigation
    NavbarLinks            = $NavbarLinks
}

# Parameters to pass to Start-UDDashboard
$StartUDSplat = @{
    Dashboard=(New-UDDashboard @DashboardParams)
    Name=$ConfigurationFile.title
    Endpoint=$Endpoints
}

# Determine launch behaviour
switch ($RunMode) {
    "Standalone" { 
        $StartUDSplat.Add("Port",$Port)
        $LaunchBrowser=$true
    }
    "Service"  { $StartUDSplat.Add("Wait",$true) }
}
#endregion Launch

Start-UDDashboard @StartUDSplat

# Kick off a browser session - because we're nice like that.
if($LaunchBrowser){Start-Process "http://localhost:$Port"}