jyhi / purl-workers Goto Github PK
View Code? Open in Web Editor NEWA serverless URL redirection service running on Cloudflare Workers, with a few bells and whistles.
License: GNU Affero General Public License v3.0
A serverless URL redirection service running on Cloudflare Workers, with a few bells and whistles.
License: GNU Affero General Public License v3.0
Just make people fell better :) Formatting, linting, any other checks that I run locally.
A template stored in a value will be rendered and returned based on the option set in the metadata object:
The metadata object should support some new options:
{
"render": true,
"fetch": "http://api.example.com"
}
This enables direct KV database management (create, read, update, delete) on purl-workers
, without wrangler
. This requires a configuration to be stored in the database. Because all keys without a /
prefix cannot be directly accessed, a special key-value can be utilized to achieve this; for example, _config
:
{
"admins": {
"root": "toor",
"beep": "boop"
}
}
However, this will always introduce a second read operation on KV upon request. This feature thus should be configurable in compile time. Can use secrets.
This gives an option for keeping some values secret. Both the entry object and the metadata object can use the following to enable basic authentication on a value:
{
"auth": [
"Basic Zm9vQGJhcg==",
"Basic YmF6QHphYg=="
]
}
Update: Now it's not only authorization but also any header requirements.
Allow diverting into different responses upon different Accept
MIME types. Basically this is like multi-aliases based on what the client accepts, so modifying what "is"
accepts in the entry object is a good choice. The implementation can further support arbitrary header matching with RegEx.
{
"is": {
"accept": {
"text/html": "/foo.html",
"application/json": "/foo.json",
"image/jpeg": "/foo.jpg"
},
"user-agent": {
"MicroMsg": "/link-mm",
"Safari": "/link-aapl"
}
}
}
Allow a user to write a predicate to perform if-then-else branching, in an entry object or in a metadata object.
{
"if": []
"then": {},
"else": {}
}
Where:
"if"
is followed by a predicate (see below)."then"
is followed by an entry object or a string of an entry key in the KV database."else"
is followed by an entry object or a string of an entry key in the KV database.type Expression = (string | Expression)[];
interface Entry {
if: Expression,
then: Entry | string,
else: Entry | string,
// ... omitted
}
All of the three keys are optional:
"if"
is present, then when the predicate evaluates to true, the current entry object will be parsed and presented. Otherwise, a HTTP 403 Forbidden
will be returned."if"
and "then"
are present, then when the predicate evaluates to true, "then"
branch will be taken. Otherwise, the current entry object will be parsed and presented."if"
and "else"
are present, then when the predicate evaluates to true, the current entry object will be parsed and presented. Otherwise, the "else"
branch will be taken."then"
branch will be taken. Otherwise, "else"
branch will be taken.If "then"
and "else"
are followed by entry objects, then the entry objects will be parsed and run. If they are followed by strings instead, then the string will be used as a key to get an entry in the KV database. This is a shorthand of writing an alias using "is"
:
{
"if": ["header", "api-version", "^1$"],
"then": "/test/if/v1"
}
{
"if": ["header", "api-version", "^1$"],
"then": {
"is": "/test/if/v1"
}
}
Other combinations, e.g. only "then"
, only "else"
, are invalid and will be ignored.
{
"if": ["header", "accept", "text/html"],
"then": {
"status": 307,
"statusText": "Temporary Redirect",
"location": "/test/if.html"
},
"else": {
"status": 404,
"statusText": "Not Found"
}
}
The design of predicate is inspired by S-expression; one can say it's just S-expressions written in JSON. This is for allowing complex conditions being specified, while keeping the structure of JSON (for example, if we choose a full-fledged DSL, then we need to quote it fully in a JSON string, which doesn't look good). In fact, it's a domain-specific, restricted Lisp dialect.
An example:
["all", ["any", ["header", "accept", "^application/json"],
["query", "t", "j"],
["query", "type", "json"],
["header", "accept", "\\*/\\*"]],
["any", ["header", "api-version"],
["header", "x-api-version", "^v.+"]]]
The above is conceptually equivalent to this Lisp code:
(all (any (header "accept" "^application/json")
(query "t" "j")
(query "type" "json")
(header "accept" "\\*/\\*"))
(any (header "api-version")
(header "x-api-version" "^v.+")))
An expression is enclosed with a JSON array, in which the first element must be a JSON string specifying a function. The following is a list of planned functions:
all
: true
if all its arguments are true
.any
: true
if one of its arguments is true
.not
: true
if its (only) argument is false
.header
: check whether the incoming request contains the header key and matches the header value (if specified) in regular expression.query
: check whether the incoming request contains the query parameter key and matches the query parameter value (if specified) in regular expression.auth
: check whether an authentication requirement is satisfied.None of all
, any
, or not
returns a boolean value by itself, so other functions (e.g. header
, query
) must stay in the deepest level. Complex logic can then be specified by combining all
, any
, and not
, which correspond to logical all (&&
), logical or (||
), and logical not (!
).
Any sufficiently complicated C or Fortran program contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of Common Lisp. -- Greenspun's tenth rule
Deploy with Workers button, looks very appealing.
This turns purl-workers
into a proxy to a single resource. Upon a request to a caching key, the value is first retrieved from the specified URL (upstream), then stored in the value for TTL amount of time, after which the value will be re-fetched from the upstream. This should be a configuration in the metadata object, as the value needs to be rewritten from time to time.
{
"cacheUrl": "http://http.cat/200",
"cacheTtl": 86400,
"cacheTime": "2021-12-31T00:00:00Z"
}
There could also be support for expunging the cache and forcefully refresh it, either via ?refresh=1
or Purl-Workers-Cache-Refresh: 1
.
It's possible to use a single KV database to support different hosts which cannot access each others' entries - just match the host name too. It's also possible to match the schema:
https://foo.example.com/link
: http://foo.com
http://bar.example.com/link
: https://foo.example.com/link
/link
is returned to all host names / schema then.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.