diff --git a/docs/Tutorials/Basics.md b/docs/Tutorials/Basics.md index 864149159..1cbf52739 100644 --- a/docs/Tutorials/Basics.md +++ b/docs/Tutorials/Basics.md @@ -1,24 +1,67 @@ # Basics -All of your main server logic must be set using the [`Start-PodeServer`](../../Functions/Core/Start-PodeServer) block: +!!! warning + You can only start one server in your script + +The script for your server should be set in the [`Start-PodeServer`](../../Functions/Core/Start-PodeServer) function, via the `-ScriptBlock` parameter. The following example will listen over HTTP on port 8080, and expose a simple HTML page of running processes at `http://localhost:8080/processes`: ```powershell Start-PodeServer { - # attach to port 8080 + # attach to port 8080 for http Add-PodeEndpoint -Address * -Port 8080 -Protocol Http - # logic for routes, timers, schedules, etc + # a simple page for displaying services + Add-PodePage -Name 'processes' -ScriptBlock { Get-Process } } ``` -!!! warning - You can only start one server in your script - -The above server will start a basic HTTP listener on port 8080. To start the server you can either: +To start the server you can either: * Directly run the `./server.ps1` script, or * If you've created a `package.json` file, ensure the `./server.ps1` script is set as your `main` or `scripts/start`, then just run `pode start` (more [here](../../Getting-Started/CLI)) +## Terminating + +Once your Pode server has started, you can terminate it at any time using `Ctrl+C`. If you want to disable your server from being terminated then use the `-DisableTermination` switch on the [`Start-PodeServer`](../../Functions/Core/Start-PodeServer) function. + +## Restarting + +You can restart your Pode server by using `Ctrl+R`, or on Unix you can also use `Shift+C` and `Shift+R` as well. When the server restarts it will only re-invoke the initial `-ScriptBlock`, so any changes made to this main scriptblock will *not* be reflected - you'll need to terminate and start your server again. + +## Script from File + +You can also define your server's scriptblock in a separate file, and use it via the `-FilePath` parameter on the [`Start-PodeServer`](../../Functions/Core/Start-PodeServer) function. + +Using this approach there are 2 ways to start you server: + +1. You can put your scriptblock into a separate file, and put your [`Start-PodeServer`](../../Functions/Core/Start-PodeServer) call into another script. This other script is then what you call on the CLI. +2. You can directly call [`Start-PodeServer`](../../Functions/Core/Start-PodeServer) on the CLI. + +When you call [`Start-PodeServer`](../../Functions/Core/Start-PodeServer) directly on the CLI, then your server's root path will be set to directory of that file. You can override this behaviour by either defining a path via `-RootPath`, or by telling the server to use the current working path as root via `-CurrentPath`. + +For example, the following is a file that contains the same scriptblock for the server at the top of this page. Following that are the two ways to run the server - the first is via another script, and the second is directly from the CLI: + +* File.ps1 +```powershell +{ + # attach to port 8080 for http + Add-PodeEndpoint -Address * -Port 8080 -Protocol Http + + # a simple page for displaying services + Add-PodePage -Name 'processes' -ScriptBlock { Get-Process } +} +``` + +* Server.ps1 (start via script) +```powershell +Start-PodeServer -FilePath './File.ps1' +``` +then use `./Server.ps1` on the CLI. + +* CLI (start from CLI) +```powershell +PS> Start-PodeServer -FilePath './File.ps1' +``` + !!! tip - Once Pode has started, you can exit out at any time using `Ctrl+C`. You can also restart the server by using `Ctrl+R`. - On Unix, you can also use `Shuft+C` and `Shift+R` + Normally when you restart your Pode server any changes to the main scriptblock don't reflect. However, if you reference a file instead, then restarting the server will reload the scriptblock from that file - so any changes will reflect. diff --git a/docs/Tutorials/Routes/Overview.md b/docs/Tutorials/Routes/Overview.md index ef5bc2487..15421bbdf 100644 --- a/docs/Tutorials/Routes/Overview.md +++ b/docs/Tutorials/Routes/Overview.md @@ -126,3 +126,21 @@ The following request will invoke the above route: ```powershell Invoke-WebRequest -Uri 'http://localhost:8080/users/12345' -Method Get ``` + +## Script from File + +You normally define a route's script using the `-ScriptBlock` parameter however, you can also reference a file with the required scriptblock using `-FilePath`. Using the `-FilePath` parameter will dot-source a scriptblock from the file, and set it as the route's script. + +For example, to create a route from a file that will write a simple JSON response on a route: + +* File.ps1 +```powershell +{ + Write-PodeJsonResponse -Value @{ 'value' = 'pong'; } +} +``` + +* Timer +```powershell +Add-PodeRoute -Method Get -Path '/ping' -FilePath './Routes/File.ps1' +``` diff --git a/docs/Tutorials/Schedules.md b/docs/Tutorials/Schedules.md index 5d8289137..e660bfeb8 100644 --- a/docs/Tutorials/Schedules.md +++ b/docs/Tutorials/Schedules.md @@ -11,30 +11,24 @@ To create a new Schedule in your server you use the Schedule functions. To create a basic Schedule, the following example will work; this will trigger at '00:05' every Tuesday outputting the current date/time: ```powershell -Start-PodeServer { - Add-PodeSchedule -Name 'date' -Cron '5 0 * * TUE' -ScriptBlock { - Write-Host "$([DateTime]::Now)" - } +Add-PodeSchedule -Name 'date' -Cron '5 0 * * TUE' -ScriptBlock { + Write-Host "$([DateTime]::Now)" } ``` Whereas the following will create the same schedule, but will only trigger the schedule 4 times due to the `-Limit` value supplied: ```powershell -Start-PodeServer { - Add-PodeSchedule -Name 'date' -Cron '5 0 * * TUE' -Limit 4 -ScriptBlock { - Write-Host "$([DateTime]::Now)" - } +Add-PodeSchedule -Name 'date' -Cron '5 0 * * TUE' -Limit 4 -ScriptBlock { + Write-Host "$([DateTime]::Now)" } ``` You can also supply multiple cron expressions for the same Schedule. For example, the following will trigger the same schedule every minute and every hour: ```powershell -Start-PodeServer { - Add-PodeSchedule -Name 'date' -Cron @('@minutely', '@hourly') -ScriptBlock { - Write-Host "$([DateTime]::Now)" - } +Add-PodeSchedule -Name 'date' -Cron @('@minutely', '@hourly') -ScriptBlock { + Write-Host "$([DateTime]::Now)" } ``` @@ -57,12 +51,10 @@ The `-StartTime ` parameter will cause the Schedule to only be trigger The following will create a Schedule that triggers at 16:00 every Friday, and is delayed by 1 year: ```powershell -Start-PodeServer { - $start = [DateTime]::Now.AddYears(1) +$start = [DateTime]::Now.AddYears(1) - Add-PodeSchedule -Name 'date' -Cron '0 16 * * FRI' -StartTime $start { - Write-Host "$([DateTime]::Now)" - } +Add-PodeSchedule -Name 'date' -Cron '0 16 * * FRI' -StartTime $start -ScriptBlock { + Write-Host "$([DateTime]::Now)" } ``` @@ -73,11 +65,27 @@ The `-EndTime ` parameter will cause the Schedule to cease triggering The following will create a Schedule that triggers at 16:00 every Friday, and stops triggering in 1 year: ```powershell -Start-PodeServer { - $end = [DateTime]::Now.AddYears(1) +$end = [DateTime]::Now.AddYears(1) - Add-PodeSchedule -Name 'date' -Cron '0 16 * * FRI' -EndTime $end -ScriptBlock { - Write-Host "$([DateTime]::Now)" - } +Add-PodeSchedule -Name 'date' -Cron '0 16 * * FRI' -EndTime $end -ScriptBlock { + Write-Host "$([DateTime]::Now)" } ``` + +## Script from File + +You normally define a schedule's script using the `-ScriptBlock` parameter however, you can also reference a file with the required scriptblock using `-FilePath`. Using the `-FilePath` parameter will dot-source a scriptblock from the file, and set it as the schedule's script. + +For example, to create a schedule from a file that will output `Hello, world` every minute: + +* File.ps1 +```powershell +{ + 'Hello, world!' | Out-PodeHost +} +``` + +* Timer +```powershell +Add-PodeSchedule -Name 'from-file' -Cron '@minutely' -FilePath './Schedules/File.ps1' +``` diff --git a/docs/Tutorials/Timers.md b/docs/Tutorials/Timers.md index 3dad9dc4d..3657e2585 100644 --- a/docs/Tutorials/Timers.md +++ b/docs/Tutorials/Timers.md @@ -12,10 +12,8 @@ To create a new Timer in your server you use the Timer functions. To create a basic Timer, the following example will work; this will loop every 5 seconds outputting the date/time: ```powershell -Start-PodeServer { - Add-PodeTimer -Name 'date' -Interval 5 -ScriptBlock { - Write-Host "$([DateTime]::Now)" - } +Add-PodeTimer -Name 'date' -Interval 5 -ScriptBlock { + Write-Host "$([DateTime]::Now)" } ``` @@ -26,10 +24,8 @@ The `-Skip ` parameter will cause the Timer to skip its first initial trigg The following will create a Timer that runs every 10 seconds, and skips the first 5 iterations: ```powershell -Start-PodeServer { - Add-PodeTimer -Name 'date' -Interval 10 -Skip 5 -ScriptBlock { - Write-Host "$([DateTime]::Now)" - } +Add-PodeTimer -Name 'date' -Interval 10 -Skip 5 -ScriptBlock { + Write-Host "$([DateTime]::Now)" } ``` @@ -40,9 +36,25 @@ Normally a Timer will run forever, or at least until you terminate the server. S The following will run every 20 seconds, and will only run 3 times: ```powershell -Start-PodeServer { - Add-PodeTimer -Name 'date' -Interval 20 -Limit 3 -ScriptBlock { - Write-Host "$([DateTime]::Now)" - } +Add-PodeTimer -Name 'date' -Interval 20 -Limit 3 -ScriptBlock { + Write-Host "$([DateTime]::Now)" } ``` + +## Script from File + +You normally define a timer's script using the `-ScriptBlock` parameter however, you can also reference a file with the required scriptblock using `-FilePath`. Using the `-FilePath` parameter will dot-source a scriptblock from the file, and set it as the timer's script. + +For example, to create a timer from a file that will output `Hello, world` every 2secs: + +* File.ps1 +```powershell +{ + 'Hello, world!' | Out-PodeHost +} +``` + +* Timer +```powershell +Add-PodeTimer -Name 'from-file' -Interval 2 -FilePath './Timers/File.ps1' +``` diff --git a/examples/scripts/server.ps1 b/examples/scripts/server.ps1 new file mode 100644 index 000000000..5ef8f72d9 --- /dev/null +++ b/examples/scripts/server.ps1 @@ -0,0 +1,13 @@ +{ + Add-PodeEndpoint -Address * -Port 8081 -Protocol Http + New-PodeLoggingMethod -Terminal | Enable-PodeErrorLogging + Set-PodeViewEngine -Type Pode + + Add-PodeTimer -Name 'Hi' -Interval 4 -ScriptBlock { + 'Hello from a file!' | Out-PodeHost + } + + Add-PodeRoute -Method Get -Path '/' -ScriptBlock { + Write-PodeViewResponse -Path 'simple' -Data @{ 'numbers' = @(1, 2, 3); } + } +} \ No newline at end of file diff --git a/examples/server-from-file.ps1 b/examples/server-from-file.ps1 new file mode 100644 index 000000000..01cdc2353 --- /dev/null +++ b/examples/server-from-file.ps1 @@ -0,0 +1,8 @@ +$path = Split-Path -Parent -Path (Split-Path -Parent -Path $MyInvocation.MyCommand.Path) +Import-Module "$($path)/src/Pode.psm1" -Force -ErrorAction Stop + +# or just: +# Import-Module Pode + +# create a basic server +Start-PodeServer -FilePath './scripts/server.ps1' -CurrentPath \ No newline at end of file diff --git a/src/Private/Context.ps1 b/src/Private/Context.ps1 index 05d3322d2..257e2c2b1 100644 --- a/src/Private/Context.ps1 +++ b/src/Private/Context.ps1 @@ -1,21 +1,32 @@ function New-PodeContext { + [CmdletBinding()] param ( + [Parameter()] [scriptblock] $ScriptBlock, + [Parameter()] + [string] + $FilePath, + + [Parameter()] [int] $Threads = 1, + [Parameter()] [int] $Interval = 0, + [Parameter()] [string] $ServerRoot, + [Parameter()] [string] $Name = $null, + [Parameter()] [string] $ServerType ) @@ -49,6 +60,7 @@ function New-PodeContext # set the server name, logic and root $ctx.Server.Name = $Name $ctx.Server.Logic = $ScriptBlock + $ctx.Server.LogicPath = $FilePath $ctx.Server.Interval = $Interval $ctx.Server.PodeModulePath = (Get-PodeModulePath) diff --git a/src/Private/Server.ps1 b/src/Private/Server.ps1 index ef8341942..9940e84f8 100644 --- a/src/Private/Server.ps1 +++ b/src/Private/Server.ps1 @@ -13,9 +13,18 @@ function Start-PodeInternalServer # setup temp drives for internal dirs Add-PodePSInbuiltDrives - # create the runspace state, execute the server logic, and start the runspaces + # create the shared runspace state New-PodeRunspaceState - Invoke-PodeScriptBlock -ScriptBlock $PodeContext.Server.Logic -NoNewClosure + + # get the server's script and invoke it - to set up routes, timers, middleware, etc + $_script = $PodeContext.Server.Logic + if (Test-PodePath -Path $PodeContext.Server.LogicPath -NoStatus) { + $_script = Convert-PodeFileToScriptBlock -FilePath $PodeContext.Server.LogicPath + } + + Invoke-PodeScriptBlock -ScriptBlock $_script -NoNewClosure + + # start the runspace pools for web, schedules, etc New-PodeRunspacePools # create timer/schedules for auto-restarting diff --git a/src/Public/Core.ps1 b/src/Public/Core.ps1 index 5d4fcf6df..a737b4bd1 100644 --- a/src/Public/Core.ps1 +++ b/src/Public/Core.ps1 @@ -8,6 +8,10 @@ Starts a Pode Server with the supplied ScriptBlock. .PARAMETER ScriptBlock The main logic for the Server. +.PARAMETER FilePath +A literal, or relative, path to a file containing a ScriptBlock for the Server's logic. +The directory of this file will be used as the Server's root path - unless a specific -RootPath is supplied. + .PARAMETER Interval For 'Service' type Servers, will invoke the ScriptBlock every X seconds. @@ -32,6 +36,9 @@ Disables the ability to terminate the Server. .PARAMETER Browse Open the web Server's default endpoint in your defualt browser. +.PARAMETER CurrentPath +Sets the Server's root path to be the current working path - for -FilePath only. + .EXAMPLE Start-PodeServer { /* logic */ } @@ -43,12 +50,16 @@ Start-PodeServer -Request $LambdaInput -Type 'AwsLambda' { /* logic */ } #> function Start-PodeServer { - [CmdletBinding()] + [CmdletBinding(DefaultParameterSetName='Script')] param ( - [Parameter(Mandatory=$true, ValueFromPipeline=$true)] + [Parameter(Mandatory=$true, ValueFromPipeline=$true, Position=0, ParameterSetName='Script')] [scriptblock] $ScriptBlock, + [Parameter(Mandatory=$true, ParameterSetName='File')] + [string] + $FilePath, + [Parameter()] [int] $Interval = 0, @@ -77,7 +88,11 @@ function Start-PodeServer $DisableTermination, [switch] - $Browse + $Browse, + + [Parameter(ParameterSetName='File')] + [switch] + $CurrentPath ) # ensure the session is clean @@ -85,13 +100,30 @@ function Start-PodeServer $ShowDoneMessage = $true try { + # if we have a filepath, resolve it - and extract a root path from it + if ($PSCmdlet.ParameterSetName -ieq 'file') { + $FilePath = Get-PodeRelativePath -Path $FilePath -Resolve -TestPath + + # if not already supplied, set root path + if ([string]::IsNullOrWhiteSpace($RootPath)) { + if ($CurrentPath) { + $RootPath = $PWD.Path + } + else { + $RootPath = Split-Path -Parent -Path $FilePath + } + } + } + # configure the server's root path if (!(Test-IsEmpty $RootPath)) { $RootPath = Get-PodeRelativePath -Path $RootPath -RootPath $MyInvocation.PSScriptRoot -JoinRoot -Resolve -TestPath } # create main context object - $PodeContext = New-PodeContext -ScriptBlock $ScriptBlock ` + $PodeContext = New-PodeContext ` + -ScriptBlock $ScriptBlock ` + -FilePath $FilePath ` -Threads $Threads ` -Interval $Interval ` -ServerRoot (Protect-PodeValue -Value $RootPath -Default $MyInvocation.PSScriptRoot) `