Coder Social home page Coder Social logo

badgerati / pode Goto Github PK

View Code? Open in Web Editor NEW
785.0 51.0 89.0 23.9 MB

Pode is a Cross-Platform PowerShell web framework for creating REST APIs, Web Sites, and TCP/SMTP servers

Home Page: https://badgerati.github.io/Pode

License: Other

PowerShell 92.42% JavaScript 0.13% Dockerfile 0.05% HTML 0.01% C# 7.38%
powershell rest powershell-core windows unix cross-platform web server framework https

pode's People

Contributors

alan-null avatar ariehein avatar avin3sh avatar badgerati avatar chris--a avatar dependabot[bot] avatar fatherofinvention avatar fraham avatar glatzert avatar haidouks avatar ili101 avatar ittchmh avatar jhainau avatar mark05e avatar mdaneri avatar pcgeek86 avatar phatmandrake avatar plk avatar robinbeismann avatar ssugar avatar szeraax avatar thebakabandit avatar windos avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

pode's Issues

Support on custom static routes for directories, and returning an index/default.html

So right now, if you define a static route:

route static '/assets' './assets''

then a call to http://localhost:8080/assets/icon.png will simple get the static file from ./assets/icon.png. However, if you just called http://localhost:8080/assets then a 404 will be returned.

This change will make it so that calling http://localhost:8080/assets, if it matches a defined static route, will instead return an index.html/default.html or any other variant if one exists within the directory.

Internally, there should be a defined list of files to check. This list should be customisable, either via a function or a config file - pode.json?

Remove Obsolete functions

A few old functions to write json/xml/view responses were made obsolete for more streamlined functions, in the Responses.ps1 file.

They were made obsolete a few versions ago, and now need to be removed - and for some of them, also removed from being exported.

Support for Allowing/Denying IP and Subnet Addresses

The idea behind this is to allow IP address and subnet masks to be allowed/denied, and any incoming requests that meet the requirement are allowed or denied access.

This will be under a new keyword of access to be used within the Server, examples include:

# allowing the local ip
access allow ip 127.0.0.1

# allow a subnet, will allow everything 10.10.0.0 - 10.10.0.255
access allow ip '10.10.0.0/24'

# deny an array of ips
access deny ip @('192.168.1.2', '192.168.1.3')

# deny every address
access deny ip all

The allow list is checked first, followed by the deny list. If the IP is in neither but we have an allow list then auto-deny the IP.

Feature can maybe be extended later on to support content types (ie: access allow content 'text/plain')

Basic and Forms Authentication Support

Feature to support basic authentication using the session middleware. This will not be using the inbuilt AuthenticationSchemes on an HttpListener, and instead be built as middleware for extensibility.

Have a * HTTP method so a Route can be used on every method

At the moment, routes can only be attached to a single method; this issue is, for now, to allow a new method of *. This will allow a route to be attached onto every HTTP method.

The * is also special, in that routes will first be checked against the main methods, and then finally - if one cannot be found - it will check the route against *.

This will return hello on every method when calling /hello

# return hello on every method
route * '/hello' {
    json @{ 'value' = 'hello!' }
}

This will return "bonjour!" on /hello for get, but "hello!" for every other method

# return bonjour on every get
route get '/hello' {
    json @{ 'value' = 'bonjour!' }
}

# return hello on every method
route * '/hello' {
    json @{ 'value' = 'hello!' }
}

Ability to listen on a specific IP rather than everything

Allow the ability for Pode to listen on a specific IP rather than listening on everything (0.0.0.0).

A new argument on the Server will be created called IP, which will allow you to specify an IP address to listen on; if no IP address is passed then Pode will listen on everything (default behaviour):

# Pode will listen on 127.0.0.2:8080
Server -IP 127.0.0.2 -Port 8080 {
    # logic
}

# Pode will listen on everything
Server -Port 8080 {
    # logic
}

New "json", "xml" and "view" functions

Add 3 new functions of json, xml and view to replace the Write-ViewResponse functions.

A view is always from a file and uses the view engine, but a json/xml can be an object/pure json or a file path that needs reading in.

Support for basic rate limiting of requests per x seconds from IPs

Similar to the access feature, include a basic rate-limiting feature to limit the number of requests over a period time from an IP/Subnet. When the limit is reached, return the HTTP Status of 429.

A simple example of this, to limit 127.0.0.1 to only 5 request every 10 seconds is:

Server -Port 8080 {
    limit ip 127.0.0.1 5 10
}

Only 5 requests can be made over 10 seconds; if a 6+ call is made HTTP 429 is returned. After 10 seconds, the limit is reset to 0 for another 10 seconds

Subnets are a little different; you could either treat 10.0.0.0/24 to block each IP individually, or you can treat is as a whole and each IP the subnet covers share a single limit:

Server -Port 8080 {
    # this will limit every IP in the subnet individually
    limit ip '10.0.0.0/24' 5 10

    # this will limit every IP in the subnet as one
    limit ip '10.0.0.0/24' 5 10 -shared
}

Request Header information

To get the information from the header, the following code will work.

In the Start-WebServer, right after you define the content type:

[System.Collections.Specialized.NameValueCollection]$headers = $request.Headers
$HeadersArray = foreach ($key in $headers.AllKeys){
[array]$values = $headers.GetValues($key);
if($values.Length -gt 0) {
New-Object psobject -Property ([ordered]@{
HeaderName = $key
HeaderValue = $values[0]
})
}
}

And this to add it for the output.
$PodeSession.Web.Headers = $HeadersArray

Unable to access functions from external ps1 files

If you have an external ps1 file, then at the moment just referencing it in your server script still won't allow routes/timers/etc to see those functions.

Possible solution here is to have a module (.psm1) which has those functions in, then use the ImportPSModule on the runspace pools to allow runspaces to access those functions. ie:

Server {
    script './path/to/module.psm1'
}

This will import the module.psm1 onto each runspace pool

Accessing a variable set inside a timer from a route

I'd like to have a Server timer running every X seconds that will pull some data and update a hash table. I would then like to read from that hash table when a specific api route is called.

I haven't been able to figure out a way to do that. I looked through the readme and the timer example, but that is more about how to create/set a timer, and not about the logic and variables within the timer. Any guidance would be appreciated.

Ability for Pode to Internally Restart when a File Change is Detected

This feature comes from #20, and a code example via PowerShell can be found here: https://gallery.technet.microsoft.com/scriptcenter/Powershell-FileSystemWatche-dfd7084b

  • Pode should be able to monitor for file changes (created, changed, or deleted) and internally restart the server logic.
  • On a restart, the current session should be disposed (ie, runspaces, timers and routes) before the server script is restarted.
  • When a restart is initiated, it should be logged to the CLI.
  • I'm thinking this should be optional, so only enable monitoring if the Server block has the -Monitor switch supplied?

Log output and user requests

@Badgerati - Was wondering if you had any thoughts or plans on adding some logging to pode Servers in order to have a historic view of what is being requested, sourceip, ... along the lines of something we'd get if we ran an iis-based web/smtp server with basic logging enabled. I'd be willing to take a first crack at it if you'd like.

Calling "pode start" fails to import Module into Runspace

When using pode start, it fails because it throws an error when attempting to import the Pode module into the runspaces:

Exception calling "ImportPSModule" with "1" argument(s): "Value cannot be null.
Parameter name: name"
At C:\Program Files\WindowsPowerShell\Modules\Pode\Tools\Session.ps1:136 char:5
+     $state.ImportPSModule((Get-Module -Name Pode).Path)
+     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : ArgumentNullException

Session creation and storage (in-mem/custom), as well as signed cookies

New feature to support session middleware, such that it will check for/create new sessions on web requests, and store any related data in-mem or in a custom storage (redis/mongo/etc).

  • The middleware should check if a request's cookies contains a session-cookie, and then check if it's correctly signed and get the sessionId. If no session is found, a new one should be created, signed, and attached to the response cookies.
  • Data for the session by default will be store in-mem, but there should be the ability to use a custom store.
  • Age/expiry/duration of the session-cookie should be controllable.
  • A secret key should be able to be specified for signing cookies.
  • Ability to be able to specify a custom sessionId generator - default is just a guid.

An example of setting up session middleware:

middleware (session @{
    'Secret' = 'schwifty';  # secret-key used to sign session cookie
    'Name' = 'pode.sid';    # session cookie name (def: pode.sid)
    'Duration' = 120;       # duration of the cookie, in seconds
    'Extend' = $true;       # extend the duration of the cookie on each call
    'GenerateId' = {        # custom SessionId generator (def: guid)
        return [System.IO.Path]::GetRandomFileName()
    };
    'Store' = $null;        # custom object with required methods (def: in-mem)
})

An example of using a session in a route to increase views counter:

route 'get' '/' {
    param($s)
    $s.Session.Data.Views++
    json @{ 'Views' = $s.Session.Data.Views }
}

The counter will continue to increment on each call to the route until the session expires.

Return web requests asynchronously

I've already started work on this in the async-content branch.

Basic idea: Pode should be able to handle content requests asynchronously - ie, the files within the /public directory. This is to help when large image files (or other files) are being returned, and to stop content requests building up and making them slower and slower over time.

The async part is achieved using Runspaces in PowerShell, and the number of Runspaces being created to handle content requests should be configurable - default and minimum is 1. Requests will be setup as a new runspace, and PowerShell will handle the queuing internally.

This will also include some basic performance tests using k6.

Work on getting all requests to be handled async will be worked on separately in a different ticket (ie, views) - as this requires a bigger overhaul.

Support for Middleware on Web Servers

In order to start supporting features like authentication, Pode needs to support middleware first. This will allow you run logic - in the order the middleware is defined - on the Request and Response objects.

By default, Pode will have some pre-defined middleware that will run first (ie, access, rate limiting, static content routing, query string, and body parsing), and then the final route that will be run (which will also support any intermediate middleware for logic like auth'ing a request - think passport).

For defining new middleware, the following could be a possible example in Server:

middleware {
    # $session will contain the Request and Response
    param($session)

    # check the requests HTTP protocol version
    if ($session.Request.ProtocolVersion -ieq '1.1') {
        # some logic
    }

    # if fail, set status and return $false (will halt and return immediately)
    status 400
    json @{'Message' = 'Failed'}
    return $false

    # on success return $true, and run next middleware
    return $true

    # if success but you want to stop processing other middleware/routes
    return $false
}

If any middleware fails and returns $false, then Pode should stop processing the request

For a route, you could have:

# a normal route, with a method, path and logic
route get '/path' { # logic }

# route but with middleware that runs first
# if the middleware fails, the route logic is not run
route get '/path' { #middleware } { # logic }

# route with an array of middlewares
route get '/path' @({ #m1 }, { #m2 }) { #logic }

The order in which middlewares are run are as follows:

  • access control
  • rate limiting
  • public/static content
  • parse body/payload content
  • get querystring parameters
  • middleware [run in order supplied]
  • route logic, and any inline middleware supplied

Proper way to include javascript file

I have files at:
/views/index.pode
/public/scripts/main.js.pode

In /views/index.pode, I use the following code to try and include /public/scripts/main.js.pode
$(include scripts/main)
or even using
$(include scripts/main.js.pode)

I get a "file not found at path" error.

A comment within Responses.ps1 on line 309 seems to indicate that only the views path is looked at:
# only look in the view directory

In the rest of the Include function, I don't see any code that would look through the public folder.

I am able to use the following successfully:
$(include ../public/scripts/main.js.pode)

Am I making a mistake somewhere?

Set Dockerfile to use a fixed version of the PowerShell container, rather than latest

The Dockerfile used currently is always targeting latest; this issue is simply to set it to use microsoft/powershell:ubuntu-xenial. I've already done some testing with it, and this version works as normal.

This will allows temporary preview versions of Pode to use preview versions of PowerShell - aka, the issues seen with File Monitoring that are resolved with 6.1.0 currently in preview.

Ability to alter the views and public assets directories

Right now the directories for view and public assets are coded to /views and /public respectively. This change will add a function to allow the altering of these directories.

Something like the following maybe?

paths assets '/assets'
paths views '/routes'

Status function to easily alter the StatusCode of a Response

Right now the only way to edit the StatusCode of a response in a route is via:

$session.Response.StatusCode = 200

This update will make it easier via:

# just the status code
status 200

# status code and description
status 200 'some description'

Add Limit option to Schedules

Like how timers have -Limit option, this will be added to schedules as well. This will allow schedules to run X times, and then be removed.

Also, a removal list will be added internal for schedules, much like how timers are removed when they've come to their last trigger.

Request for a new release. Matching the documentation and several functions.

Hi Matthew,

I really like your Pode repo. I have one challenge though. The documentation and some of the examples does not match the latest release available on DockerHub or/and the PowerShell module. For example the Pode and route functions are not in the latest public release. Will you update soon?

I guess I'll have to clone for now and go from there. But it will lovely to get an official release.

πŸ‘

Just a suggestion for an improvement to the Dockerfile used as an example.

Hi @Badgerati,

In the Dockerfile at > https://github.com/Badgerati/Pode#docker one doesn't actually have to have the RUN mkdir.... line. As the line below, the COPY .... will create folders on the destination, for every folder in the src folder. As long as the one remembers to have a trailing slash on dest specified.

See more https://docs.docker.com/engine/reference/builder/#copy where that last line of the COPY section says > If <dest> doesn’t exist, it is created along with all missing directories in its path.

A minor improvement but I just felt like sharing it as I really like your Pode framework and because it will make the resulting image, from running Docker build.... smaller. The fewer lines in the Dockerfile the fewer layers in the image. Is the general rule of thumb as far as I have understood.

Have a great evening.

When an Internal Restart is triggered, RunspacePools should be recreated

When an internal restart is triggered due to a file change - if we're monitoring - then the runspace pools need to be disposed and recreated. This will also include recreating the shared session state as well.

The reason for this, is the runspace pools won't update any changed modules and will continue to use older versions. (mostly since the script function was introduced).

Async timers to run tasks and processes in a separate thread

Add the ability to create async timers that will run in a separate runspace along the core server logic.

Timers should be able to run indefinitely, or for a limited number of runs.

When ctrl-c is hit, or the app errors out, dispose and close the extra runspaces.

Response function to help attach/download files in the Public directory

Add a new helper response function to attach a file to the response that can be downloaded; files should come from the Public directory, much like normal content requests.

ie, doing the following in a route would start a download for the public/downloads/install.exe file:

route get '/installer' {
    param($s)
    attach 'downloads/install.exe'
}

Support for scheduled tasks using cron expressions

Repeatable tasks at the moment are supported via timers that run at fixed intervals/seconds.

This feature is to support a new schedule which works much like timers, but using cron expressions, instead of fixed seconds.

This could replace timers at some point, as they're effectively their successor

Basic cron expressions will be supported, with more advanced cron expression syntax being added later on. Some predefined expressions will be setup, like @hourly, etc.

Example of a schedule:

Server {
    # schedule minutely using predefined cron
    schedule 'predefined' '@minutely' {
        # logic
    }

    # schedule to run every tuesday at midnight
    schedule 'tuesdays' '0 0 * * TUE' {
        # logic
    }
}

Update PowerShell Docker Image to 6.1.0

PowerShell 6.1.0 has now been released, which means the image in the Dockerfile can be updated to the following:

mcr.microsoft.com/powershell:6.1.0-ubuntu-16.04

I've already done some testing with it, and everything works all right - including the ability for internal restarts to now function properly!

Status code in response

Is there a reason wht the function Status is not exported?
Is it posible to add status code in all of the responses?

Not displaying images the easy way.

I have been trying to display some images, but it is not working.

The workaround that i found was to base64 encode them in a Pode file.
img src="data:image/png;base64,$([convert]::ToBase64String((Get-Content image.png -Encoding byte)))" alt="png"
It is working, but i have to rewrite some pages.

Do you thing it will be fixed in a newer version?
I am on the newest commit: 9132d7d

Support for relative paths on views.

Hi @Badgerati,

So there I was. Starting the server via PowerShell dot-sourcing. However, not in the folder of the .PS1 being dot-sourced. But from several levels up in the folder hierarchy.

Issue

When I do this, Pode cannot locate the "Whatever.html" file I want loaded as this >

$Path = (Join-Path 'views' $Path)
<-- is not path relative compatible.
I tried with some $global:MyInvocation.MyCommand.Path magic inside the Responses.ps1 file. Now my "Whatever.html" file could be located but other files, such as CSS and JS files could still no be loaded (HTTP404 returned).

Wish

It would be nice if it was possible for Pode to support relative paths. So that one does NOT have to be at the correct path when invoking the PS1 file containing Server definitions and so forth.

I hope I made myself understandable. Let me know what you think.

Thank you.

Helper function to ease URL Redirecting

Quick helper function of redirect to ease URL redirection from routes. It should support both 301 and 302 redirect statuses:

# sends 302
route get '/redirect' {
    redirect 'https://google.com'
}

# sends 301
route get '/moved' {
    redirect -moved 'https://google.com'
}

Server parameter aliases

Only a small enhancement, but set up some [Alias()] attributes on the Server function's parameters.

Ie, -Name could be -n using [Alias('n')]

High CPU usage

Hi Matthew,

When running Pode i have noticed the CPU usage in idle is arround 40% in my test environments.
Do you know if it is a .Net "feature" on Windows? (Both 2012 R2)

Setup SMTP/TCP listeners to run in separate runspace like Web

On setting up the file monitoring, the web server was configured to run in its own isolated runspace. SMTP and TCP servers were missed, meaning ctrl-c doesn't work.

This issue is to setup the SMTP/TCP servers to also run in their own isolated runspaces.

Ability to run a web server, and view through a Desktop Application

Similar to tools like Electron, give Pode the ability to host a web server (against localhost), and be able to display it via a GUI Desktop Application.

The Desktop App will be displayed using WPF, and host a WebBrowser within the Window.

ie:

Server {
    listen 127.0.0.1:8080 http

    gui 'Desktop App' @{
        'Icon' = 'path/to/icon.ico';
        'State' = 'Maximized';
    }
}

Re-use or setup Self-Signed Certificates when using HTTPS

Ability for Pode to re-use or create its own self-signed certificates when being used over HTTPS - this will be inbuilt logic to handle certs in the WebServer, using the example web-pages-https.ps1 from @ssugar.

Note: for now this will only be for Windows environments, and not PS-Core/Linux environments. Maybe it's possible to use OpenSSL instead?

Before checking if a cert exists, check to ensure a cert isn't already bound to the IP:Port binding using netsh http show sslcert - if there is, skip the other steps.

A new parameter on Server called something similar to -SSLName, which is the certificate name/domain to look for existing certs at Cert:/LocalMachine/My or to create a new self-signed cert. (If no name supplied, use some default name like "Pode SSL Certificate").

Also another parameter for -NoSelfSignedCert (or similar), so that if no cert is bound or can be found, then disable creating a self-signed cert and throw an error instead.

If no binding, and no cert, then create a self-signed cert using New-SelfSignedCertificate.

Then attach the new/existing cert using netsh http add sslcert to bind the cert to the IP:Port config.

If any Middleware or Route throws an error, a 500 response should be returned - not a 200

If any middleware or route logic throws an error currently, then a 200 response is returned. This issue is to change that so a 500 is returned instead, using status 500.

For middleware this can be done in the Invoke-PodeMiddleware function, within the catch of the try-catch. For routes this could be done within the catch of the try-catch in the WebServer - around line 156.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.