From 200d1c34ef2496c9ddee7010764063df52aefad1 Mon Sep 17 00:00:00 2001 From: Nathan Totten Date: Mon, 13 Nov 2023 12:15:32 -0500 Subject: [PATCH] updated prettier --- .github/SECURITY.md | 4 +- .github/workflows/codeql.yml | 63 ++++---- .github/workflows/dependency-review.yml | 6 +- babel.config.js | 2 +- crawler.blog.json | 2 +- cspell.json | 11 +- .../articles/archiving-requests-to-storage.md | 2 +- docs/articles/cache.md | 54 +++++-- docs/articles/check-ip-address.md | 3 +- docs/articles/cloudflare-settings.md | 43 +++++- docs/articles/custom-audit-log-policy.md | 18 ++- docs/articles/custom-cors-policy.md | 9 +- docs/articles/custom-log-level.md | 12 +- docs/articles/custom-logging-example.md | 22 ++- .../dev-portal-create-consumer-on-auth.md | 4 +- docs/articles/dev-portal-supabase-auth.md | 10 +- docs/articles/dev-portal-theme.md | 17 ++- docs/articles/environment-variables.md | 43 ++++-- docs/articles/environments.md | 76 +++++++--- docs/articles/graphql-security.md | 143 +++++++++++------- docs/articles/handling-form-data.md | 10 +- docs/articles/http-problems.md | 2 +- docs/articles/key-value-store.md | 6 +- .../lazy-load-configuration-into-cache.md | 5 +- docs/articles/log-export.md | 26 +++- .../articles/log-plugin-vmware-log-insight.md | 2 +- docs/articles/log-plugins.md | 12 +- docs/articles/metrics-plugins.md | 4 +- docs/articles/multiple-auth-policies.md | 44 ++++-- docs/articles/not-found-handler.md | 2 +- .../articles/per-user-rate-limits-using-db.md | 23 ++- docs/articles/rename-or-move-project.md | 24 ++- .../routes-json-deprecation-for-openapi.md | 45 ++++-- docs/articles/runtime-behaviors.md | 20 ++- docs/articles/runtime-extensions.md | 8 +- docs/articles/secure-tunnel.md | 77 ++++++++-- docs/articles/securing-your-backend.md | 54 +++++-- docs/articles/step-3-add-rate-limiting.md | 40 +++-- docs/articles/web-crypto-apis.md | 8 +- docs/articles/what-is-zuplo.md | 25 ++- docs/articles/who-uses-and-why.md | 79 +++++++--- docs/articles/zone-cache.md | 19 ++- docs/articles/zp-body-removed.md | 12 +- docs/articles/zuplo-context.md | 2 +- docs/articles/zuplo-json.md | 34 ++++- docs/errors/get-head-body-error.md | 4 +- ...valid-settings-dev-portal-auth-audience.md | 10 +- .../invalid-settings-dev-portal-auth.md | 6 +- docs/errors/no-project-set.md | 3 +- .../settings-to-dev-portal-migration.md | 3 +- docs/errors/system-configuration-error.md | 3 +- docs/errors/unknown-error.md | 3 +- docs/handlers/custom-handler.md | 2 +- docs/handlers/websocket-handler.md | 5 +- docs/intro.mdx | 8 +- docs/sample-apis.md | 9 +- package-lock.json | 21 +-- package.json | 6 +- policies/ab-test-outbound/policy.ts | 2 +- policies/acl-policy-inbound/policy.ts | 6 +- policies/amberflo-metering-inbound/doc.md | 19 ++- policies/amberflo-metering-inbound/intro.md | 7 +- policies/api-key-auth-inbound/intro.md | 9 +- .../archive-request-aws-s3-inbound/policy.ts | 2 +- .../policy.ts | 2 +- .../policy.ts | 2 +- .../policy.ts | 2 +- policies/auth0-jwt-auth-inbound/intro.md | 4 +- policies/bot-detection-inbound/intro.md | 6 +- policies/composite-inbound/intro.md | 3 +- policies/custom-code-inbound/doc.md | 6 +- policies/custom-code-inbound/intro.md | 3 +- policies/custom-code-outbound/doc.md | 31 +++- policies/custom-code-outbound/intro.md | 3 +- .../graphql-complexity-limit-inbound/doc.md | 11 +- .../doc.md | 3 +- .../schema.json | 7 +- policies/hmac-auth-inbound/doc.md | 6 +- policies/hmac-auth-inbound/policy.ts | 12 +- policies/index.md | 15 +- policies/ip-restriction-inbound/policy.ts | 2 +- policies/open-id-jwt-auth-inbound/example.md | 12 +- policies/rate-limit-inbound/doc.md | 4 +- policies/rbac-policy-inbound/policy.ts | 10 +- policies/readme-metrics-inbound/intro.md | 4 +- policies/remove-headers-inbound/schema.json | 2 +- policies/require-origin-inbound/intro.md | 11 +- policies/set-body-inbound/intro.md | 7 +- policies/set-headers-inbound/intro.md | 11 +- policies/supabase-jwt-auth-inbound/doc.md | 4 +- policies/supabase-jwt-auth-inbound/intro.md | 10 +- policies/transform-body-outbound/policy.ts | 2 +- .../intro.md | 10 +- scripts/check-prepared.mjs | 10 +- scripts/update-bundles.mjs | 4 +- scripts/update-policies.tsx | 42 +---- vercel.json | 2 +- 97 files changed, 1000 insertions(+), 518 deletions(-) diff --git a/.github/SECURITY.md b/.github/SECURITY.md index 117dc0f1..2aabd963 100644 --- a/.github/SECURITY.md +++ b/.github/SECURITY.md @@ -1 +1,3 @@ -If you have a security concern or believe you have found a vulnerability in any part of Zuplo please contact us immediately by emailing us at security@zuplo.com. +If you have a security concern or believe you have found a vulnerability in any +part of Zuplo please contact us immediately by emailing us at +security@zuplo.com. diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 753408c2..03d8e83e 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -13,12 +13,12 @@ name: "CodeQL" on: push: - branches: [ "main" ] + branches: ["main"] pull_request: # The branches below must be a subset of the branches above - branches: [ "main" ] + branches: ["main"] schedule: - - cron: '18 10 * * 1' + - cron: "18 10 * * 1" jobs: analyze: @@ -32,43 +32,42 @@ jobs: strategy: fail-fast: false matrix: - language: [ 'javascript' ] + language: ["javascript"] # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support steps: - - name: Checkout repository - uses: actions/checkout@v4 + - name: Checkout repository + uses: actions/checkout@v4 - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v2 - with: - languages: ${{ matrix.language }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. - # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs - # queries: security-extended,security-and-quality + # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + # Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v2 - # Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java). - # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - uses: github/codeql-action/autobuild@v2 + # ℹ️ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun - # ℹ️ Command-line programs to run using the OS shell. - # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + # If the Autobuild fails above, remove it and uncomment the following three lines. + # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. - # If the Autobuild fails above, remove it and uncomment the following three lines. - # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. + # - run: | + # echo "Run, Build Application using script" + # ./location_of_script_within_repo/buildscript.sh - # - run: | - # echo "Run, Build Application using script" - # ./location_of_script_within_repo/buildscript.sh - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 - with: - category: "/language:${{matrix.language}}" + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 + with: + category: "/language:${{matrix.language}}" diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index 4e751977..57ec650d 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -4,7 +4,7 @@ # # Source repository: https://github.com/actions/dependency-review-action # Public documentation: https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/about-dependency-review#dependency-review-enforcement -name: 'Dependency Review' +name: "Dependency Review" on: [pull_request] permissions: @@ -14,7 +14,7 @@ jobs: dependency-review: runs-on: ubuntu-latest steps: - - name: 'Checkout Repository' + - name: "Checkout Repository" uses: actions/checkout@v4 - - name: 'Dependency Review' + - name: "Dependency Review" uses: actions/dependency-review-action@v3 diff --git a/babel.config.js b/babel.config.js index e00595da..bfd75dbd 100644 --- a/babel.config.js +++ b/babel.config.js @@ -1,3 +1,3 @@ module.exports = { - presets: [require.resolve('@docusaurus/core/lib/babel/preset')], + presets: [require.resolve("@docusaurus/core/lib/babel/preset")], }; diff --git a/crawler.blog.json b/crawler.blog.json index 319e6fa7..e16c68a9 100644 --- a/crawler.blog.json +++ b/crawler.blog.json @@ -22,4 +22,4 @@ "lvl5": "article h6", "text": "article p, article li, article pre, article code, article td:last-child" } -} \ No newline at end of file +} diff --git a/cspell.json b/cspell.json index ea06ca4b..a5eb56aa 100644 --- a/cspell.json +++ b/cspell.json @@ -4,16 +4,9 @@ // language - current active spelling language "language": "en", // words - list of words to be always considered correct - "words": [ - "Kubernetes", - "Linkerd", - "Quickstart", - "quickstarts" - ], + "words": ["Kubernetes", "Linkerd", "Quickstart", "quickstarts"], // flagWords - list of words to be always considered incorrect // This is useful for offensive words and common spelling errors. // For example "hte" should be "the" - "flagWords": [ - "hte" - ] + "flagWords": ["hte"] } diff --git a/docs/articles/archiving-requests-to-storage.md b/docs/articles/archiving-requests-to-storage.md index 3c3a4b94..d22cca74 100644 --- a/docs/articles/archiving-requests-to-storage.md +++ b/docs/articles/archiving-requests-to-storage.md @@ -55,7 +55,7 @@ export type RequestArchivePolicyOptions = { export default async function ( request: ZuploRequest, context: ZuploContext, - options: RequestArchivePolicyOptions + options: RequestArchivePolicyOptions, ) { // because we will read the body, we need to // create a clone of this request first, otherwise diff --git a/docs/articles/cache.md b/docs/articles/cache.md index 406d6790..157bdb91 100644 --- a/docs/articles/cache.md +++ b/docs/articles/cache.md @@ -4,17 +4,23 @@ sidebar_label: Cache draft: true --- -The [Cache API](https://developer.mozilla.org/en-US/docs/Web/API/Cache) provides a persistent storage mechanism for Request / Response object pairs that are cached in long lived memory. +The [Cache API](https://developer.mozilla.org/en-US/docs/Web/API/Cache) provides +a persistent storage mechanism for Request / Response object pairs that are +cached in long lived memory. :::tip -Only a subset of the standard Cache API is supported. Below are the interfaces and methods that are supported and known limitations. +Only a subset of the standard Cache API is supported. Below are the interfaces +and methods that are supported and known limitations. ::: ## CacheStorage -The `CacheStorage` is exposed as the `caches` global object. This object allows you to open instances of a `Cache`. When calling `caches.open` if the named cache does not exist it will be created, otherwise the existing cache will be returned. +The `CacheStorage` is exposed as the `caches` global object. This object allows +you to open instances of a `Cache`. When calling `caches.open` if the named +cache does not exist it will be created, otherwise the existing cache will be +returned. **Definition** @@ -32,7 +38,8 @@ const cache = await caches.open("MY_CACHE"); ## Cache -The `Cache` object stores `Request` and `Response` objects based on header values. +The `Cache` object stores `Request` and `Response` objects based on header +values. **Definition** @@ -41,7 +48,7 @@ interface Cache { put(request: RequestInfo, response: Response): Promise; match( request: RequestInfo, - options?: CacheQueryOptions + options?: CacheQueryOptions, ): Promise; delete(request: RequestInfo, options?: CacheQueryOptions): Promise; } @@ -64,7 +71,9 @@ interface CacheQueryOptions { :::warning -At this time, the `options` parameter will be ignored entirely when running on in a developer environment (i.e. working copy). In non-developer environments, the `ignoreMethod` property is supported. All other properties will be ignored. +At this time, the `options` parameter will be ignored entirely when running on +in a developer environment (i.e. working copy). In non-developer environments, +the `ignoreMethod` property is supported. All other properties will be ignored. ::: @@ -74,7 +83,8 @@ At this time, the `options` parameter will be ignored entirely when running on i await cache.put(request, response); ``` -The `put()` method of the `Cache` interface allows key/value pairs to be added to the current Cache object. +The `put()` method of the `Cache` interface allows key/value pairs to be added +to the current Cache object. ### Match @@ -82,7 +92,9 @@ The `put()` method of the `Cache` interface allows key/value pairs to be added t const response = await cache.match(request); ``` -The `match()` method of the `Cache` interface returns a Promise that resolves to the Response associated with the first matching request in the Cache object. If no match is found, the Promise resolves to `undefined`. +The `match()` method of the `Cache` interface returns a Promise that resolves to +the Response associated with the first matching request in the Cache object. If +no match is found, the Promise resolves to `undefined`. ### Delete @@ -90,20 +102,27 @@ The `match()` method of the `Cache` interface returns a Promise that resolves to await cache.delete(request); ``` -The delete() method of the Cache interface finds the Cache entry whose key is the request, and if found, deletes the Cache entry and returns a Promise that resolves to true. If no Cache entry is found, it resolves to false. +The delete() method of the Cache interface finds the Cache entry whose key is +the request, and if found, deletes the Cache entry and returns a Promise that +resolves to true. If no Cache entry is found, it resolves to false. ## Headers -The following headers can be used to control the cache when adding a response using the `put()` method. +The following headers can be used to control the cache when adding a response +using the `put()` method. -- `Cache-Control`: Controls caching directives. [More info](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control) -- `ETag`: Allows cache.match() to evaluate conditional requests with If-None-Match. +- `Cache-Control`: Controls caching directives. + [More info](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control) +- `ETag`: Allows cache.match() to evaluate conditional requests with + If-None-Match. - `Expires`: A string that specifies when the resource becomes invalid. -- `Last-Modified`: Allows cache.match() to evaluate conditional requests with If-Modified-Since. +- `Last-Modified`: Allows cache.match() to evaluate conditional requests with + If-Modified-Since. ## Examples -The below example shows how to use a cached response and populate the cache in the event there is no response already cached. +The below example shows how to use a cached response and populate the cache in +the event there is no response already cached. ```ts const request = new Request(`https://echo.zuplo.io`); @@ -117,7 +136,8 @@ if (!response) { const data = await response.json(); ``` -If you just want to store the value, just create a new simple Response and set the `Cache-Control` header. +If you just want to store the value, just create a new simple Response and set +the `Cache-Control` header. ```ts const request = new Request(`https://echo.zuplo.io`); @@ -135,7 +155,9 @@ const cachedResponse = new Response(response, { await cache.put(request, cachedResponse); ``` -When adding to the cache, headers are used to control how long resources are stored. If you are reusing the response headers, make sure to account for additional cache headers that may have been sent. +When adding to the cache, headers are used to control how long resources are +stored. If you are reusing the response headers, make sure to account for +additional cache headers that may have been sent. ```ts const request = new Request(`https://echo.zuplo.io`); diff --git a/docs/articles/check-ip-address.md b/docs/articles/check-ip-address.md index 1f0580e1..a528590e 100644 --- a/docs/articles/check-ip-address.md +++ b/docs/articles/check-ip-address.md @@ -2,7 +2,8 @@ title: How to check an incoming IP address --- -Sometimes you want to access the true IP address of the gateway's client making the current request. To do this you can read the `true-client-ip` header: +Sometimes you want to access the true IP address of the gateway's client making +the current request. To do this you can read the `true-client-ip` header: ```ts const ip = request.headers.get("true-client-ip"); diff --git a/docs/articles/cloudflare-settings.md b/docs/articles/cloudflare-settings.md index 94e9ae26..6e45b13d 100644 --- a/docs/articles/cloudflare-settings.md +++ b/docs/articles/cloudflare-settings.md @@ -3,26 +3,55 @@ title: Zuplo and Cloudflare Settings sidebar_label: Cloudflare Settings --- -All Zuplo environments are deployed behind Cloudflare's Web Application Firewall, DDoS protection, Bot Detection, and SSL termination. The combination of Cloudflare's network infrastructure and Zuplo's API Gateway help provide critical security and performance capabilities to your API all with zero custom configuration. +All Zuplo environments are deployed behind Cloudflare's Web Application +Firewall, DDoS protection, Bot Detection, and SSL termination. The combination +of Cloudflare's network infrastructure and Zuplo's API Gateway help provide +critical security and performance capabilities to your API all with zero custom +configuration. ## Web Application Firewall Rules -By default, WAF settings are in log only mode. This means that no request should be blocked by the Web Application Firewall. +By default, WAF settings are in log only mode. This means that no request should +be blocked by the Web Application Firewall. -You can choose to enable some preconfigured default rulesets to protect your API Gateway. Our default rules provide a high level of protection with a low likelihood of causing false positives for API transactions. For enterprise customers, Zuplo offers the ability to customize WAF rules to suite your specific needs. +You can choose to enable some preconfigured default rulesets to protect your API +Gateway. Our default rules provide a high level of protection with a low +likelihood of causing false positives for API transactions. For enterprise +customers, Zuplo offers the ability to customize WAF rules to suite your +specific needs. ## DDos Protection -DDoS protection is available for every Zuplo API through [Cloudflare's DDoS protection service](https://support.cloudflare.com/hc/en-us/articles/200172676-Understanding-Cloudflare-DDoS-protection). By default, the **rule sensitivy** setting is set to **Essentially Off** meaning almost no requests will be blocked. For paying customers, we offer the ability to modify this setting as needed. +DDoS protection is available for every Zuplo API through +[Cloudflare's DDoS protection service](https://support.cloudflare.com/hc/en-us/articles/200172676-Understanding-Cloudflare-DDoS-protection). +By default, the **rule sensitivy** setting is set to **Essentially Off** meaning +almost no requests will be blocked. For paying customers, we offer the ability +to modify this setting as needed. ## Security Level -Cloudflare uses a blanket [security protection setting](https://support.cloudflare.com/hc/en-us/articles/200170056-Understanding-the-Cloudflare-Security-Level) that uses the IP reputation of a visitor to decide if the request should be blocked. By default, Zuplo this security setting is **Off** for all Zuplo APIs. For paying customers, this setting can be customized as needed for your API. +Cloudflare uses a blanket +[security protection setting](https://support.cloudflare.com/hc/en-us/articles/200170056-Understanding-the-Cloudflare-Security-Level) +that uses the IP reputation of a visitor to decide if the request should be +blocked. By default, Zuplo this security setting is **Off** for all Zuplo APIs. +For paying customers, this setting can be customized as needed for your API. ## Caching -Cloudflare's global CDN enables your API Gateway to set cache headers in order to reduce the number of requests that hit your origin API. By default, all APIs deployed to Zuplo are configured on Cloudflare to respect existing cache headers. The means that if you want content cached, simply set the [appropriate headers](https://developers.cloudflare.com/cache/about/cache-control/) and Cloudflare's global CDN will cache responses at the edge. +Cloudflare's global CDN enables your API Gateway to set cache headers in order +to reduce the number of requests that hit your origin API. By default, all APIs +deployed to Zuplo are configured on Cloudflare to respect existing cache +headers. The means that if you want content cached, simply set the +[appropriate headers](https://developers.cloudflare.com/cache/about/cache-control/) +and Cloudflare's global CDN will cache responses at the edge. ## Developer Environments -Generally, developer environments (i.e. any API running on the domain `zuplo.dev`) has similar security configurations as "production" environments (i.e. any API running on the domain `zuplo.app` or your own custom domain). There are some exceptions where we have slightly more security measures in place to prevent abuse of developer resources. If you see any issues or have requests blocked to your developer instance please contact [support@zuplo.com](mailto:support@zuplo.com) and we can assist with adjusting rules to ensure you can test your API as needed. +Generally, developer environments (i.e. any API running on the domain +`zuplo.dev`) has similar security configurations as "production" environments +(i.e. any API running on the domain `zuplo.app` or your own custom domain). +There are some exceptions where we have slightly more security measures in place +to prevent abuse of developer resources. If you see any issues or have requests +blocked to your developer instance please contact +[support@zuplo.com](mailto:support@zuplo.com) and we can assist with adjusting +rules to ensure you can test your API as needed. diff --git a/docs/articles/custom-audit-log-policy.md b/docs/articles/custom-audit-log-policy.md index 026e0a24..ebdc5ee6 100644 --- a/docs/articles/custom-audit-log-policy.md +++ b/docs/articles/custom-audit-log-policy.md @@ -3,13 +3,23 @@ title: Custom Audit Logging Policy sidebar_label: Audit Logging --- -Audit logging is an important part of API security that plays a critical role in detecting and correcting issues such as unauthorized access or permission elevations within your system. Audit logging is also a requirement for many compliance certifications as well as part of the buying criteria for larger enterprises. +Audit logging is an important part of API security that plays a critical role in +detecting and correcting issues such as unauthorized access or permission +elevations within your system. Audit logging is also a requirement for many +compliance certifications as well as part of the buying criteria for larger +enterprises. -Adding Audit Logging to your APIs that are secured with Zuplo is as easy as adding a custom policy. Typically you want to add audit logs to any API that modifies data, however depending on the API you may want it on read operations as well (i.e. retrieve a secret key, etc.) +Adding Audit Logging to your APIs that are secured with Zuplo is as easy as +adding a custom policy. Typically you want to add audit logs to any API that +modifies data, however depending on the API you may want it on read operations +as well (i.e. retrieve a secret key, etc.) ## Example Policy: WorkOS Audit Logs -[WorkOS](https://workos.com/) provides various services that help enable enterprise features on your service such as SSO and Audit Logs. With Zuplo it is easy to create a [custom policy](/docs/policies/custom-code-inbound) that uses [runtime hooks](./runtime-extensions.md) to log API calls using their API. +[WorkOS](https://workos.com/) provides various services that help enable +enterprise features on your service such as SSO and Audit Logs. With Zuplo it is +easy to create a [custom policy](/docs/policies/custom-code-inbound) that uses +[runtime hooks](./runtime-extensions.md) to log API calls using their API. ```ts import { ZuploContext, ZuploRequest, environment } from "@zuplo/runtime"; @@ -17,7 +27,7 @@ import { ZuploContext, ZuploRequest, environment } from "@zuplo/runtime"; export async function auditLogPlugin( request: ZuploRequest, context: ZuploContext, - policyName: string + policyName: string, ) { // Clone the request so the body can be read in the hook // note: remove this is you don't need content from the body diff --git a/docs/articles/custom-cors-policy.md b/docs/articles/custom-cors-policy.md index fa2251ec..f7b17f50 100644 --- a/docs/articles/custom-cors-policy.md +++ b/docs/articles/custom-cors-policy.md @@ -22,10 +22,10 @@ policies: A CORS policy consists of a name and set of CORS headers to be returned for cross-origin requests (both the simple type and pre-flight request). -:::warning +:::warning Make sure to not have a trailing `/` on your allowedOrigins. e.g. -`https://example.com` is valid, `https://example.com/` will not work. +`https://example.com` is valid, `https://example.com/` will not work. ::: @@ -83,7 +83,8 @@ For more information on CORS, check out the MDN documentation: ## Using environment variables -If you need to support different origins, headers, etc. in different environments, you can use environment variables +If you need to support different origins, headers, etc. in different +environments, you can use environment variables ```json { @@ -101,5 +102,3 @@ If you need to support different origins, headers, etc. in different environment ] } ``` - - diff --git a/docs/articles/custom-log-level.md b/docs/articles/custom-log-level.md index 41a8b420..b8fd1655 100644 --- a/docs/articles/custom-log-level.md +++ b/docs/articles/custom-log-level.md @@ -10,11 +10,15 @@ There are four logging levels in Zuplo: - `info` - `debug` -If the log level is set to `debug` - all entries are logged. If it is set to `warn`, only `warn` and `error` events are logged. Zuplo defaults the log level to `debug` (everything) for working-copy environments and `error` for everything else. You can override this by setting an [environment variable](../articles/environment-variables) called `ZUPLO_LOG_LEVEL` to one of the values above. +If the log level is set to `debug` - all entries are logged. If it is set to +`warn`, only `warn` and `error` events are logged. Zuplo defaults the log level +to `debug` (everything) for working-copy environments and `error` for everything +else. You can override this by setting an +[environment variable](../articles/environment-variables) called +`ZUPLO_LOG_LEVEL` to one of the values above. -:::warning -The log levels are case sensitive - they must be entered correctly, in lower case for logging to work. -::: +:::warning The log levels are case sensitive - they must be entered correctly, +in lower case for logging to work. ::: The log levels map to the different methods on `context.log`, e.g. diff --git a/docs/articles/custom-logging-example.md b/docs/articles/custom-logging-example.md index 8dc2af66..1bb738ac 100644 --- a/docs/articles/custom-logging-example.md +++ b/docs/articles/custom-logging-example.md @@ -2,9 +2,11 @@ title: Custom Logging Policy --- -Some of our customers want to build custom logging for their gateway runtime. This is an example of just how powerful the programmability of Zuplo is. +Some of our customers want to build custom logging for their gateway runtime. +This is an example of just how powerful the programmability of Zuplo is. -In this custom inbound policy we show how you could post to a service (in this case we just use RequestBin.com). +In this custom inbound policy we show how you could post to a service (in this +case we just use RequestBin.com). ```ts import { ZuploContext, ZuploRequest, ResponseSentEvent } from "@zuplo/runtime"; @@ -58,7 +60,7 @@ const logReqRes = async ( req: any, response: Response, context: ZuploContext, - start: number + start: number, ) => { // we don't want any errors thrown that might impact // our consumers experience so catch everything and @@ -83,7 +85,7 @@ export default async function ( request: ZuploRequest, context: ZuploContext, options: CustomLoggingOptions, - policyName: string + policyName: string, ) { // We need to read the body of the request before it's used by the handler // so let's serialize the request now @@ -99,7 +101,7 @@ export default async function ( req, event.response, context, - start + start, ); // We need to ask the runtime now to shut down until this is complete, @@ -126,10 +128,14 @@ We would then configure the policy as follows } ``` -And don't forget to register your new custom policy on your routes! This should be the very first inbound policy to see the incoming request, unmodified by other policies (or blocked by auth, rate-limiting etc). +And don't forget to register your new custom policy on your routes! This should +be the very first inbound policy to see the incoming request, unmodified by +other policies (or blocked by auth, rate-limiting etc). -You'll then see live entries with details of the requests and responses for your test calls: +You'll then see live entries with details of the requests and responses for your +test calls: ![Pipedream output](./media/pipedream.png) -You can create a free requestbin at [requestbin.com](https://requestbin.com) - to get started quickly look for the link to create a `public bin`. +You can create a free requestbin at [requestbin.com](https://requestbin.com) - +to get started quickly look for the link to create a `public bin`. diff --git a/docs/articles/dev-portal-create-consumer-on-auth.md b/docs/articles/dev-portal-create-consumer-on-auth.md index b9f00c99..8de0602d 100644 --- a/docs/articles/dev-portal-create-consumer-on-auth.md +++ b/docs/articles/dev-portal-create-consumer-on-auth.md @@ -68,7 +68,7 @@ const API_KEY_BUCKET = "my-bucket"; exports.onExecutePostLogin = async (event, api) => { if (event.user.app_metadata.api_consumer) { console.log( - `Skipping creating of API consumer. Already exists: ${event.user.app_metadata.api_consumer}` + `Skipping creating of API consumer. Already exists: ${event.user.app_metadata.api_consumer}`, ); return; } @@ -94,7 +94,7 @@ exports.onExecutePostLogin = async (event, api) => { Authorization: `Bearer ${event.secrets.API_KEY}`, "content-type": "application/json", }, - } + }, ); const result = await response.json(); if (response.status !== 200) { diff --git a/docs/articles/dev-portal-supabase-auth.md b/docs/articles/dev-portal-supabase-auth.md index 611070ac..aa7ad1a9 100644 --- a/docs/articles/dev-portal-supabase-auth.md +++ b/docs/articles/dev-portal-supabase-auth.md @@ -140,7 +140,7 @@ Auth App codebase from earlier. formAction={`/auth/sign-up${ typeof searchParams?.["session-create-url"] === "string" ? `?session-create-url=${encodeURIComponent( - searchParams["session-create-url"] + searchParams["session-create-url"], )}` : "" }`} @@ -200,7 +200,7 @@ Auth App codebase from earlier. `${requestUrl.origin}/login?error=Could not authenticate user`, { status: 301, - } + }, ); } @@ -229,7 +229,7 @@ Auth App codebase from earlier. `${requestUrl.origin}/login?error=Could not authenticate user`, { status: 301, - } + }, ); } @@ -328,7 +328,7 @@ to sign into your Supabase project via the Developer Portal { // a 301 status is required to redirect from a POST to a GET route status: 301, - } + }, ); } @@ -359,7 +359,7 @@ to sign into your Supabase project via the Developer Portal `${requestUrl.origin}/login?error=Could not authenticate user`, { status: 301, - } + }, ); } diff --git a/docs/articles/dev-portal-theme.md b/docs/articles/dev-portal-theme.md index a4e906ef..bf0de5fd 100644 --- a/docs/articles/dev-portal-theme.md +++ b/docs/articles/dev-portal-theme.md @@ -3,7 +3,11 @@ title: Developer Portal Theme sidebar_label: Theming --- -The developer portal supports custom theming by editing the `docs/theme.css` file in your project. The following [CSS variables](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties) and classes are supported. By default, we support a `light` and a `dark` theme, which can be toggled between by the user. +The developer portal supports custom theming by editing the `docs/theme.css` +file in your project. The following +[CSS variables](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties) +and classes are supported. By default, we support a `light` and a `dark` theme, +which can be toggled between by the user. ## Variables @@ -46,7 +50,8 @@ The developer portal supports custom theming by editing the `docs/theme.css` fil ## Theming -The theme color is set using the `.dark` or `.light` CSS class on the `body` like in the example below. +The theme color is set using the `.dark` or `.light` CSS class on the `body` +like in the example below. ```html @@ -86,8 +91,12 @@ The logo of the portal is set via the CSS class `.theme-logo`. :::danger -Custom styles and CSS variables beyond what is documented above are not officially supported and may break with future releases. Use custom CSS with caution. +Custom styles and CSS variables beyond what is documented above are not +officially supported and may break with future releases. Use custom CSS with +caution. ::: -The `theme.css` stylesheet is injected in the `` of the dev portal. As such, you can modify any style you like. The portal is built using [Tailwind CSS](https://tailwindcss.com/). +The `theme.css` stylesheet is injected in the `` of the dev portal. As +such, you can modify any style you like. The portal is built using +[Tailwind CSS](https://tailwindcss.com/). diff --git a/docs/articles/environment-variables.md b/docs/articles/environment-variables.md index f498db4f..07ef54c9 100644 --- a/docs/articles/environment-variables.md +++ b/docs/articles/environment-variables.md @@ -2,25 +2,35 @@ title: Environment Variables --- -Environment variables are key-value pairs that are stored outside of source code. The values of environment variables can be applied to particular environments in order to change behavior or configuration. +Environment variables are key-value pairs that are stored outside of source +code. The values of environment variables can be applied to particular +environments in order to change behavior or configuration. -Environment variables can be read into source code and many configuration files in your project. Variables are only applied to environments on new deployments. If you change an environment variable, you must redeploy the environment in order for the updated value to take effect. +Environment variables can be read into source code and many configuration files +in your project. Variables are only applied to environments on new deployments. +If you change an environment variable, you must redeploy the environment in +order for the updated value to take effect. -Environment variables can be configuration or secrets. While all values are stored encrypted at rest, only non-secret values can be read. Secrets are write-only, meaning the value cannot be retrieved once it is set. +Environment variables can be configuration or secrets. While all values are +stored encrypted at rest, only non-secret values can be read. Secrets are +write-only, meaning the value cannot be retrieved once it is set. ## Environment Variable Editor -To set environment variables in your project, open the **Settings** tab and then select the **Environment Variables** section. +To set environment variables in your project, open the +**Settings** tab and then select the **Environment Variables** section. To create a new variable, click **Add new variable**. ![](https://cdn.zuplo.com/assets/bec84962-0139-4371-b3fd-a30e70860169.png) -Enter the name and value of your environment variable and select if you would like the value to be a secret or a regular value. +Enter the name and value of your environment variable and select if you would +like the value to be a secret or a regular value. ## Environments -Environment variables can be applied to one or many different environments. You can select one or more environments in which to apply the variable. +Environment variables can be applied to one or many different environments. You +can select one or more environments in which to apply the variable. | Environment | Description | | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | @@ -28,13 +38,18 @@ Environment variables can be applied to one or many different environments. You | Preview | Any environment that is deployed from source control that is **not the default** branch. (i.e. `staging` or `preview`). This also includes any branch that is created from a pull request. | | Working Copy | Any environment that is deployed while developing with the portal. Each developer gets their own working copy. Working copies are always deployed to `zuplo.dev` | -For the **Preview** environment option, a specific named environment can be selected. For example, if you want a variable set only for the environment deployed from the `staging` branch in source control. +For the **Preview** environment option, a specific named environment can be +selected. For example, if you want a variable set only for the environment +deployed from the `staging` branch in source control. -For the **Working Copy** option, developers can set a personal override. This value **ONLY** applies to the developer who set the value. +For the **Working Copy** option, developers can set a personal override. This +value **ONLY** applies to the developer who set the value. :::info -A single environment variable name cannot overlap environments. For example, if you set a variable named `MY_VAR` and select all the environments a second variable named `MY_VAR` cannot be set on say the **Production** environments. +A single environment variable name cannot overlap environments. For example, if +you set a variable named `MY_VAR` and select all the environments a second +variable named `MY_VAR` cannot be set on say the **Production** environments. ::: @@ -58,9 +73,11 @@ const myVar = environment.MY_VAR; ### Configuration Files -Inside some configuration files, environment variables can be referenced with the pattern `$env(MY_VAR)`. +Inside some configuration files, environment variables can be referenced with +the pattern `$env(MY_VAR)`. -For example, in the `policies.json` file, an environment variable could be set on a policy option. +For example, in the `policies.json` file, an environment variable could be set +on a policy option. ```json { @@ -79,7 +96,9 @@ For example, in the `policies.json` file, an environment variable could be set o ### Rewrite & Forwarding Handler -When referencing environment variables inside of the URL Rewrite handler and the URL Forward handler, variables are substituted using Javascript style string interpolation. +When referencing environment variables inside of the URL Rewrite handler and the +URL Forward handler, variables are substituted using Javascript style string +interpolation. ```txt https://${env.API_URL}/path/to/call diff --git a/docs/articles/environments.md b/docs/articles/environments.md index 0ec1c317..42896f7b 100644 --- a/docs/articles/environments.md +++ b/docs/articles/environments.md @@ -2,39 +2,69 @@ title: Environments --- -One of the things that makes Zuplo different from most API gateways, and API management platforms is that you can rapidly deploy many environments. Some of our customers have as many as 250 concurrently deployed edge environments! This facilitates collaboration, where teams can collaborate on new features with a dedicated environment, deployed for no additional cost in under 20s. +One of the things that makes Zuplo different from most API gateways, and API +management platforms is that you can rapidly deploy many environments. Some of +our customers have as many as 250 concurrently deployed edge environments! This +facilitates collaboration, where teams can collaborate on new features with a +dedicated environment, deployed for no additional cost in under 20s. ## Environment Types There are two distinct types of environment: -- **working-copy** - this is your development environment. You can think of this as your personal cloud laptop. To deploy to this environment you just need to save a change in portal.zuplo.com, that will automatically trigger a build and deploy of your working-copy. A working-copy environment typically ends in a `.dev` URL. These are _not_ deployed to the edge and run in a data-center. There are some subtle differences in behavior between edge deployments and working-copies, so it is important to test both before going to production. Note, every developer using Zuplo gets a private working-copy. These are not shared with anybody else. If you share a project with another person, they will have their own working-copy environment. - -:::warning -Never use a working-copy environment in a production setting. They offer no SLA, and will often sleep after inactivity leading to slower cold-start times. There can be occasional downtime of a few seconds for upgrades. -::: - -- **edge deployment** - these are real deployments that are deployed to the edge at 100s of data-centers around the world. They have 0ms startup time and offer very high availability due to their headless, distributed nature. You can deploy edge environments using our [GitHub integration](/docs/articles/source-control) or building a [custom CI/CD pipeline](/docs/articles/custom-ci-cd). - -Edge deployments typically have a domain ending with `.app` but you can also configure [custom vanity domains](/docs/articles/custom-domains). - -Edge deployments cannot be directly edited in portal.zuplo.com but they can be viewed - so you can see the source code currently deployed to an environment. +- **working-copy** - this is your development environment. You can think of this + as your personal cloud laptop. To deploy to this environment you just need to + save a change in portal.zuplo.com, that will automatically trigger a build and + deploy of your working-copy. A working-copy environment typically ends in a + `.dev` URL. These are _not_ deployed to the edge and run in a data-center. + There are some subtle differences in behavior between edge deployments and + working-copies, so it is important to test both before going to production. + Note, every developer using Zuplo gets a private working-copy. These are not + shared with anybody else. If you share a project with another person, they + will have their own working-copy environment. + +:::warning Never use a working-copy environment in a production setting. They +offer no SLA, and will often sleep after inactivity leading to slower cold-start +times. There can be occasional downtime of a few seconds for upgrades. ::: + +- **edge deployment** - these are real deployments that are deployed to the edge + at 100s of data-centers around the world. They have 0ms startup time and offer + very high availability due to their headless, distributed nature. You can + deploy edge environments using our + [GitHub integration](/docs/articles/source-control) or building a + [custom CI/CD pipeline](/docs/articles/custom-ci-cd). + +Edge deployments typically have a domain ending with `.app` but you can also +configure [custom vanity domains](/docs/articles/custom-domains). + +Edge deployments cannot be directly edited in portal.zuplo.com but they can be +viewed - so you can see the source code currently deployed to an environment. ## Navigating Environments -At the top left corner of portal.zuplo.com you will see your **project/environment** selector. This consists of two selectors separated by a `/`. +At the top left corner of portal.zuplo.com you will see your +**project/environment** selector. This consists of two selectors separated by a +`/`. -On the left is the current project - you can change project by clicking on the project name (or create a new project). +On the left is the current project - you can change project by clicking on the +project name (or create a new project). -One the right is the current **environment**. By default you will be looking at **working-copy** which is your private, editable, instance of the current project you are working on. +One the right is the current **environment**. By default you will be looking at +**working-copy** which is your private, editable, instance of the current +project you are working on. ![Environments](./media/environments.png) -If you have other any **edge-deployments** you will see them listed alongside your working-copy. In this case we have two edge-deployments called `development` and `prod (main)`. +If you have other any **edge-deployments** you will see them listed alongside +your working-copy. In this case we have two edge-deployments called +`development` and `prod (main)`. -For users using [GitHub integration](/docs/articles/source-control) the name of the deployment matches the branch name (yes - creating a new environment is literally as easy as creating a new branch). +For users using [GitHub integration](/docs/articles/source-control) the name of +the deployment matches the branch name (yes - creating a new environment is +literally as easy as creating a new branch). -You can't edit the code of an edge-deployment in portal.zuplo.com but you can switch into those environments to perform a number of functions, such as: +You can't edit the code of an edge-deployment in portal.zuplo.com but you can +switch into those environments to perform a number of functions, such as: - edit API consumers for this environment - view analytics for this environment @@ -42,7 +72,11 @@ You can't edit the code of an edge-deployment in portal.zuplo.com but you can sw ## Different Backends per Environment -It's common to want a different backend for your production, staging and preview environments. This can be easily achieved by using [environment variables](./environment-variables.md) to specify the origin of the backend and then using that in your [URL Rewrite Handlers](../handlers/url-rewrite.md). +It's common to want a different backend for your production, staging and preview +environments. This can be easily achieved by using +[environment variables](./environment-variables.md) to specify the origin of the +backend and then using that in your +[URL Rewrite Handlers](../handlers/url-rewrite.md). For example, @@ -50,7 +84,9 @@ For example, ${env.BASE_PATH}${pathname} ``` -A url rewrite like this will combine the `BASE_PATH` environment variable, say `https://example.com` with the incoming path, e.g. `/foo/bar` to create a re-written URL: +A url rewrite like this will combine the `BASE_PATH` environment variable, say +`https://example.com` with the incoming path, e.g. `/foo/bar` to create a +re-written URL: ```json https://example.com/foo/bar diff --git a/docs/articles/graphql-security.md b/docs/articles/graphql-security.md index 9eae7cb7..b149c5d1 100644 --- a/docs/articles/graphql-security.md +++ b/docs/articles/graphql-security.md @@ -5,27 +5,44 @@ sidebar_label: Secure your GraphQL API **Secure your GraphQL API with Zuplo** -GraphQL is a powerful query language for your APIs. While it offers great flexibility for clients, it also exposes potential security risks. Fortunately, with Zuplo, you can secure your GraphQL API by implementing various policies. This article walks you through our security policies: [GraphQL Complexity Limit](/docs/policies/graphql-complexity-limit-inbound), and [GraphQL Disable Introspection](/docs/policies/graphql-disable-introspection-inbound). +GraphQL is a powerful query language for your APIs. While it offers great +flexibility for clients, it also exposes potential security risks. Fortunately, +with Zuplo, you can secure your GraphQL API by implementing various policies. +This article walks you through our security policies: +[GraphQL Complexity Limit](/docs/policies/graphql-complexity-limit-inbound), and +[GraphQL Disable Introspection](/docs/policies/graphql-disable-introspection-inbound). ### 1. Understanding the Risks #### a. Deeply Nested Queries -Without restrictions, a client can send deeply nested queries that could potentially overwhelm your server, resulting in a Denial of Service (DoS) attack. This is because deeply nested queries can cause the server to process a huge amount of data and operations. While this might be a planed attack, it could also be a mistake by a client who is unaware of the query depth limit. +Without restrictions, a client can send deeply nested queries that could +potentially overwhelm your server, resulting in a Denial of Service (DoS) +attack. This is because deeply nested queries can cause the server to process a +huge amount of data and operations. While this might be a planed attack, it +could also be a mistake by a client who is unaware of the query depth limit. #### b. Query Complexity -Even without deep nesting, a query can be crafted to be very complex. This could force your server to execute resource-intensive operations, potentially slowing down the system or causing a DoS. +Even without deep nesting, a query can be crafted to be very complex. This could +force your server to execute resource-intensive operations, potentially slowing +down the system or causing a DoS. #### c. Introspection -GraphQL allows clients to introspect your schema. While this is beneficial during development, it can expose detailed schema information to potential attackers in production. +GraphQL allows clients to introspect your schema. While this is beneficial +during development, it can expose detailed schema information to potential +attackers in production. ### 2. Add your GraphQL API & setting up Policies #### Setup POST endpoint -If you did not already do so, you need to setup a POST endpoint in your API. This endpoint will be used to send the GraphQL queries to your API. We'll use the urlRewriteHandler to rewrite the request to your GraphQL API. -For this example let's assume your GraphQL API is hosted at `https://api.example.com/graphql`. + +If you did not already do so, you need to setup a POST endpoint in your API. +This endpoint will be used to send the GraphQL queries to your API. We'll use +the urlRewriteHandler to rewrite the request to your GraphQL API. For this +example let's assume your GraphQL API is hosted at +`https://api.example.com/graphql`. To do so, add the following to your `config/routes.oas.json` @@ -55,67 +72,74 @@ To do so, add the following to your `config/routes.oas.json` } ``` -For all the risks mentioned above, Zuplo offers policies that can be configured to protect your GraphQL API. These policies can be configured for Zuplo. -We'll create the configuration for the three policies "graphql-depth-limit", "graphql-complexity-points" and "graphql-disable-introspection-policy" next. +For all the risks mentioned above, Zuplo offers policies that can be configured +to protect your GraphQL API. These policies can be configured for Zuplo. We'll +create the configuration for the three policies "graphql-depth-limit", +"graphql-complexity-points" and "graphql-disable-introspection-policy" next. #### a. GraphQL Depth Limit -This policy limits how deep a query can be nested. You can find the detailed documentation [here](/docs/policies/graphql-complexity-limit-inbound) +This policy limits how deep a query can be nested. You can find the detailed +documentation [here](/docs/policies/graphql-complexity-limit-inbound) Add this into your `config/policies.json` ```json { "policies": [ - { - "name": "graphql-complexity-limit-policy", - "policyType": "graphql-complexity-limit-inbound", - "handler": { - "export": "GraphQLComplexityLimitInboundPolicy", - "module": "$import(@zuplo/graphql)", - "options": { - "useDepthLimit": { - "depthLimit": 20 - } - } + { + "name": "graphql-complexity-limit-policy", + "policyType": "graphql-complexity-limit-inbound", + "handler": { + "export": "GraphQLComplexityLimitInboundPolicy", + "module": "$import(@zuplo/graphql)", + "options": { + "useDepthLimit": { + "depthLimit": 20 + } } - } - ] + } + } + ] } ``` -By limiting the query depth, you prevent malicious or mistakenly deep queries from consuming excessive server resources. +By limiting the query depth, you prevent malicious or mistakenly deep queries +from consuming excessive server resources. #### b. GraphQL Complexity Limit -We can extend the same policy to also check for compexity. By defining a max for each type of operation, and then it limits the total complexity a query can have. -In order to use the Complexity Limit you need to allow introspection in your GraphQL API. +We can extend the same policy to also check for compexity. By defining a max for +each type of operation, and then it limits the total complexity a query can +have. In order to use the Complexity Limit you need to allow introspection in +your GraphQL API. ```json { "policies": [ - { - "name": "graphql-complexity-limit-policy", - "policyType": "graphql-complexity-limit-inbound", - "handler": { - "export": "GraphQLComplexityLimitInboundPolicy", - "module": "$import(@zuplo/graphql)", - "options": { - "useDepthLimit": { - "depthLimit": 20 - }, - "useComplexityLimit": { - "complexityLimit": 50, - "endpointUrl": "https://api.example.com/graphql" - } - } + { + "name": "graphql-complexity-limit-policy", + "policyType": "graphql-complexity-limit-inbound", + "handler": { + "export": "GraphQLComplexityLimitInboundPolicy", + "module": "$import(@zuplo/graphql)", + "options": { + "useDepthLimit": { + "depthLimit": 20 + }, + "useComplexityLimit": { + "complexityLimit": 50, + "endpointUrl": "https://api.example.com/graphql" + } } - } - ] + } + } + ] } ``` -The complexity limit ensures that a potential attacker cannot overload the system by sending overly complicated queries. +The complexity limit ensures that a potential attacker cannot overload the +system by sending overly complicated queries. #### c. GraphQL Disable Introspection @@ -124,22 +148,27 @@ Disable introspection in production environments to hide schema details. ```json { "policies": [ - { - "name": "graphql-disable-introspection-policy", - "policyType": "graphql-disable-introspection-inbound", - "handler": { - "export": "GraphQLDisableIntrospectionInboundPolicy", - "module": "$import(@zuplo/graphql)", - "options": { - } - } - } - ] + { + "name": "graphql-disable-introspection-policy", + "policyType": "graphql-disable-introspection-inbound", + "handler": { + "export": "GraphQLDisableIntrospectionInboundPolicy", + "module": "$import(@zuplo/graphql)", + "options": {} + } + } + ] } ``` -By disabling introspection in production, you prevent attackers from gaining insights into your GraphQL schema, thereby reducing potential attack vectors. -### 4. Example Repository +By disabling introspection in production, you prevent attackers from gaining +insights into your GraphQL schema, thereby reducing potential attack vectors. -For those who prefer a hands-on approach or wish to see these configurations in action, we've created a GitHub repository with everything set up. This repository offers a comprehensive example of how to configure and secure a GraphQL API using Zuplo. Check out the [GraphQL API with Zuplo example repository](https://github.com/zuplo/zuplo-graphql-example) to dive deeper. +### 4. Example Repository +For those who prefer a hands-on approach or wish to see these configurations in +action, we've created a GitHub repository with everything set up. This +repository offers a comprehensive example of how to configure and secure a +GraphQL API using Zuplo. Check out the +[GraphQL API with Zuplo example repository](https://github.com/zuplo/zuplo-graphql-example) +to dive deeper. diff --git a/docs/articles/handling-form-data.md b/docs/articles/handling-form-data.md index 5b6f0036..9dbcfc12 100644 --- a/docs/articles/handling-form-data.md +++ b/docs/articles/handling-form-data.md @@ -2,13 +2,17 @@ title: Handling FormData --- -Zuplo supports working with `FormData` including `multipart/form-data`. In this simple example we show how you can parse a multipart entry and read the stream into memory for use in the Zuplo runtime. +Zuplo supports working with `FormData` including `multipart/form-data`. In this +simple example we show how you can parse a multipart entry and read the stream +into memory for use in the Zuplo runtime. -In this case, we upload a JSON file as a multipart/form-data entry using Insomnia, with a key `foo` +In this case, we upload a JSON file as a multipart/form-data entry using +Insomnia, with a key `foo` ![Insomnia multipart/form-data](https://cdn.zuplo.com/assets/2d372851-af24-429b-8eeb-cb880589f30d.png) -We can then handle this programmatically inside Zuplo using a function handler. We also modify the JSON before forwarding on to the target backend server. +We can then handle this programmatically inside Zuplo using a function handler. +We also modify the JSON before forwarding on to the target backend server. ```ts import { ZuploContext, ZuploRequest } from "@zuplo/runtime"; diff --git a/docs/articles/http-problems.md b/docs/articles/http-problems.md index e421ea2c..7c318a4c 100644 --- a/docs/articles/http-problems.md +++ b/docs/articles/http-problems.md @@ -158,7 +158,7 @@ HttpProblems.badRequest( }, { "my-error-code": "230", - } + }, ); ``` diff --git a/docs/articles/key-value-store.md b/docs/articles/key-value-store.md index fdbfe623..2b7c99d9 100644 --- a/docs/articles/key-value-store.md +++ b/docs/articles/key-value-store.md @@ -3,7 +3,11 @@ title: Key Value Store sidebar_label: Key Value Store --- -Zuplo has a globally distributed key-value store called `KeyValueStore`. When running in developer mode (e.g. in the portal at portal.zuplo.com) your cache will reset with each build. When running in production mode at the edge the cache will support edge distribution - it can take up to ~1 minute for writes to sync across all nodes globally (though is usually much quicker). +Zuplo has a globally distributed key-value store called `KeyValueStore`. When +running in developer mode (e.g. in the portal at portal.zuplo.com) your cache +will reset with each build. When running in production mode at the edge the +cache will support edge distribution - it can take up to ~1 minute for writes to +sync across all nodes globally (though is usually much quicker). ### Example diff --git a/docs/articles/lazy-load-configuration-into-cache.md b/docs/articles/lazy-load-configuration-into-cache.md index 80eb7e30..5ce013d8 100644 --- a/docs/articles/lazy-load-configuration-into-cache.md +++ b/docs/articles/lazy-load-configuration-into-cache.md @@ -20,8 +20,7 @@ and zone cache in combination to afford the lowest possible latency. :::warning Do take care not to load so much data into memory that you OOM (out-of-memory) your process. Processes in Zuplo typically have ~120MB of memory -to perform all their work, including holding request bodies etc. -::: +to perform all their work, including holding request bodies etc. ::: Here's a simple example of the usage of MemoryZoneReadthroughCache being used to store configuration data. @@ -59,7 +58,7 @@ async function loadConfig(context: ZuploContext) { if (response.status !== 200) { throw new Error( - `Error reading config ${response.status}: '${await response.text()}'` + `Error reading config ${response.status}: '${await response.text()}'`, ); } diff --git a/docs/articles/log-export.md b/docs/articles/log-export.md index 876a1be0..d4144d1d 100644 --- a/docs/articles/log-export.md +++ b/docs/articles/log-export.md @@ -4,11 +4,14 @@ title: Log Export :::note -The log export option is available only to customers on a Zuplo enterprise plan. For more information contact [sales@zuplo.com](mailto:sales@zuplo.com). +The log export option is available only to customers on a Zuplo enterprise plan. +For more information contact [sales@zuplo.com](mailto:sales@zuplo.com). ::: -In addition to viewing logs in the the portal, it may be useful to have logs pushed to you so that they can be used in your monitoring, alerting, and troubleshooting processes. +In addition to viewing logs in the the portal, it may be useful to have logs +pushed to you so that they can be used in your monitoring, alerting, and +troubleshooting processes. Zuplo supports pushing logs to the following sources: @@ -17,9 +20,13 @@ Zuplo supports pushing logs to the following sources: - Google Cloud Storage - Cloudflare R2 -Logs are batched and sent withing one minute (not guaranteed, but generally less than one minute). Batches will typically contain no more than 10,000 records, but depending on volume of your API a batch could have up to 100,000 records. +Logs are batched and sent withing one minute (not guaranteed, but generally less +than one minute). Batches will typically contain no more than 10,000 records, +but depending on volume of your API a batch could have up to 100,000 records. -Logs are send as compressed gzip files. Each gzip file has a single text file with each log line containing a log entry serialized as JSON. For example a single file would look like the below. +Logs are send as compressed gzip files. Each gzip file has a single text file +with each log line containing a log entry serialized as JSON. For example a +single file would look like the below. ``` {"ClientIP":"2a16:95c0:3300::103","ClientRequestHost":"api.example.com","ClientRequestMethod":"PO...} @@ -39,13 +46,18 @@ Logs are send as compressed gzip files. Each gzip file has a single text file wi {"ClientIP":"35.199.23.186","ClientRequestHost":"api.example.com","ClientRequestMethod":"GET","Cl...} ``` -To send the logs into the system of your choice, run a job on either a schedule or based on events from your storage provider. +To send the logs into the system of your choice, run a job on either a schedule +or based on events from your storage provider. In order to activate log export for your account, contact your support. You will need to provide us with the following information: -1. The credentials to your storage account - typically something like an access key and secret. +1. The credentials to your storage account - typically something like an access + key and secret. 2. The url of the storage account -Note, the storage account that you provide to us only needs permission to write files. We don't need to list or delete buckets or objects. If you would like to clean up objects, we recommend doing that after you ingest the logs or through expiration policies on the storage bucket itself. +Note, the storage account that you provide to us only needs permission to write +files. We don't need to list or delete buckets or objects. If you would like to +clean up objects, we recommend doing that after you ingest the logs or through +expiration policies on the storage bucket itself. diff --git a/docs/articles/log-plugin-vmware-log-insight.md b/docs/articles/log-plugin-vmware-log-insight.md index 4d35201c..3f8c2bd8 100644 --- a/docs/articles/log-plugin-vmware-log-insight.md +++ b/docs/articles/log-plugin-vmware-log-insight.md @@ -27,7 +27,7 @@ export function runtimeInit(runtime: RuntimeExtensions) { fields: { appname: "zuplo", }, - }) + }), ); } ``` diff --git a/docs/articles/log-plugins.md b/docs/articles/log-plugins.md index 04e99585..8cc1c81f 100644 --- a/docs/articles/log-plugins.md +++ b/docs/articles/log-plugins.md @@ -51,7 +51,7 @@ export function runtimeInit(runtime: RuntimeExtensions) { secretAccessKey: environment.AWS_SECRET_ACCESS_KEY, logGroupName: "zuplo", logStreamName: "my-stream", - }) + }), ); } ``` @@ -70,7 +70,7 @@ export function runtimeInit(runtime: RuntimeExtensions) { new DataDogLoggingPlugin({ url: "https://http-intake.logs.datadoghq.com/api/v2/logs", apiKey: environment.DATADOG_API_KEY, - }) + }), ); } ``` @@ -95,7 +95,7 @@ export function runtimeInit(runtime: RuntimeExtensions) { new DynaTraceLoggingPlugin({ url: "https://xxxxxxx.live.dynatrace.com/api/v2/logs/ingest", apiToken: environment.DYNATRACE_API_TOKEN, - }) + }), ); } ``` @@ -114,7 +114,7 @@ export function runtimeInit(runtime: RuntimeExtensions) { new GoogleCloudLoggingPlugin({ logName: "my-api-gateway", serviceAccountJson: environment.GCP_SERVICE_ACCOUNT, - }) + }), ); } ``` @@ -134,7 +134,7 @@ export function runtimeInit(runtime: RuntimeExtensions) { url: "https://logs-prod-us-central1.grafana.net/loki/api/v1/push", username: "my-username", password: environment.LOKI_PASSWORD, - }) + }), ); } ``` @@ -160,7 +160,7 @@ export function runtimeInit(runtime: RuntimeExtensions) { runtime.addPlugin( new SumoLogicLoggingPlugin({ url: "https://endpoint4.collection.sumologic.com/receiver/v1/http/XXXXXX", - }) + }), ); } ``` diff --git a/docs/articles/metrics-plugins.md b/docs/articles/metrics-plugins.md index cecaa149..238ce643 100644 --- a/docs/articles/metrics-plugins.md +++ b/docs/articles/metrics-plugins.md @@ -90,7 +90,7 @@ export function runtimeInit(runtime: RuntimeExtensions) { method: false, statusCode: false, }, - }) + }), ); } ``` @@ -172,7 +172,7 @@ export function runtimeInit(runtime: RuntimeExtensions) { method: false, statusCode: false, }, - }) + }), ); } ``` diff --git a/docs/articles/multiple-auth-policies.md b/docs/articles/multiple-auth-policies.md index 543b2ce7..eed88f43 100644 --- a/docs/articles/multiple-auth-policies.md +++ b/docs/articles/multiple-auth-policies.md @@ -3,11 +3,16 @@ title: Handling Multiple Authentication Policies sidebar_label: Multiple Auth Policies --- -Sometimes multiple types of authentication are needed on an API. For example, an API could support JWT Authentication and API Key authentication or two different OAuth providers (i.e. Azure AD for employees and Auth0 for partners). Configuring multiple policies in Zuplo can be done in several ways. +Sometimes multiple types of authentication are needed on an API. For example, an +API could support JWT Authentication and API Key authentication or two different +OAuth providers (i.e. Azure AD for employees and Auth0 for partners). +Configuring multiple policies in Zuplo can be done in several ways. ## JWT and API Key Authentication -JWT and API Key authentication can be handled by adding three policies to a route (or using [composite policies](../policies/composite-inbound.md) to keep everything organized). The three policies required are: +JWT and API Key authentication can be handled by adding three policies to a +route (or using [composite policies](../policies/composite-inbound.md) to keep +everything organized). The three policies required are: 1. [API Key Authentication Policy](../policies/api-key-inbound.md) 1. [Any JWT Authentication Policy](../policies/open-id-jwt-auth-inbound.md) @@ -15,25 +20,42 @@ JWT and API Key authentication can be handled by adding three policies to a rout :::warning -The order of these policies is critical. Placing them in the wrong order can cause errors or lead to security issues. +The order of these policies is critical. Placing them in the wrong order can +cause errors or lead to security issues. ::: -All of the Zuplo built-in authentication policies have an option called `allowUnauthenticatedRequests`. This option is `true` by default, meaning for an anonymous request, the policy will immediately return a response with a 401: Unauthorized status. **In the case of multiple policies, this setting must be `false`.** This means that even if the request is NOT authenticated, the policy will still let the request through. +All of the Zuplo built-in authentication policies have an option called +`allowUnauthenticatedRequests`. This option is `true` by default, meaning for an +anonymous request, the policy will immediately return a response with a 401: +Unauthorized status. **In the case of multiple policies, this setting must be +`false`.** This means that even if the request is NOT authenticated, the policy +will still let the request through. :::tip -The option `allowUnauthenticatedRequests` can also be used to make a route work for both authenticated and anonymous users. Allowing unauthenticated requests on a public API allows modifying behaviors based on the type of user. For example, rate limits could be set higher for authenticated users. +The option `allowUnauthenticatedRequests` can also be used to make a route work +for both authenticated and anonymous users. Allowing unauthenticated requests on +a public API allows modifying behaviors based on the type of user. For example, +rate limits could be set higher for authenticated users. ::: -In the route that handles multiple authentication policies, add the API Key Authentication policy and the JWT Authentication policy of your choice (i.e. Auth0, Okta, Cognito, etc.). For both policies, set the option `allowUnauthenticatedRequests` to `true`. +In the route that handles multiple authentication policies, add the API Key +Authentication policy and the JWT Authentication policy of your choice (i.e. +Auth0, Okta, Cognito, etc.). For both policies, set the option +`allowUnauthenticatedRequests` to `true`. Configure the other options as usual. -Finally, if the route **requires** authentication, a third policy is necessary to enforce that after both authentication policies run, the request has been authenticated. Generally, this policy would come immediately **after** the two authentication policies. +Finally, if the route **requires** authentication, a third policy is necessary +to enforce that after both authentication policies run, the request has been +authenticated. Generally, this policy would come immediately **after** the two +authentication policies. -To enforce authentication, check that the value of `request.user.sub` is as expected. Additional checks can be added as your API requires. Below is an example of a simple authentication check policy. +To enforce authentication, check that the value of `request.user.sub` is as +expected. Additional checks can be added as your API requires. Below is an +example of a simple authentication check policy. ```ts import { ZuploContext, ZuploRequest } from "@zuplo/runtime"; @@ -47,4 +69,8 @@ export default async function (request: ZuploRequest, context: ZuploContext) { ### Multiple JWT Authentication Policies -Multiple JWT authentication policies is slightly more challenging than adding JWT and API key authentication because JWT authentication performs validation on the token value. In some cases, the easiest approach is to write a custom policy that checks the issuer of the token first then applies validation appropriate for each issuer. +Multiple JWT authentication policies is slightly more challenging than adding +JWT and API key authentication because JWT authentication performs validation on +the token value. In some cases, the easiest approach is to write a custom policy +that checks the issuer of the token first then applies validation appropriate +for each issuer. diff --git a/docs/articles/not-found-handler.md b/docs/articles/not-found-handler.md index b49326f2..6ae904fb 100644 --- a/docs/articles/not-found-handler.md +++ b/docs/articles/not-found-handler.md @@ -27,7 +27,7 @@ export function runtimeInit(runtime: RuntimeExtensions) { request, context, {}, - { allow: allowedMethods } + { allow: allowedMethods }, ); } diff --git a/docs/articles/per-user-rate-limits-using-db.md b/docs/articles/per-user-rate-limits-using-db.md index 4af6ecba..06549367 100644 --- a/docs/articles/per-user-rate-limits-using-db.md +++ b/docs/articles/per-user-rate-limits-using-db.md @@ -2,13 +2,23 @@ title: Per user rate-limiting using a database and the ZoneCache --- -In this example we show a more advanced implementation of [dynamic rate limiting](../articles/per-user-rate-limits-using-db.md). It uses a database lookup to get the customer details and combines that with the ZoneCache to improve performance, reduce latency and lower the load on the database. +In this example we show a more advanced implementation of +[dynamic rate limiting](../articles/per-user-rate-limits-using-db.md). It uses a +database lookup to get the customer details and combines that with the ZoneCache +to improve performance, reduce latency and lower the load on the database. -In this example we use [supabase](https://supabase.com) as the database but you could use your own API, [Xata](https://xata.io), [Firebase](https://firebase.com) etc. The implementation will be similar for all. +In this example we use [supabase](https://supabase.com) as the database but you +could use your own API, [Xata](https://xata.io), +[Firebase](https://firebase.com) etc. The implementation will be similar for +all. -If you haven't already, check out the [rate-limiting policy](../policies/rate-limit-inbound.md) and the [dynamic rate limiting quickstart](../articles/per-user-rate-limits-using-db.md). Then you should be oriented to how dynamic rate limiting works. +If you haven't already, check out the +[rate-limiting policy](../policies/rate-limit-inbound.md) and the +[dynamic rate limiting quickstart](../articles/per-user-rate-limits-using-db.md). +Then you should be oriented to how dynamic rate limiting works. -Below is a full implementation of a custom rate limiting function. In our example this is a module called `per-user-rate-limiting.ts`. +Below is a full implementation of a custom rate limiting function. In our +example this is a module called `per-user-rate-limiting.ts`. ```ts import { @@ -28,7 +38,7 @@ const FALLBACK_REQUESTS_ALLOWED = 100; export async function rateLimitKey( request: ZuploRequest, context: ZuploContext, - policyName: string + policyName: string, ): Promise { // We'll get the customer ID from the user data. // This might be from a JWT or API Key metadata @@ -74,7 +84,8 @@ export async function rateLimitKey( } ``` -The above function can be applied to a rate limiter with the following configuration in policies +The above function can be applied to a rate limiter with the following +configuration in policies ```json { diff --git a/docs/articles/rename-or-move-project.md b/docs/articles/rename-or-move-project.md index 4d3d4771..1c8967e0 100644 --- a/docs/articles/rename-or-move-project.md +++ b/docs/articles/rename-or-move-project.md @@ -3,25 +3,35 @@ title: How to rename or move a project sidebar_label: Rename/Move Project --- -Projects cannot be moved between accounts or renamed in Zuplo but there is an easy workaround using Source Control. +Projects cannot be moved between accounts or renamed in Zuplo but there is an +easy workaround using Source Control. -If the project you want to move or rename is not already connected to source control then [follow our GitHub integration guide](/docs/articles/source-control). This will copy the contents of your project to a GitHub repo. +If the project you want to move or rename is not already connected to source +control then +[follow our GitHub integration guide](/docs/articles/source-control). This will +copy the contents of your project to a GitHub repo. -If your project is already connected to Source Control (or you just connected it above) the next step is to push any changes you want to be included in when you move to a different project. +If your project is already connected to Source Control (or you just connected it +above) the next step is to push any changes you want to be included in when you +move to a different project. -If you're confident all your code is stored safely in the repo you can now disconnect the project from Source Control. +If you're confident all your code is stored safely in the repo you can now +disconnect the project from Source Control. ![Disconnect Project](./media/disconnect-project.png) -Next create a new project in the correct account if moving accounts or with the correct name. Choose the `Advanced` option on the new project dialog. +Next create a new project in the correct account if moving accounts or with the +correct name. Choose the `Advanced` option on the new project dialog. ![Advanced New Project](./media/advanced-new-project.png) -You should see a list of Orgs and Repos - pick the source repo you wanted to move and click Create Project from Repository. +You should see a list of Orgs and Repos - pick the source repo you wanted to +move and click Create Project from Repository. Your new project will now be connected to this repo and ready to go. -NOTE - assets and data that is not 'code' and stored in the repo will not be moved. Things that will not be moved when renaming a project include: +NOTE - assets and data that is not 'code' and stored in the repo will not be +moved. Things that will not be moved when renaming a project include: - API Key consumers - Environment variable values diff --git a/docs/articles/routes-json-deprecation-for-openapi.md b/docs/articles/routes-json-deprecation-for-openapi.md index df323079..cde1c09b 100644 --- a/docs/articles/routes-json-deprecation-for-openapi.md +++ b/docs/articles/routes-json-deprecation-for-openapi.md @@ -3,32 +3,57 @@ title: Deprecating routes.json, adding support for OpenAPI sidebar_label: Moving to OpenAPI --- -Zuplo is going native in support for the [OpenAPI standard](https://www.openapis.org/). This means we will be deprecating support for our proprietary `routes.json` file format. +Zuplo is going native in support for the +[OpenAPI standard](https://www.openapis.org/). This means we will be deprecating +support for our proprietary `routes.json` file format. -The old `routes.json` file contained both the route information and policies. This will change in the new version with policies being defined in a separate `policies.json` file and the routes defined in one or more `*.oas.json` files. New projects will automatically get a `routes.oas.json` and `policies.json` file. +The old `routes.json` file contained both the route information and policies. +This will change in the new version with policies being defined in a separate +`policies.json` file and the routes defined in one or more `*.oas.json` files. +New projects will automatically get a `routes.oas.json` and `policies.json` +file. You can still use the old `routes.json` format until it is formally deprecated. -We identify new project simply by the presence of `*.oas.json` files, so if your project has both a `routes.json` file and OpenAPI files the `routes.json` will be ignored. +We identify new project simply by the presence of `*.oas.json` files, so if your +project has both a `routes.json` file and OpenAPI files the `routes.json` will +be ignored. -You can delete the `routes.json` file by deleting this file in GitHub and doing a **Pull Hard**. +You can delete the `routes.json` file by deleting this file in GitHub and doing +a **Pull Hard**. -If you need to create a new project with support for the old routing format, please contact [support](mailto:support@zuplo.com) and we can share a template with you and guidance to recreate an old-format project in GitHub. Note - we will be removing support for `routes.json` in mid-2023 when most customers are off-boarded. +If you need to create a new project with support for the old routing format, +please contact [support](mailto:support@zuplo.com) and we can share a template +with you and guidance to recreate an old-format project in GitHub. Note - we +will be removing support for `routes.json` in mid-2023 when most customers are +off-boarded. -We hope you're as excited about our support for open standards like Open API and Problem Details for APIs. As always, [join our Discord](https://discord.gg/8QbEjr2MgZ) to chat with the team about any question or concerns you have. +We hope you're as excited about our support for open standards like Open API and +Problem Details for APIs. As always, +[join our Discord](https://discord.gg/8QbEjr2MgZ) to chat with the team about +any question or concerns you have. ## Migrating from `routes.json` -It's easy to convert your project from the old `routes.json` format to use our new OpenAPI support. We have provided a CLI tool that can be invoked via [`npx`](https://www.npmjs.com/package/npx). +It's easy to convert your project from the old `routes.json` format to use our +new OpenAPI support. We have provided a CLI tool that can be invoked via +[`npx`](https://www.npmjs.com/package/npx). -Simply execute the following cmd in your root Zuplo folder (at the level of the `/config` and `/module` folders): +Simply execute the following cmd in your root Zuplo folder (at the level of the +`/config` and `/module` folders): ``` npx @zuplo/cli@latest convert ``` -This will generate a new `routes.oas.json` and `policies.json` file in your `/config` folder based on your `routes.json` file. Use git to add these to your repo and do a **Pull Hard** to sync these changes with your working copy (in [portal.zuplo.com](https://portal.zuplo.com)). Once your ready and confident everything is working, you can delete the `routes.json` file and sync via git/GitHub again. +This will generate a new `routes.oas.json` and `policies.json` file in your +`/config` folder based on your `routes.json` file. Use git to add these to your +repo and do a **Pull Hard** to sync these changes with your working copy (in +[portal.zuplo.com](https://portal.zuplo.com)). Once your ready and confident +everything is working, you can delete the `routes.json` file and sync via +git/GitHub again. You're now on the OpenAPI train 🚂 (choo choo). -For more on GitHub Source Control integration see [GitHub integration](/docs/articles/source-control). +For more on GitHub Source Control integration see +[GitHub integration](/docs/articles/source-control). diff --git a/docs/articles/runtime-behaviors.md b/docs/articles/runtime-behaviors.md index 5a8064bd..c7075d41 100644 --- a/docs/articles/runtime-behaviors.md +++ b/docs/articles/runtime-behaviors.md @@ -2,12 +2,24 @@ title: Runtime Behaviors --- -Zuplo's core gateway runtime is built on [open web-standards](./web-standard-apis.md). However, there are some cases where Zuplo either differentiates from the standard or where the standard doesn't apply due to Zuplo running server-side rather than in the browser. There are also some behaviors that are enforced in Zuplo that may be unfamiliar to developers coming from other systems. This document aims to outline behaviors that developers may encounter and suggested ways to handle these behaviors. +Zuplo's core gateway runtime is built on +[open web-standards](./web-standard-apis.md). However, there are some cases +where Zuplo either differentiates from the standard or where the standard +doesn't apply due to Zuplo running server-side rather than in the browser. There +are also some behaviors that are enforced in Zuplo that may be unfamiliar to +developers coming from other systems. This document aims to outline behaviors +that developers may encounter and suggested ways to handle these behaviors. -Because some legacy APIs may require non-standard behavior, most of these behaviors can be modified for your particular deployment. Contact support@zuplo.com to discuss options. +Because some legacy APIs may require non-standard behavior, most of these +behaviors can be modified for your particular deployment. Contact +support@zuplo.com to discuss options. ## Request.body -The [standard](https://developer.mozilla.org/en-US/docs/Web/API/Request/body) for `Request.body` specifies that on `GET` and `HEAD` requests the value must be `null`. Different APIs, networks, and gateways follow this spec to varying degrees. In some cases they allow in others they don't. +The [standard](https://developer.mozilla.org/en-US/docs/Web/API/Request/body) +for `Request.body` specifies that on `GET` and `HEAD` requests the value must be +`null`. Different APIs, networks, and gateways follow this spec to varying +degrees. In some cases they allow in others they don't. -By default, Zuplo will return a 500 error in the event that a `GET` or `HEAD` request has a body. +By default, Zuplo will return a 500 error in the event that a `GET` or `HEAD` +request has a body. diff --git a/docs/articles/runtime-extensions.md b/docs/articles/runtime-extensions.md index c19b238d..9cda747b 100644 --- a/docs/articles/runtime-extensions.md +++ b/docs/articles/runtime-extensions.md @@ -67,7 +67,7 @@ export function runtimeInit(runtime: RuntimeExtensions) { runtime.problemResponseFormat = ( { problem, statusText, additionalHeaders }, request, - context + context, ) => { // Build the response body const body = JSON.stringify(problem, null, 2); @@ -121,7 +121,7 @@ import { ZuploContext, ZuploRequest } from "@zuplo/runtime"; export async function pluginWithHook( request: ZuploRequest, context: ZuploContext, - policyName: string + policyName: string, ) { const cloned = request.clone(); context.addResponseSendingFinalHook( @@ -131,7 +131,7 @@ export async function pluginWithHook( method: "GET", body, }); - } + }, ); return request; @@ -155,7 +155,7 @@ export function runtimeInit(runtime: RuntimeExtensions) { async (request, context, event: AwsLambdaEventV1) => { event.path = context.custom.lambdaPath ?? event.path; return event; - } + }, ); } ``` diff --git a/docs/articles/secure-tunnel.md b/docs/articles/secure-tunnel.md index e4abafb3..694f05d1 100644 --- a/docs/articles/secure-tunnel.md +++ b/docs/articles/secure-tunnel.md @@ -2,38 +2,85 @@ title: Secure Tunnel --- -For customers running on bare metal, on-premises, or other non-cloud providers tunnels provides a way to secure your backend without mTLS or IAM. +For customers running on bare metal, on-premises, or other non-cloud providers +tunnels provides a way to secure your backend without mTLS or IAM. -The way this system works is by deploying a small service inside your network or VPC that makes a secure outbound connection to Zuplo's infrastructure. Your Zuplo API Gateway can then use this tunnel to securely route traffic to your private API. The benefits of a secure tunnel are: +The way this system works is by deploying a small service inside your network or +VPC that makes a secure outbound connection to Zuplo's infrastructure. Your +Zuplo API Gateway can then use this tunnel to securely route traffic to your +private API. The benefits of a secure tunnel are: -1. Because the tunnel makes an outbound connection, there is no need for your API to be exposed on the internet at all. +1. Because the tunnel makes an outbound connection, there is no need for your + API to be exposed on the internet at all. 2. All traffic between Zuplo and your API is fully encrypted. -3. You eliminate the need to configure complex ingress, firewall, or other types of policies to route traffic into your API. Simply install the tunnel and Zuplo takes care of the rest. +3. You eliminate the need to configure complex ingress, firewall, or other types + of policies to route traffic into your API. Simply install the tunnel and + Zuplo takes care of the rest. ## How does the Tunnel Work? -The Zuplo tunnel can run on virtually any infrastructure. The most common way users install the tunnel is as a Docker container, but we can provide you a build in virtually any format (i.e. an Azure VM, etc.). The tunnel itself is a lightweight service that when started makes an outbound connection to the Zuplo network and then through to your Zuplo Gateway. +The Zuplo tunnel can run on virtually any infrastructure. The most common way +users install the tunnel is as a Docker container, but we can provide you a +build in virtually any format (i.e. an Azure VM, etc.). The tunnel itself is a +lightweight service that when started makes an outbound connection to the Zuplo +network and then through to your Zuplo Gateway. -When the tunnel service connects to the Zuplo network traffic from your gateway can be routed to internal services running in your network or VPC. For example, if your API is running on the internal DNS address `external-api.local`, the tunnel will route traffic from the Zuplo API Gateway to your internal service based only on the code and policies you have set up in your Zuplo Gateway. +When the tunnel service connects to the Zuplo network traffic from your gateway +can be routed to internal services running in your network or VPC. For example, +if your API is running on the internal DNS address `external-api.local`, the +tunnel will route traffic from the Zuplo API Gateway to your internal service +based only on the code and policies you have set up in your Zuplo Gateway. -The example below illustrates how the Zuplo tunnel would be configured in an AWS ECS Cluster. Notice that there is no public IP address or ingress traffic in this configuration. This is a completely private VPC. The tunnel makes an outbound connection to the Zuplo Gateway and then uses internal DNS to route requests to the Private API. +The example below illustrates how the Zuplo tunnel would be configured in an AWS +ECS Cluster. Notice that there is no public IP address or ingress traffic in +this configuration. This is a completely private VPC. The tunnel makes an +outbound connection to the Zuplo Gateway and then uses internal DNS to route +requests to the Private API. ![](https://cdn.zuplo.com/assets/fefdc7fb-f3b6-4908-8485-3d20cb769cfd.png) ## Is this Secure? -Zuplo builds on top of many different tools to ensure that your gateway and API stay secure. Each tunnel uses a secret key that allows it to securely connect to Zuplo's network. Under the hood, Zuplo relies on Cloudflare's network for establishing secure and reliable tunnel connections. Each tunnel is configured with unique access policies that allow only the Zuplo Gateway that you have authorized to make connections over that tunnel. Every incoming request is terminated using Cloudflare's network which provides sophisticated DDoS, bot, and threat protections. Next, the request is routed through your Zuplo Gateway which can be configured with all policies and code you require to control access to your API. By default, no requests will be routed from your gateway to your API until you configure routes, URL rewrites, and policies in your Gateway. - -All traffic is terminated at the edge with SSL certificates and encrypted through the entire route to your API. All requests that your gateway makes to Zuplo's internal services like API Key Management or Rate Limiting are also transported over secure and encrypted tunnels. - -Every request is logged and you can configure Zuplo's logs to push to the log service of your choice. +Zuplo builds on top of many different tools to ensure that your gateway and API +stay secure. Each tunnel uses a secret key that allows it to securely connect to +Zuplo's network. Under the hood, Zuplo relies on Cloudflare's network for +establishing secure and reliable tunnel connections. Each tunnel is configured +with unique access policies that allow only the Zuplo Gateway that you have +authorized to make connections over that tunnel. Every incoming request is +terminated using Cloudflare's network which provides sophisticated DDoS, bot, +and threat protections. Next, the request is routed through your Zuplo Gateway +which can be configured with all policies and code you require to control access +to your API. By default, no requests will be routed from your gateway to your +API until you configure routes, URL rewrites, and policies in your Gateway. + +All traffic is terminated at the edge with SSL certificates and encrypted +through the entire route to your API. All requests that your gateway makes to +Zuplo's internal services like API Key Management or Rate Limiting are also +transported over secure and encrypted tunnels. + +Every request is logged and you can configure Zuplo's logs to push to the log +service of your choice. ## How will tunnels perform? -Most customers are fine running two instances of the tunnel service for redundancy in the event one pod/service fails. Each tunnel is able to handle millions of requests per minute. For customers that require additional scale, simply increase the number of tunnel instances you are running or configure auto-scaling on your deployment. It is unlikely that the tunnel will become the bottleneck in your traffic before other factors, but if you do run into any issues [contact support](mailto:support@zuplo.com) and we will work out a solution that meets your scale requirements. +Most customers are fine running two instances of the tunnel service for +redundancy in the event one pod/service fails. Each tunnel is able to handle +millions of requests per minute. For customers that require additional scale, +simply increase the number of tunnel instances you are running or configure +auto-scaling on your deployment. It is unlikely that the tunnel will become the +bottleneck in your traffic before other factors, but if you do run into any +issues [contact support](mailto:support@zuplo.com) and we will work out a +solution that meets your scale requirements. ## How should I Configure the Tunnel? -You should run your tunnel with an IAM role or other network policies that only allow the tunnel to make requests to the network services that you want your Gateway to access. This can be done in a variety of ways depending on your setup. IAM roles and internal service meshes are common means of controlling which services the tunnel can access. For example, at Zuplo we use [Linkerd](https://linkerd.io/) in our Kubernetes clusters as one mechanism for controlling what services are accessible to our tunnel deployment. +You should run your tunnel with an IAM role or other network policies that only +allow the tunnel to make requests to the network services that you want your +Gateway to access. This can be done in a variety of ways depending on your +setup. IAM roles and internal service meshes are common means of controlling +which services the tunnel can access. For example, at Zuplo we use +[Linkerd](https://linkerd.io/) in our Kubernetes clusters as one mechanism for +controlling what services are accessible to our tunnel deployment. -For details on how to configure a tunnel on your network see [the setup guide](tunnel-setup.md). +For details on how to configure a tunnel on your network see +[the setup guide](tunnel-setup.md). diff --git a/docs/articles/securing-your-backend.md b/docs/articles/securing-your-backend.md index a1c711b3..f7a9794e 100644 --- a/docs/articles/securing-your-backend.md +++ b/docs/articles/securing-your-backend.md @@ -2,34 +2,66 @@ title: Securing your backend --- -When using a gateway, it's important to ensure that your backend API is only receiving traffic via the gateway to be confident that your policies are being correctly applied to all traffic. +When using a gateway, it's important to ensure that your backend API is only +receiving traffic via the gateway to be confident that your policies are being +correctly applied to all traffic. ![](https://cdn.zuplo.com/assets/b7290dd1-43fa-49f8-8629-6b4899e2e9f3.png) -To do this, we need to secure the communication between Zuplo and your backend APIs (origin). There are several options to do this securely. +To do this, we need to secure the communication between Zuplo and your backend +APIs (origin). There are several options to do this securely. ## 1/ Shared secret / API Key -This is the most popular option and is used by companies like Supabase, Firebase, and Stripe to secure their own APIs. In this solution the backend requires a secret that is known only by the gateway. This is usually an opaque key sent as a header on every request to the origin. Zuplo adds this to the request - the client is never aware of the secret. An example of how to set this up, including using [Environment Variables](./environment-variables.md) to store the secret is included in [Step 1 - Setup a Basic Gateway](./step-1-setup-basic-gateway.md). +This is the most popular option and is used by companies like Supabase, +Firebase, and Stripe to secure their own APIs. In this solution the backend +requires a secret that is known only by the gateway. This is usually an opaque +key sent as a header on every request to the origin. Zuplo adds this to the +request - the client is never aware of the secret. An example of how to set this +up, including using [Environment Variables](./environment-variables.md) to store +the secret is included in +[Step 1 - Setup a Basic Gateway](./step-1-setup-basic-gateway.md). ## 2/ Federated Authentication -This is a new option where you can configure your cloud service (e.g. GCP or AWS) to trust a JWT token created by the Zuplo runtime. If you're interested in using this option please contact us at `support@zuplo.com`. +This is a new option where you can configure your cloud service (e.g. GCP or +AWS) to trust a JWT token created by the Zuplo runtime. If you're interested in +using this option please contact us at `support@zuplo.com`. ## 3/ Upstream Service Authentication -Utilize the IAM controls provided by your Cloud host to secure inbound requests and allow only authorized service principals access to your service. +Utilize the IAM controls provided by your Cloud host to secure inbound requests +and allow only authorized service principals access to your service. -- For Azure users, you can user our [Upstream Azure AD Service Auth](../policies/upstream-azure-ad-service-auth-inbound.md) policy. This uses Azure AD App registrations to create a token that Zuplo will send to requests to Azure. +- For Azure users, you can user our + [Upstream Azure AD Service Auth](../policies/upstream-azure-ad-service-auth-inbound.md) + policy. This uses Azure AD App registrations to create a token that Zuplo will + send to requests to Azure. -- For GCP users, you can use our [Upstream GCP Service AUth](../policies/upstream-gcp-service-auth-inbound.md) or [Upstream GCP JWT](../policies/upstream-gcp-jwt-inbound.md) policies. These use a service.json credential to create or issue JWT tokens that Zuplo will send to requests to GCP. +- For GCP users, you can use our + [Upstream GCP Service AUth](../policies/upstream-gcp-service-auth-inbound.md) + or [Upstream GCP JWT](../policies/upstream-gcp-jwt-inbound.md) policies. These + use a service.json credential to create or issue JWT tokens that Zuplo will + send to requests to GCP. ## 4/ mTLS Authentication -Mutual Certificate authentication allows the configuration of a trust relationship between your Zuplo gateway and your backend API using certificates. If you're interested in using this option please contact us at `support@zuplo.com`. +Mutual Certificate authentication allows the configuration of a trust +relationship between your Zuplo gateway and your backend API using certificates. +If you're interested in using this option please contact us at +`support@zuplo.com`. ## 5/ Secure Tunneling -Used by some of our larger customers, our [secure tunnels](./secure-tunnel.md) allow you to create a WireGuard based tunnel from your VPC or private data-center that connects directly to your Zuplo gateway. This option is generally useful when running workloads in a non-cloud provider (i.e. bare metal, on premises, etc.) that do not have IAM or mTLS capabilities. In this solution, your backend API does not need to be exposed to the internet at all. This is a more complex setup and is only available on our [enterprise plan](https://zuplo.com/pricing). - -To discuss security and connectivity options, our [discord channel](https://discord.gg/8QbEjr2MgZ) is a great community, with active participation from the Zuplo team. +Used by some of our larger customers, our [secure tunnels](./secure-tunnel.md) +allow you to create a WireGuard based tunnel from your VPC or private +data-center that connects directly to your Zuplo gateway. This option is +generally useful when running workloads in a non-cloud provider (i.e. bare +metal, on premises, etc.) that do not have IAM or mTLS capabilities. In this +solution, your backend API does not need to be exposed to the internet at all. +This is a more complex setup and is only available on our +[enterprise plan](https://zuplo.com/pricing). + +To discuss security and connectivity options, our +[discord channel](https://discord.gg/8QbEjr2MgZ) is a great community, with +active participation from the Zuplo team. diff --git a/docs/articles/step-3-add-rate-limiting.md b/docs/articles/step-3-add-rate-limiting.md index 806e0515..39d96630 100644 --- a/docs/articles/step-3-add-rate-limiting.md +++ b/docs/articles/step-3-add-rate-limiting.md @@ -2,17 +2,24 @@ title: Step 3 - Rate Limiting --- -In this guide we'll add Rate Limiting to a route. You can do this for any Zuplo project but will need a route, consider completing [step 1](./step-1-setup-basic-gateway.md) first. +In this guide we'll add Rate Limiting to a route. You can do this for any Zuplo +project but will need a route, consider completing +[step 1](./step-1-setup-basic-gateway.md) first. -Rate Limiting is one of our most popular policies - you should never ship an API without rate limiting because your customers or internal developers **will** accidentally DDoS your API. Usually with a rogue `useEffect` call in React code. +Rate Limiting is one of our most popular policies - you should never ship an API +without rate limiting because your customers or internal developers **will** +accidentally DDoS your API. Usually with a rogue `useEffect` call in React code. -Zuplo offers a programmable approach to rate limiting that allows you to vary how rate limiting is applied for each customer, or request. +Zuplo offers a programmable approach to rate limiting that allows you to vary +how rate limiting is applied for each customer, or request. -Implementing truly distributed, high performance Rate Limiting is difficult, our promise is that using Zuplo is cheaper and faster than doing this yourself. +Implementing truly distributed, high performance Rate Limiting is difficult, our +promise is that using Zuplo is cheaper and faster than doing this yourself. ## 1/ Add a rate-limiting policy -Navigate to your route in the **Route Designer** and click **Add Policy** on the request pipeline. +Navigate to your route in the **Route Designer** and click **Add Policy** on the +request pipeline. ![Add policy](https://cdn.zuplo.com/assets/d2ba60f0-c8ea-4795-b219-ad26835da3d8.png) @@ -20,7 +27,9 @@ Search for the rate limiting policy ![Add rate-limiting policy](https://cdn.zuplo.com/assets/97c17fe4-a11f-45c3-815a-0f6620bc995c.png) -Assuming you followed [step 2](./step-2-add-api-key-auth.md) and setup API Key Authentication, set the policy to `rateLimitBy` `user` and allow 1 request every 1 minute. +Assuming you followed [step 2](./step-2-add-api-key-auth.md) and setup API Key +Authentication, set the policy to `rateLimitBy` `user` and allow 1 request every +1 minute. ``` { @@ -34,19 +43,24 @@ Assuming you followed [step 2](./step-2-add-api-key-auth.md) and setup API Key A } ``` -Now each consumer will get a separate bucket for rate limiting. You can test this using the API key you created in [step 2](./step-2-add-api-key-auth.md). +Now each consumer will get a separate bucket for rate limiting. You can test +this using the API key you created in [step 2](./step-2-add-api-key-auth.md). You should receive a **429 Too many requests** after 1 request. -Try creating another API Key Consumer (you can be the manager of multiple keys) and switching keys. +Try creating another API Key Consumer (you can be the manager of multiple keys) +and switching keys. ## 2/ Try dynamic rate-limiting -This time, we will make the rate-limiting policy more dynamic, based on properties of the customer. Update the metadata of your two API Key consumers to have a property `customerType`. Set one to `free` and another to `premium`. +This time, we will make the rate-limiting policy more dynamic, based on +properties of the customer. Update the metadata of your two API Key consumers to +have a property `customerType`. Set one to `free` and another to `premium`. ![Customer Metadata](https://cdn.zuplo.com/assets/259b5845-cbe4-47f8-986a-a9a469c30be6.png) -Now add a new module to the files section by clicking on the `+` next to the **Modules** folder and choose new empty module. +Now add a new module to the files section by clicking on the `+` next to the +**Modules** folder and choose new empty module. ![New module](https://cdn.zuplo.com/assets/1f6b403a-67b9-43ac-8fb4-e2b813376911.png) @@ -85,7 +99,8 @@ export function rateLimit(request: ZuploRequest, context: ZuploContext) { } ``` -Now we'll reconfigure the rate-limit policy to wire up our custom function. Find the policy in the **Route Designer** and click edit. +Now we'll reconfigure the rate-limit policy to wire up our custom function. Find +the policy in the **Route Designer** and click edit. ![Edit Policy](https://cdn.zuplo.com/assets/acfa7e19-1b6c-4633-ad5c-56af4734f717.png) @@ -107,6 +122,7 @@ Update the configuration } ``` -This identifies our `rate-limit` module and the function `rateLimit` that it exports. +This identifies our `rate-limit` module and the function `rateLimit` that it +exports. **NEXT** Try [step 4 - deploying to the edge](./step-4-deploying-to-the-edge). diff --git a/docs/articles/web-crypto-apis.md b/docs/articles/web-crypto-apis.md index 7d5646c3..41175589 100644 --- a/docs/articles/web-crypto-apis.md +++ b/docs/articles/web-crypto-apis.md @@ -34,7 +34,7 @@ const myDigest = await crypto.subtle.digest( { name: "SHA-256", }, - myText // The data you want to hash as an ArrayBuffer + myText, // The data you want to hash as an ArrayBuffer ); console.log(new Uint8Array(myDigest)); @@ -89,7 +89,7 @@ async function sign(value: string, secret: string) { secretKeyData, { name: "HMAC", hash: "SHA-256" }, false, - ["sign"] + ["sign"], ); const mac = await crypto.subtle.sign("HMAC", key, encoder.encode(value)); @@ -136,7 +136,7 @@ async function verify(signature: string, value: string, secret: string) { secretKeyData, { name: "HMAC", hash: "SHA-256" }, false, - ["verify"] + ["verify"], ); // The received MAC is Base64-encoded, so you have to go to some trouble to @@ -152,7 +152,7 @@ async function verify(signature: string, value: string, secret: string) { "HMAC", key, receivedMac, - encoder.encode(value) + encoder.encode(value), ); return verified; diff --git a/docs/articles/what-is-zuplo.md b/docs/articles/what-is-zuplo.md index 2db295f4..eb9ff322 100644 --- a/docs/articles/what-is-zuplo.md +++ b/docs/articles/what-is-zuplo.md @@ -2,15 +2,24 @@ title: What is Zuplo? --- -Zuplo is a light-weight, fully-managed API Management platform, built for developers (GitOps, fast deployment, unlimited preview environments etc.). We're working with engineering leaders to quickly add: +Zuplo is a light-weight, fully-managed API Management platform, built for +developers (GitOps, fast deployment, unlimited preview environments etc.). We're +working with engineering leaders to quickly add: -* rate limiting -* auth and access management, -* programmable layer of abstraction between an API and its consumers (you can write code that executes inside the Gateway) -* full policy library +- rate limiting +- auth and access management, +- programmable layer of abstraction between an API and its consumers (you can + write code that executes inside the Gateway) +- full policy library -Zuplo doesn't come with the crazy costs, training courses, etc. of a traditional API Management platforms, like Apigee or Azure APIM. +Zuplo doesn't come with the crazy costs, training courses, etc. of a traditional +API Management platforms, like Apigee or Azure APIM. -Zuplo embraces shift-left and infrastructure as code - the gateway definition is all stored in text files that can be source controlled. We can deploy a new environment, to the edge, in under 20s with a simple git commit. +Zuplo embraces shift-left and infrastructure as code - the gateway definition is +all stored in text files that can be source controlled. We can deploy a new +environment, to the edge, in under 20s with a simple git commit. -We're doing over 1.5 billion API requests per month, supporting API-first engineering teams of 5-50. Zuplo was created by the founder of Azure API Management and is backed by the founders of some of the most exciting developer-centric solutions like Auth0, Snyk, and BigID. +We're doing over 1.5 billion API requests per month, supporting API-first +engineering teams of 5-50. Zuplo was created by the founder of Azure API +Management and is backed by the founders of some of the most exciting +developer-centric solutions like Auth0, Snyk, and BigID. diff --git a/docs/articles/who-uses-and-why.md b/docs/articles/who-uses-and-why.md index fd87c534..74048f2a 100644 --- a/docs/articles/who-uses-and-why.md +++ b/docs/articles/who-uses-and-why.md @@ -2,49 +2,94 @@ title: Who uses Zuplo, and why? --- -We have customers ranging in size from 1-person startups to mature, large enterprises with many 1000s of employees. All enjoy Zuplo's developer experience, gitops workflow, support for near unlimited deployments and ease of extensibility. They also enjoy the built-in analytics and abstraction a gateway provides as a clean layer of separation in their architecture. +We have customers ranging in size from 1-person startups to mature, large +enterprises with many 1000s of employees. All enjoy Zuplo's developer +experience, gitops workflow, support for near unlimited deployments and ease of +extensibility. They also enjoy the built-in analytics and abstraction a gateway +provides as a clean layer of separation in their architecture. There are a number of reasons people decide to use Zuplo: ### Enterprise Customers -We have many enterprise customers using Zuplo to manage multiple APIs, some examples include +We have many enterprise customers using Zuplo to manage multiple APIs, some +examples include -* [BlockDaemon](https://blockdaemon.com) - the leading infrastructure provider for blockchain with an incredible customer list including Goldman Sachs, Microsoft, Plaid and many more. -* [Imburse Payments](https://imbursepayments.com) part of [Duck Creek Technologies](https://www.duckcreek.com/) - a leading payments platform for Insurance companies. See our 🎥 video interview with the Imburse team here: [▶️ Why Imburse Payments chose Zuplo as their API Gateway](https://youtu.be/z94pRJE2zfs) +- [BlockDaemon](https://blockdaemon.com) - the leading infrastructure provider + for blockchain with an incredible customer list including Goldman Sachs, + Microsoft, Plaid and many more. +- [Imburse Payments](https://imbursepayments.com) part of + [Duck Creek Technologies](https://www.duckcreek.com/) - a leading payments + platform for Insurance companies. See our 🎥 video interview with the Imburse + team here: + [▶️ Why Imburse Payments chose Zuplo as their API Gateway](https://youtu.be/z94pRJE2zfs) ### Startup Customers -We also have many smaller businesses using Zuplo, launching a delightful Stripe-quality experience in record time. Examples include: +We also have many smaller businesses using Zuplo, launching a delightful +Stripe-quality experience in record time. Examples include: -* [Rewiring America](https://www.rewiringamerica.org/) - the leading electrification nonprofit, focused on electrifying our homes, businesses, and communities. We develop accessible, actionable data and tools, and build coalitions and partnerships to make going electric easier for households and communities. See our 🎥 video interview with the Rewiring America team here: [▶️ Tom Carden of Rewiring America on accelerating an API Program with Zuplo](https://youtu.be/wUKLrNIRC_8) +- [Rewiring America](https://www.rewiringamerica.org/) - the leading + electrification nonprofit, focused on electrifying our homes, businesses, and + communities. We develop accessible, actionable data and tools, and build + coalitions and partnerships to make going electric easier for households and + communities. See our 🎥 video interview with the Rewiring America team here: + [▶️ Tom Carden of Rewiring America on accelerating an API Program with Zuplo](https://youtu.be/wUKLrNIRC_8) -* [iTicket](https://iticket.co.nz) - New Zealand's largest ticketing provider, leading a revolution against customers being held to ransom when buying event tickets. See our 🎥 video interview with the iTicket team here: [▶️ Azure API Management alternative - why iTicket chose Zuplo instead](https://youtu.be/ZWS4x4pwyuo) +- [iTicket](https://iticket.co.nz) - New Zealand's largest ticketing provider, + leading a revolution against customers being held to ransom when buying event + tickets. See our 🎥 video interview with the iTicket team here: + [▶️ Azure API Management alternative - why iTicket chose Zuplo instead](https://youtu.be/ZWS4x4pwyuo) -* [Common Paper](https://commonpaper.com) - YC-backed startup looking to make contracts as clear and programmable as APIs, built on open-source standards. See our 🎥 video interview with the iTicket team here: [▶️ How Common Paper shipped their API *fast*](https://youtu.be/1rAxJFVXU84) +- [Common Paper](https://commonpaper.com) - YC-backed startup looking to make + contracts as clear and programmable as APIs, built on open-source standards. + See our 🎥 video interview with the iTicket team here: + [▶️ How Common Paper shipped their API _fast_](https://youtu.be/1rAxJFVXU84) ## Launching a new API -Most of our startup customers are looking to launch an API and want to achieve a 'Stripe-quality' API experience for their customers quickly. +Most of our startup customers are looking to launch an API and want to achieve a +'Stripe-quality' API experience for their customers quickly. -They also want to save time, money and engineering effort and avoid building out common features like API key authentication, rate-limiting, quotas, request validation and developer documentation. +They also want to save time, money and engineering effort and avoid building out +common features like API key authentication, rate-limiting, quotas, request +validation and developer documentation. -API Key authentication is non-trivial to implement and requires writing security critical code to safely store keys and help customer's self-serve. +API Key authentication is non-trivial to implement and requires writing security +critical code to safely store keys and help customer's self-serve. -Our promise is that Zuplo should be cheaper and faster - than building these features yourself, while future-proofing your architecture. +Our promise is that Zuplo should be cheaper and faster - than building these +features yourself, while future-proofing your architecture. ## Managing multiple APIs -Most of our larger customers are managing multiple APIs behind Zuplo, including multiple internal APIs. In some cases combining multiple backends into a single API but also using Zuplo as a way to ensure that all APIs are using consistent Authentication, Rate-Limiting and other common policies described by their internal best practices. +Most of our larger customers are managing multiple APIs behind Zuplo, including +multiple internal APIs. In some cases combining multiple backends into a single +API but also using Zuplo as a way to ensure that all APIs are using consistent +Authentication, Rate-Limiting and other common policies described by their +internal best practices. -Development teams also prefer Zuplo over other API Management products because it allows for a more collaborative workflow within the team. Each team can create as many deployments as necessary for exploration and testing, while architects and leadership can maintain control over protected branches (QA, staging, production) with a clean CI/CD integrated workflow. +Development teams also prefer Zuplo over other API Management products because +it allows for a more collaborative workflow within the team. Each team can +create as many deployments as necessary for exploration and testing, while +architects and leadership can maintain control over protected branches (QA, +staging, production) with a clean CI/CD integrated workflow. ## Microservices Gateway -Many customers are looking to Zuplo to augment or replace their traditional microservices gateway as an easier to use, fully-managed solution with built-in serverless auto-scale. Zuplo might be used to create a single API, with a programmable orchestration layer hosted on Zuplo. +Many customers are looking to Zuplo to augment or replace their traditional +microservices gateway as an easier to use, fully-managed solution with built-in +serverless auto-scale. Zuplo might be used to create a single API, with a +programmable orchestration layer hosted on Zuplo. -Some of our customers use Zuplo as a smart-routing layer, with each request being routed to different, isolated backends based on user credentials or other metadata. This is covered in this [video tutorial](https://www.youtube.com/watch?v=SC-HuZqEEPE). +Some of our customers use Zuplo as a smart-routing layer, with each request +being routed to different, isolated backends based on user credentials or other +metadata. This is covered in this +[video tutorial](https://www.youtube.com/watch?v=SC-HuZqEEPE). ## API over BaaS -Some of our smaller customers are choosing to use Backed-as-a-Service (BaaS) solutions like supabase, firebase etc and want to use Zuplo as programmable API-first presentation of their backend. More on this scenario in this [video guide](https://www.youtube.com/watch?v=GJSkbxMnWxE). +Some of our smaller customers are choosing to use Backed-as-a-Service (BaaS) +solutions like supabase, firebase etc and want to use Zuplo as programmable +API-first presentation of their backend. More on this scenario in this +[video guide](https://www.youtube.com/watch?v=GJSkbxMnWxE). diff --git a/docs/articles/zone-cache.md b/docs/articles/zone-cache.md index bc0ddca8..f850ff8f 100644 --- a/docs/articles/zone-cache.md +++ b/docs/articles/zone-cache.md @@ -3,9 +3,15 @@ title: ZoneCache sidebar_label: ZoneCache --- -The ZoneCache is used to store data in a shared cache, typically local to the Zone your gateway is running (for example, the same data center). This can be used to store small, simple objects. It's excellent for improving the latency of your gateway if you need to access remote data in your policies, such as calling another API in a policy. +The ZoneCache is used to store data in a shared cache, typically local to the +Zone your gateway is running (for example, the same data center). This can be +used to store small, simple objects. It's excellent for improving the latency of +your gateway if you need to access remote data in your policies, such as calling +another API in a policy. -There's an demonstration of ZoneCache use in the [Per User Rate Limits Using a Database](/docs/articles/per-user-rate-limits-using-db) example. +There's an demonstration of ZoneCache use in the +[Per User Rate Limits Using a Database](/docs/articles/per-user-rate-limits-using-db) +example. ## Constructing the Cache @@ -29,7 +35,9 @@ const data = await cache.get("key"); await cache.put("key", data, 60); ``` -When writing to the cache the `data` parameters will be JSON serialized. If your data does not serialize cleanly to JSON (like the `Headers` object does not) you won't be able to read your data back/ +When writing to the cache the `data` parameters will be JSON serialized. If your +data does not serialize cleanly to JSON (like the `Headers` object does not) you +won't be able to read your data back/ ## Deleting from the Cache @@ -37,7 +45,10 @@ When writing to the cache the `data` parameters will be JSON serialized. If your await cache.delete("key"); ``` -**TIP** On some code paths you may not want to `await` the cache to wait for the operation to complete. For example, when writing data you may choose to write asynchronously. However, we recommend catching errors if you do this, for example: +**TIP** On some code paths you may not want to `await` the cache to wait for the +operation to complete. For example, when writing data you may choose to write +asynchronously. However, we recommend catching errors if you do this, for +example: ```ts cache.put("key", data, 60).catch((err) => context.log.error(err)); diff --git a/docs/articles/zp-body-removed.md b/docs/articles/zp-body-removed.md index bebe5f7e..3fde7bcc 100644 --- a/docs/articles/zp-body-removed.md +++ b/docs/articles/zp-body-removed.md @@ -3,11 +3,17 @@ title: zp-body-removed sidebar_label: zp-body-removed --- -Zuplo does not support GET or HEAD requests with bodies. This is because the product is based on web standards and our stack makes heavy use of [fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) which explicitly does not support GET, HEAD requests with a body. +Zuplo does not support GET or HEAD requests with bodies. This is because the +product is based on web standards and our stack makes heavy use of +[fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) which +explicitly does not support GET, HEAD requests with a body. -For this reason, any body of a GET/HEAD request is stripped on entry into Zuplo infrastructure and a header `zp-body-removed` is added to the request. +For this reason, any body of a GET/HEAD request is stripped on entry into Zuplo +infrastructure and a header `zp-body-removed` is added to the request. -This allows your origin/backend server to know that a body was removed. If you want to enforce this and reject such requests it is easy to write a custom policy that looks for a `zp-body-removed` header and return a response, e.g. +This allows your origin/backend server to know that a body was removed. If you +want to enforce this and reject such requests it is easy to write a custom +policy that looks for a `zp-body-removed` header and return a response, e.g. ```ts import { HttpProblems, ZuploContext, ZuploRequest } from "@zuplo/runtime"; diff --git a/docs/articles/zuplo-context.md b/docs/articles/zuplo-context.md index f07a5e0a..d434bdcd 100644 --- a/docs/articles/zuplo-context.md +++ b/docs/articles/zuplo-context.md @@ -117,7 +117,7 @@ const result = await context.invokeInboundPolicy("my-policy", request); if (result instanceof Response) { // if you want to do something special if type is Response, maybe log for example context.log.warn( - `My policy wanted to short circuit with a status code of '${result.status}'` + `My policy wanted to short circuit with a status code of '${result.status}'`, ); } diff --git a/docs/articles/zuplo-json.md b/docs/articles/zuplo-json.md index fbef0399..b5a20b68 100644 --- a/docs/articles/zuplo-json.md +++ b/docs/articles/zuplo-json.md @@ -2,9 +2,12 @@ title: Zuplo Project Config (zuplo.json) --- -Certain advanced project-level settings can be configured using the `zuplo.jsonc` file at the root of a project. The `zuplo.json` file is created by default for new projects and contains the default configuration. +Certain advanced project-level settings can be configured using the +`zuplo.jsonc` file at the root of a project. The `zuplo.json` file is created by +default for new projects and contains the default configuration. -The default `zuplo.jsonc` file is shown below. The only current valid `version` of the file is `1`. +The default `zuplo.jsonc` file is shown below. The only current valid `version` +of the file is `1`. ```jsonc { @@ -16,14 +19,29 @@ The default `zuplo.jsonc` file is shown below. The only current valid `version` :::warning -The `zuplo.jsonc` file is not currently shown or editable in the Zuplo portal. Connect your project to source control and edit inside your source control provider or by pushing a local change with git. If your project does not have a `zuplo.jsonc` it can be added using source control +The `zuplo.jsonc` file is not currently shown or editable in the Zuplo portal. +Connect your project to source control and edit inside your source control +provider or by pushing a local change with git. If your project does not have a +`zuplo.jsonc` it can be added using source control ::: ## Compatibility Date -Zuplo is constantly shipping updates to the underlying runtime of projects. Occasionally, these updates are not backwards compatible. Additionally, Zuplo deploys portions of projects to Cloudflare Workers who also occasionally make non-backward compatible changes. In order to ensure that your project continues to build, deploy and operate as you expect it you can set a compatibility date to lock in the behavior and APIs of the runtime. - -Current valid settings for the compatibility date are to not have it set or to set it to `2023-03-14`. When not set, the Zuplo runtime does not set a compatibility date on Cloudflare Workers. When the value is set to `2023-03-14`, the value is also set on the Cloudflare Worker which enables all changes up to March 14, 2023. See [Cloudflare's documentation](https://developers.cloudflare.com/workers/platform/compatibility-dates/) for a list of changes. - -Future non-backward compatible changes to the Zuplo runtime will be documented here as well as in the [changelog](https://zuplo.com/changelog). +Zuplo is constantly shipping updates to the underlying runtime of projects. +Occasionally, these updates are not backwards compatible. Additionally, Zuplo +deploys portions of projects to Cloudflare Workers who also occasionally make +non-backward compatible changes. In order to ensure that your project continues +to build, deploy and operate as you expect it you can set a compatibility date +to lock in the behavior and APIs of the runtime. + +Current valid settings for the compatibility date are to not have it set or to +set it to `2023-03-14`. When not set, the Zuplo runtime does not set a +compatibility date on Cloudflare Workers. When the value is set to `2023-03-14`, +the value is also set on the Cloudflare Worker which enables all changes up to +March 14, 2023. See +[Cloudflare's documentation](https://developers.cloudflare.com/workers/platform/compatibility-dates/) +for a list of changes. + +Future non-backward compatible changes to the Zuplo runtime will be documented +here as well as in the [changelog](https://zuplo.com/changelog). diff --git a/docs/errors/get-head-body-error.md b/docs/errors/get-head-body-error.md index 5aa97879..3ae166dc 100644 --- a/docs/errors/get-head-body-error.md +++ b/docs/errors/get-head-body-error.md @@ -2,5 +2,5 @@ title: GET/HEAD Body Error (GET_HEAD_BODY_ERROR) --- -By definition, a GET/HEAD request cannot have a body. -See: https://fetch.spec.whatwg.org/ +By definition, a GET/HEAD request cannot have a body. See: +https://fetch.spec.whatwg.org/ diff --git a/docs/errors/invalid-settings-dev-portal-auth-audience.md b/docs/errors/invalid-settings-dev-portal-auth-audience.md index 8f978778..78207e65 100644 --- a/docs/errors/invalid-settings-dev-portal-auth-audience.md +++ b/docs/errors/invalid-settings-dev-portal-auth-audience.md @@ -1,10 +1,14 @@ --- -title: Invalid Settings - Dev Portal Auth Audience (INVALID_SETTINGS_DEV_PORTAL_AUTH_AUDIENCE) +title: + Invalid Settings - Dev Portal Auth Audience + (INVALID_SETTINGS_DEV_PORTAL_AUTH_AUDIENCE) --- -We were unable to generate documentation because your 'devPortalClient' does not contain a valid auth configuration. +We were unable to generate documentation because your 'devPortalClient' does not +contain a valid auth configuration. -Ensure that the configuration for `authentication.devPortalClient` is set correctly in your `dev-portal.json` file. See the example below. +Ensure that the configuration for `authentication.devPortalClient` is set +correctly in your `dev-portal.json` file. See the example below. ```json { diff --git a/docs/errors/invalid-settings-dev-portal-auth.md b/docs/errors/invalid-settings-dev-portal-auth.md index 87760277..12c1fe69 100644 --- a/docs/errors/invalid-settings-dev-portal-auth.md +++ b/docs/errors/invalid-settings-dev-portal-auth.md @@ -2,9 +2,11 @@ title: Invalid Settings - Dev Portal Auth (INVALID_SETTINGS_DEV_PORTAL_AUTH) --- -We were unable to generate documentation because your 'devPortalClient' does not contain a valid auth configuration for 'audience'. +We were unable to generate documentation because your 'devPortalClient' does not +contain a valid auth configuration for 'audience'. -Ensure that the property `authentication.devPortalClient.audience` is set correctly in your `dev-portal.json` file. See the example below. +Ensure that the property `authentication.devPortalClient.audience` is set +correctly in your `dev-portal.json` file. See the example below. ```json { diff --git a/docs/errors/no-project-set.md b/docs/errors/no-project-set.md index 3f63c8a0..05a5f5d2 100644 --- a/docs/errors/no-project-set.md +++ b/docs/errors/no-project-set.md @@ -2,4 +2,5 @@ title: No Project Set Error (NO_PROJECT_SET) --- -This is a local development error likely caused by trying to run a project that cannot build or an invalid project. +This is a local development error likely caused by trying to run a project that +cannot build or an invalid project. diff --git a/docs/errors/settings-to-dev-portal-migration.md b/docs/errors/settings-to-dev-portal-migration.md index 3b86744a..f364217c 100644 --- a/docs/errors/settings-to-dev-portal-migration.md +++ b/docs/errors/settings-to-dev-portal-migration.md @@ -2,7 +2,8 @@ title: Settings to Dev Portal Migrations --- -The `/config/settings.json` file has been deprecated. You should migrate to the new `/config/dev-portal.json` file. +The `/config/settings.json` file has been deprecated. You should migrate to the +new `/config/dev-portal.json` file. The old `settings.json` file looks like this. diff --git a/docs/errors/system-configuration-error.md b/docs/errors/system-configuration-error.md index e952a24b..5b9c2de5 100644 --- a/docs/errors/system-configuration-error.md +++ b/docs/errors/system-configuration-error.md @@ -2,4 +2,5 @@ title: System Configuration Error (SYSTEM_CONFIGURATION_ERROR) --- -The runtime environment is not correctly configured. One or more environment variables are missing. +The runtime environment is not correctly configured. One or more environment +variables are missing. diff --git a/docs/errors/unknown-error.md b/docs/errors/unknown-error.md index 5efc23c9..c83d65ae 100644 --- a/docs/errors/unknown-error.md +++ b/docs/errors/unknown-error.md @@ -2,4 +2,5 @@ title: Unknown Error (MAIN_MOD_ERROR) --- -This in an unhandled error in the Zuplo runtime. Contact support@zuplo.com for assistance. +This in an unhandled error in the Zuplo runtime. Contact support@zuplo.com for +assistance. diff --git a/docs/handlers/custom-handler.md b/docs/handlers/custom-handler.md index ed90efd4..ea671808 100644 --- a/docs/handlers/custom-handler.md +++ b/docs/handlers/custom-handler.md @@ -10,7 +10,7 @@ definition (typescript): ```ts export type RequestHandler = ( request: ZuploRequest, - context: ZuploContext + context: ZuploContext, ) => Promise; ``` diff --git a/docs/handlers/websocket-handler.md b/docs/handlers/websocket-handler.md index 19b4aa11..c2eb4c07 100644 --- a/docs/handlers/websocket-handler.md +++ b/docs/handlers/websocket-handler.md @@ -3,9 +3,8 @@ title: WebSocket Handler sidebar_label: WebSocket Handler --- -::: note -This is an Enterprise only policy at this time. Please contact us to trial this or sign up for an Enterprise account. -::: +::: note This is an Enterprise only policy at this time. Please contact us to +trial this or sign up for an Enterprise account. ::: The WebSocket Handler enables you to manage WebSocket connections to your backend WebSocket APIs. It can be configured alongside other existing policies diff --git a/docs/intro.mdx b/docs/intro.mdx index 20f0d55c..e942b970 100644 --- a/docs/intro.mdx +++ b/docs/intro.mdx @@ -26,16 +26,16 @@ Learn how to use Zuplo to add API-key management, developer documentation, and r Getting Started - Step 1 - Setup Basic Gateway + Step 1 - Setup Basic Gateway - Step 2 - API Key Auth{" "} + Step 2 - API Key Auth - Step 3 - Rate Limiting + Step 3 - Rate Limiting - Step 4 - Deploying to the Edge + Step 4 - Deploying to the Edge diff --git a/docs/sample-apis.md b/docs/sample-apis.md index dbb8ec2d..f7d74b93 100644 --- a/docs/sample-apis.md +++ b/docs/sample-apis.md @@ -2,11 +2,13 @@ title: Sample APIs --- -Zuplo maintains a variety of sample APIs that are all built with Zuplo. These are free for anyone to use for testing or demos. +Zuplo maintains a variety of sample APIs that are all built with Zuplo. These +are free for anyone to use for testing or demos. ## Echo API -The echo API will accept any request and will return a JSON object with details of that request. This API accepts all HTTP methods and any body content. +The echo API will accept any request and will return a JSON object with details +of that request. This API accepts all HTTP methods and any body content. URL: https://echo.zuplo.io @@ -70,6 +72,7 @@ GitHub: https://github.com/zuplo/ecommerce-legacy Endpoints: - `GET /objects?type=OBJECT_TYPE`: Returns a collection of the object type -- `GET /objects?type=OBJECT_TYPE&id=OBJECT_ID`: Returns a single object based on ID +- `GET /objects?type=OBJECT_TYPE&id=OBJECT_ID`: Returns a single object based on + ID Valid `type` values are `product`, `user`, and `transaction`. diff --git a/package-lock.json b/package-lock.json index 1da1db10..b1af239e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -39,19 +39,16 @@ "@docusaurus/types": "3.0.0", "@types/glob": "^8.0.0", "@types/jsdom": "^21.1.0", - "@types/prettier": "^2.7.0", "@types/react": "^18.2.29", "@types/react-dom": "^18.2.15", - "arg": "^5.0.2", "chalk": "^4.1.2", - "chokidar": "^3.5.3", "concurrently": "^8.2.0", "esbuild": "^0.19.5", "glob": "^8.1.0", "jsdom": "^22.0.0", "json-schema": "^0.4.0", "nodemon": "^3.0.1", - "prettier": "^2.8.8", + "prettier": "^3.1.0", "prettier-plugin-organize-imports": "^3.1.1", "ts-node": "^10.8.2" } @@ -4453,12 +4450,6 @@ "resolved": "https://registry.npmjs.org/@types/parse5/-/parse5-6.0.3.tgz", "integrity": "sha512-SuT16Q1K51EAVPz1K29DJ/sXjhSQ0zjvsypYJ6tlwVsRV9jwW5Adq2ch8Dq8kDBCkYnELS7N7VNCSB5nC56t/g==" }, - "node_modules/@types/prettier": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz", - "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==", - "dev": true - }, "node_modules/@types/prismjs": { "version": "1.26.3", "resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.3.tgz", @@ -16478,15 +16469,15 @@ } }, "node_modules/prettier": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", - "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.1.0.tgz", + "integrity": "sha512-TQLvXjq5IAibjh8EpBIkNKxO749UEWABoiIZehEPiY4GNpVdhaFKqSTu+QrlU6D2dPAfubRmtJTi4K4YkQ5eXw==", "dev": true, "bin": { - "prettier": "bin-prettier.js" + "prettier": "bin/prettier.cjs" }, "engines": { - "node": ">=10.13.0" + "node": ">=14" }, "funding": { "url": "https://github.com/prettier/prettier?sponsor=1" diff --git a/package.json b/package.json index 5fca5a3d..1c3b7b2a 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "start:docusaurus": "node ./scripts/check-prepared.mjs && docusaurus start --port 3002", "build": "npm run ci:pre && docusaurus build", "build:scripts": "node ./scripts/build.mjs", + "format": "prettier --write .", "local": "npm run ci:pre", "swizzle": "docusaurus swizzle", "deploy": "docusaurus deploy", @@ -53,19 +54,16 @@ "@docusaurus/types": "3.0.0", "@types/glob": "^8.0.0", "@types/jsdom": "^21.1.0", - "@types/prettier": "^2.7.0", "@types/react": "^18.2.29", "@types/react-dom": "^18.2.15", - "arg": "^5.0.2", "chalk": "^4.1.2", - "chokidar": "^3.5.3", "concurrently": "^8.2.0", "esbuild": "^0.19.5", "glob": "^8.1.0", "jsdom": "^22.0.0", "json-schema": "^0.4.0", "nodemon": "^3.0.1", - "prettier": "^2.8.8", + "prettier": "^3.1.0", "prettier-plugin-organize-imports": "^3.1.1", "ts-node": "^10.8.2" }, diff --git a/policies/ab-test-outbound/policy.ts b/policies/ab-test-outbound/policy.ts index b0d25e29..3a1d358d 100644 --- a/policies/ab-test-outbound/policy.ts +++ b/policies/ab-test-outbound/policy.ts @@ -3,7 +3,7 @@ import { ZuploContext, ZuploRequest } from "@zuplo/runtime"; export default async function ( response: Response, request: ZuploRequest, - context: ZuploContext + context: ZuploContext, ) { // Generate a random number to segment the test groups const score = Math.random(); diff --git a/policies/acl-policy-inbound/policy.ts b/policies/acl-policy-inbound/policy.ts index b4c70044..116e1301 100644 --- a/policies/acl-policy-inbound/policy.ts +++ b/policies/acl-policy-inbound/policy.ts @@ -8,13 +8,13 @@ export default async function ( request: ZuploRequest, context: ZuploContext, options: PolicyOptions, - policyName: string + policyName: string, ) { // Check that an authenticated user is set // NOTE: This policy requires an authentication policy to run before if (!request.user) { context.log.error( - "User is not authenticated. A authorization policy must come before the ACL policy." + "User is not authenticated. A authorization policy must come before the ACL policy.", ); return HttpProblems.unauthorized(request, context); } @@ -22,7 +22,7 @@ export default async function ( // Check that the user has one of the allowed roles if (!options.users.includes(request.user.sub)) { context.log.error( - `The user '${request.user.sub}' is not authorized to perform this action.` + `The user '${request.user.sub}' is not authorized to perform this action.`, ); return HttpProblems.forbidden(request, context); } diff --git a/policies/amberflo-metering-inbound/doc.md b/policies/amberflo-metering-inbound/doc.md index 16c6a780..74c3a205 100644 --- a/policies/amberflo-metering-inbound/doc.md +++ b/policies/amberflo-metering-inbound/doc.md @@ -1,8 +1,14 @@ -You can set the customerId globally (not recommended) by setting it at the policy level or use the `customerIdPropertyPath` to read the customerId from the user object on each request. For example, if you're using API Key auth or JWT auth and want to use the `sub` property as the customerId, you would set the value as follows +You can set the customerId globally (not recommended) by setting it at the +policy level or use the `customerIdPropertyPath` to read the customerId from the +user object on each request. For example, if you're using API Key auth or JWT +auth and want to use the `sub` property as the customerId, you would set the +value as follows `"customerIdPropertyPath" : ".sub"` -You can also dive into the properties of the metadata. Imagine the `request.user` property is as follows (either based on contents of a JWT token or API Key metadata) +You can also dive into the properties of the metadata. Imagine the +`request.user` property is as follows (either based on contents of a JWT token +or API Key metadata) ```json { @@ -16,11 +22,14 @@ You can also dive into the properties of the metadata. Imagine the `request.user } ``` -You could access the `accountNumber` property as follows. Note the required preceding `'.'`. +You could access the `accountNumber` property as follows. Note the required +preceding `'.'`. `"customerIdPropertyPath" : ".data.accountNumber"` -You can also set many of the properties of the meter payload programmatically, either in a custom policy or handler. Here is some example code in a custom inbound policy: +You can also set many of the properties of the meter payload programmatically, +either in a custom policy or handler. Here is some example code in a custom +inbound policy: ```ts import { @@ -33,7 +42,7 @@ export default async function ( request: ZuploRequest, context: ZuploContext, options: MyPolicyOptionsType, - policyName: string + policyName: string, ) { AmberfloMeteringPolicy.setRequestProperties(context, { customerId: request.user.sub, diff --git a/policies/amberflo-metering-inbound/intro.md b/policies/amberflo-metering-inbound/intro.md index 98c65bf9..32d68644 100644 --- a/policies/amberflo-metering-inbound/intro.md +++ b/policies/amberflo-metering-inbound/intro.md @@ -1,3 +1,6 @@ -Amberflo ([amberflo.io](https://www.amberflo.io/)) is a usage metering and billing service. This policy allows you to meter API calls going through Zuplo and send them to your Amberflo account using your Amberflo API key. +Amberflo ([amberflo.io](https://www.amberflo.io/)) is a usage metering and +billing service. This policy allows you to meter API calls going through Zuplo +and send them to your Amberflo account using your Amberflo API key. -Add the policy to each route you want to meter. Note you can specify the Meter API Name and Meter Value (meter increment) at the policy level. +Add the policy to each route you want to meter. Note you can specify the Meter +API Name and Meter Value (meter increment) at the policy level. diff --git a/policies/api-key-auth-inbound/intro.md b/policies/api-key-auth-inbound/intro.md index 5febe940..f53480bc 100644 --- a/policies/api-key-auth-inbound/intro.md +++ b/policies/api-key-auth-inbound/intro.md @@ -1,3 +1,8 @@ -**This policy is deprecated. Use the new [API Key Authentication policy instead](https://zuplo.com/docs/policies/api-key-inbound)** +**This policy is deprecated. Use the new +[API Key Authentication policy instead](https://zuplo.com/docs/policies/api-key-inbound)** -This policy uses the managed API key storage provided by Zuplo. `allowUnauthenticatedRequests` defaults to false and rejects any request without a valid API key (returning a `401 - Unauthorized` response). You can override (set `"allowUnauthenticatedRequests" : true`) this to support multiple authentication methods or support both authenticated and anonymous requests. +This policy uses the managed API key storage provided by Zuplo. +`allowUnauthenticatedRequests` defaults to false and rejects any request without +a valid API key (returning a `401 - Unauthorized` response). You can override +(set `"allowUnauthenticatedRequests" : true`) this to support multiple +authentication methods or support both authenticated and anonymous requests. diff --git a/policies/archive-request-aws-s3-inbound/policy.ts b/policies/archive-request-aws-s3-inbound/policy.ts index edf51051..6c3195cf 100644 --- a/policies/archive-request-aws-s3-inbound/policy.ts +++ b/policies/archive-request-aws-s3-inbound/policy.ts @@ -12,7 +12,7 @@ export default async function ( request: ZuploRequest, context: ZuploContext, options: PolicyOptions, - policyName: string + policyName: string, ) { // NOTE: policy options should be validated, but to keep the sample short, // we are skipping that here. diff --git a/policies/archive-request-azure-storage-inbound/policy.ts b/policies/archive-request-azure-storage-inbound/policy.ts index 1e85e236..794e260f 100644 --- a/policies/archive-request-azure-storage-inbound/policy.ts +++ b/policies/archive-request-azure-storage-inbound/policy.ts @@ -9,7 +9,7 @@ export default async function ( request: ZuploRequest, context: ZuploContext, options: PolicyOptions, - policyName: string + policyName: string, ) { // NOTE: policy options should be validated, but to keep the sample short, // we are skipping that here. diff --git a/policies/archive-response-aws-s3-outbound/policy.ts b/policies/archive-response-aws-s3-outbound/policy.ts index 516fb5d3..af7529d3 100644 --- a/policies/archive-response-aws-s3-outbound/policy.ts +++ b/policies/archive-response-aws-s3-outbound/policy.ts @@ -13,7 +13,7 @@ export default async function ( response: Response, request: ZuploRequest, context: ZuploContext, - options: PolicyOptions + options: PolicyOptions, ) { // NOTE: policy options should be validated, but to keep the sample short, // we are skipping that here. diff --git a/policies/archive-response-azure-storage-outbound/policy.ts b/policies/archive-response-azure-storage-outbound/policy.ts index acdfd605..702500d9 100644 --- a/policies/archive-response-azure-storage-outbound/policy.ts +++ b/policies/archive-response-azure-storage-outbound/policy.ts @@ -9,7 +9,7 @@ export default async function ( response: Response, request: ZuploRequest, context: ZuploContext, - options: PolicyOptions + options: PolicyOptions, ) { // NOTE: policy options should be validated, but to keep the sample short, // we are skipping that here. diff --git a/policies/auth0-jwt-auth-inbound/intro.md b/policies/auth0-jwt-auth-inbound/intro.md index b8a75200..8aa8dc2a 100644 --- a/policies/auth0-jwt-auth-inbound/intro.md +++ b/policies/auth0-jwt-auth-inbound/intro.md @@ -1 +1,3 @@ -Authenticate requests with JWT tokens issued by Auth0. This is a customized version of the [OpenId JWT Policy](./open-id-jwt-auth-inbound.md) specifically for Auth0. +Authenticate requests with JWT tokens issued by Auth0. This is a customized +version of the [OpenId JWT Policy](./open-id-jwt-auth-inbound.md) specifically +for Auth0. diff --git a/policies/bot-detection-inbound/intro.md b/policies/bot-detection-inbound/intro.md index ada9dbf3..11c4f828 100644 --- a/policies/bot-detection-inbound/intro.md +++ b/policies/bot-detection-inbound/intro.md @@ -1 +1,5 @@ -Detect known and suspected bots based on sophisticated traffic analysis. The bot detection inbound policy provides a bot score for every request that can be used to determine the likelihood the request came from a bot. The policy can be configured to automatically block traffic with a set score or simply pass along the score for you to respond in other policies or handlers. +Detect known and suspected bots based on sophisticated traffic analysis. The bot +detection inbound policy provides a bot score for every request that can be used +to determine the likelihood the request came from a bot. The policy can be +configured to automatically block traffic with a set score or simply pass along +the score for you to respond in other policies or handlers. diff --git a/policies/composite-inbound/intro.md b/policies/composite-inbound/intro.md index 34ac9004..1cdda396 100644 --- a/policies/composite-inbound/intro.md +++ b/policies/composite-inbound/intro.md @@ -1,4 +1,5 @@ -The Composite policy allows you to create groups of other policies, for easy reuse across multiple routes. Other policies are referenced by their `name`. +The Composite policy allows you to create groups of other policies, for easy +reuse across multiple routes. Other policies are referenced by their `name`. :::warning diff --git a/policies/custom-code-inbound/doc.md b/policies/custom-code-inbound/doc.md index cea38eb3..cd2d4256 100644 --- a/policies/custom-code-inbound/doc.md +++ b/policies/custom-code-inbound/doc.md @@ -31,7 +31,7 @@ export type InboundPolicyHandler = ( request: ZuploRequest, context: ZuploContext, options: TOptions, - policyName: string + policyName: string, ) => Promise; ``` @@ -155,7 +155,7 @@ export default async function ( request: ZuploRequest, context: ZuploContext, options: MyPolicyOptionsType, - policyName: string + policyName: string, ) { // your policy code goes here, and can use the options to perform any // configuration @@ -207,7 +207,7 @@ export default async function ( request: ZuploRequest, context: ZuploContext, options: any, - policyName: string + policyName: string, ) { const apiKeyHeader = request.headers.get("api-key"); if (!apiKeyHeader) { diff --git a/policies/custom-code-inbound/intro.md b/policies/custom-code-inbound/intro.md index c95d21d1..58c9e934 100644 --- a/policies/custom-code-inbound/intro.md +++ b/policies/custom-code-inbound/intro.md @@ -1 +1,2 @@ -Add your own custom policy coded in TypeScript. See below for more details on how to build your own policy. +Add your own custom policy coded in TypeScript. See below for more details on +how to build your own policy. diff --git a/policies/custom-code-outbound/doc.md b/policies/custom-code-outbound/doc.md index 97374af4..98b6b23a 100644 --- a/policies/custom-code-outbound/doc.md +++ b/policies/custom-code-outbound/doc.md @@ -1,12 +1,21 @@ ## Writing A Policy -Custom policies can be written to extend the functionality of your gateway. This document is about outbound policies that can intercept the request and, if required, modify it before passing down the chain. +Custom policies can be written to extend the functionality of your gateway. This +document is about outbound policies that can intercept the request and, if +required, modify it before passing down the chain. -The outbound custom policy is similar to the inbound custom policy but also accepts a `Response` parameter. The outbound policy must return a valid `Response` (or throw an error, which will result in a 500 Internal Server Error for your consumer, not recommended). +The outbound custom policy is similar to the inbound custom policy but also +accepts a `Response` parameter. The outbound policy must return a valid +`Response` (or throw an error, which will result in a 500 Internal Server Error +for your consumer, not recommended). :::tip -Note that both `ZuploRequest` and `Response` are based on the web standards [Request](https://developer.mozilla.org/en-US/docs/Web/API/request) and [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response). ZuploRequest adds a few additional properties for convenience, like `user` and `params`. +Note that both `ZuploRequest` and `Response` are based on the web standards +[Request](https://developer.mozilla.org/en-US/docs/Web/API/request) and +[Response](https://developer.mozilla.org/en-US/docs/Web/API/Response). +ZuploRequest adds a few additional properties for convenience, like `user` and +`params`. ::: @@ -16,11 +25,13 @@ export type OutboundPolicyHandler = ( request: ZuploRequest, context: ZuploContext, options: TOptions, - policyName: string + policyName: string, ) => Promise; ``` -A common use case for outbound policies is to change the body of the response. In this example, we'll imagine we are proxying the `/todos` example api at [https://jsonplaceholder.typicode.com/todos](https://jsonplaceholder.typicode.com/todos). +A common use case for outbound policies is to change the body of the response. +In this example, we'll imagine we are proxying the `/todos` example api at +[https://jsonplaceholder.typicode.com/todos](https://jsonplaceholder.typicode.com/todos). The format of the /todos response looks like this @@ -53,7 +64,7 @@ export default async function ( request: ZuploRequest, context: ZuploContext, options: any, - policyName: string + policyName: string, ) { if (response.status !== 200) { // if we get an unexpected response code, something went wrong, just let the response flow @@ -80,7 +91,11 @@ export default async function ( :::tip -Note, that because we're not using the original response here (we just use the new one called `newResponse`) we didn't need to `clone` the original response before reading the body with `.json()`. If you need to read the body and use that same instance you must first `clone()` to avoid runtime errors such as "Body is unusable". +Note, that because we're not using the original response here (we just use the +new one called `newResponse`) we didn't need to `clone` the original response +before reading the body with `.json()`. If you need to read the body and use +that same instance you must first `clone()` to avoid runtime errors such as +"Body is unusable". ::: @@ -95,7 +110,7 @@ export default async function ( request: ZuploRequest, context: ZuploContext, options: any, - policyName: string + policyName: string, ) { // create a new response const newResponse = new Response(response.body, { diff --git a/policies/custom-code-outbound/intro.md b/policies/custom-code-outbound/intro.md index c95d21d1..58c9e934 100644 --- a/policies/custom-code-outbound/intro.md +++ b/policies/custom-code-outbound/intro.md @@ -1 +1,2 @@ -Add your own custom policy coded in TypeScript. See below for more details on how to build your own policy. +Add your own custom policy coded in TypeScript. See below for more details on +how to build your own policy. diff --git a/policies/graphql-complexity-limit-inbound/doc.md b/policies/graphql-complexity-limit-inbound/doc.md index 48049923..79d78eb6 100644 --- a/policies/graphql-complexity-limit-inbound/doc.md +++ b/policies/graphql-complexity-limit-inbound/doc.md @@ -1,6 +1,7 @@ ## GraphQL Complexity Limit -This policy allows you to add a limit for the depth and a limit for the complexity of a GraphQL query. +This policy allows you to add a limit for the depth and a limit for the +complexity of a GraphQL query. ### Depth Limit @@ -30,19 +31,19 @@ DoS attacks on your GraphQL server. } ``` -### Complexity Limit +### Complexity Limit + Example: - **maxComplexity** - Maximum complexity allowed for a query. - ``` { - me { + me { name # Complexity +1 age # Complexity +1 email # Complexity +1 - friends { + friends { name # Complexity +1 height # Complexity +1 } diff --git a/policies/graphql-disable-introspection-inbound/doc.md b/policies/graphql-disable-introspection-inbound/doc.md index 02a2712c..5599cf81 100644 --- a/policies/graphql-disable-introspection-inbound/doc.md +++ b/policies/graphql-disable-introspection-inbound/doc.md @@ -1,3 +1,4 @@ ## GraphQL Disable Introspection -This policy allows you to disable introspection queries on your API. Any introspection query will be blocked with a `403 Forbidden` response. +This policy allows you to disable introspection queries on your API. Any +introspection query will be blocked with a `403 Forbidden` response. diff --git a/policies/graphql-disable-introspection-inbound/schema.json b/policies/graphql-disable-introspection-inbound/schema.json index 613beea9..862b6adf 100644 --- a/policies/graphql-disable-introspection-inbound/schema.json +++ b/policies/graphql-disable-introspection-inbound/schema.json @@ -24,9 +24,7 @@ "type": "object", "description": "The options for this policy", "required": [], - "properties": { - - } + "properties": {} } }, "examples": [ @@ -34,8 +32,7 @@ "_name": "basic", "export": "GraphQLDisableIntrospectionInboundPolicy", "module": "$import(@zuplo/runtime)", - "options": { - } + "options": {} } ] } diff --git a/policies/hmac-auth-inbound/doc.md b/policies/hmac-auth-inbound/doc.md index ced1fe81..bbf88f4c 100644 --- a/policies/hmac-auth-inbound/doc.md +++ b/policies/hmac-auth-inbound/doc.md @@ -8,7 +8,7 @@ const token = await sign("my data", environment.MY_SECRET); async function sign( key: string | ArrayBuffer, - val: string + val: string, ): Promise { const encoder = new TextEncoder(); const cryptoKey = await crypto.subtle.importKey( @@ -16,12 +16,12 @@ async function sign( typeof key === "string" ? encoder.encode(key) : key, { name: "HMAC", hash: { name: "SHA-256" } }, false, - ["sign"] + ["sign"], ); const token = await crypto.subtle.sign( "HMAC", cryptoKey, - encoder.encode(val) + encoder.encode(val), ); return Array.prototype.map .call(new Uint8Array(token), (x) => ("0" + x.toString(16)).slice(-2)) diff --git a/policies/hmac-auth-inbound/policy.ts b/policies/hmac-auth-inbound/policy.ts index aad06c37..798818b0 100644 --- a/policies/hmac-auth-inbound/policy.ts +++ b/policies/hmac-auth-inbound/policy.ts @@ -9,17 +9,17 @@ export default async function ( request: ZuploRequest, context: ZuploContext, options: PolicyOptions, - policyName: string + policyName: string, ) { // Validate the policy options if (typeof options.secret !== "string") { throw new Error( - `The option 'secret' on policy '${policyName}' must be a string. Received ${typeof options.secret}.` + `The option 'secret' on policy '${policyName}' must be a string. Received ${typeof options.secret}.`, ); } if (typeof options.headerName !== "string") { throw new Error( - `The option 'headerName' on policy '${policyName}' must be a string. Received ${typeof options.headerName}.` + `The option 'headerName' on policy '${policyName}' must be a string. Received ${typeof options.headerName}.`, ); } @@ -33,7 +33,7 @@ export default async function ( // Convert the hex encoded token to an Uint8Array const tokenData = new Uint8Array( - token.match(/../g)!.map((h) => parseInt(h, 16)) + token.match(/../g)!.map((h) => parseInt(h, 16)), ); // Get the data to verify @@ -49,7 +49,7 @@ export default async function ( encodedSecret, { name: "HMAC", hash: "SHA-256" }, false, - ["verify"] + ["verify"], ); // Verify that the data @@ -57,7 +57,7 @@ export default async function ( "HMAC", key, tokenData, - encoder.encode(data) + encoder.encode(data), ); // Check if the data is verified, if not return unauthorized diff --git a/policies/index.md b/policies/index.md index ef58d2d2..086443cf 100644 --- a/policies/index.md +++ b/policies/index.md @@ -3,11 +3,16 @@ title: Policy Catalog sidebar_label: Policies --- -import ItemCatalog from '@site/src/components/ItemCatalog'; -import policyConfig from '@site/policies.v3.json'; +import ItemCatalog from '@site/src/components/ItemCatalog'; import policyConfig +from '@site/policies.v3.json'; -Zuplo includes policies for any solution you need for securing and sharing your API. See the [policy introduction](/docs/policies) to learn about using policies. +Zuplo includes policies for any solution you need for securing and sharing your +API. See the [policy introduction](/docs/policies) to learn about using +policies. -The [CORS policy](/docs/articles/custom-cors-policy) is a special type of policy that is configured separately. Check out details [here](/docs/articles/custom-cors-policy). +The [CORS policy](/docs/articles/custom-cors-policy) is a special type of policy +that is configured separately. Check out details +[here](/docs/articles/custom-cors-policy). - ({ id, name, icon, href: "/docs/policies/" + id }))} /> + ({ id, +name, icon, href: "/docs/policies/" + id }))} /> diff --git a/policies/ip-restriction-inbound/policy.ts b/policies/ip-restriction-inbound/policy.ts index da995aaf..d2234532 100644 --- a/policies/ip-restriction-inbound/policy.ts +++ b/policies/ip-restriction-inbound/policy.ts @@ -10,7 +10,7 @@ export default async function ( request: ZuploRequest, context: ZuploContext, options: PolicyOptions, - policyName: string + policyName: string, ) { // TODO: Validate the policy options. Skipping in the example for brevity diff --git a/policies/open-id-jwt-auth-inbound/example.md b/policies/open-id-jwt-auth-inbound/example.md index 74c34b27..3d0fc20d 100644 --- a/policies/open-id-jwt-auth-inbound/example.md +++ b/policies/open-id-jwt-auth-inbound/example.md @@ -1,5 +1,7 @@ The Open ID JWT Authentication policy allows you to authenticate incoming -requests using an Open ID-compliant bearer token. It works with any valid JWT token. Note that you can configure this to work with tokens generated by Auth0, Okta, or Cognito but we have custom policies to make those even easier. +requests using an Open ID-compliant bearer token. It works with any valid JWT +token. Note that you can configure this to work with tokens generated by Auth0, +Okta, or Cognito but we have custom policies to make those even easier. In this example, we have a policy configured to validate an incoming JWT token signed by a secret stored in an environment variable `AUTH_JWT_SIGNING_KEY`. @@ -15,11 +17,11 @@ signed by a secret stored in an environment variable `AUTH_JWT_SIGNING_KEY`. "secret": "$env(AUTH_JWT_SIGNING_KEY)" } } - ``` -The following example shows you how you could use a JWK URL to validate your tokens - this example would work for Auth0 (though you should just use the Auth0 policy) - +The following example shows you how you could use a JWK URL to validate your +tokens - this example would work for Auth0 (though you should just use the Auth0 +policy) ```json { @@ -32,4 +34,4 @@ The following example shows you how you could use a JWK URL to validate your tok "jwkUrl": "https://zuplo-demo.us.auth0.com/.well-known/jwks.json" } } -``` \ No newline at end of file +``` diff --git a/policies/rate-limit-inbound/doc.md b/policies/rate-limit-inbound/doc.md index 5e8daf41..fb6706a5 100644 --- a/policies/rate-limit-inbound/doc.md +++ b/policies/rate-limit-inbound/doc.md @@ -27,10 +27,10 @@ import { CustomRateLimitDetails, ZuploRequest } from "@zuplo/runtime"; export function rateLimitKey( request: ZuploRequest, context: ZuploContext, - policyName: string + policyName: string, ): CustomRateLimitDetails { context.log.info( - `processing customerId '${request.params.customerId}' for rate-limit policy '${policyName}'` + `processing customerId '${request.params.customerId}' for rate-limit policy '${policyName}'`, ); if (request.params.customerId === "43567890") { // Override timeWindowMinutes & requestsAllowed diff --git a/policies/rbac-policy-inbound/policy.ts b/policies/rbac-policy-inbound/policy.ts index 27cccd78..b9540e81 100644 --- a/policies/rbac-policy-inbound/policy.ts +++ b/policies/rbac-policy-inbound/policy.ts @@ -8,13 +8,13 @@ export default async function ( request: ZuploRequest, context: ZuploContext, options: PolicyOptions, - policyName: string + policyName: string, ) { // Check that an authenticated user is set // NOTE: This policy requires an authentication policy to run before if (!request.user) { context.log.error( - "User is not authenticated. A authorization policy must come before the RBAC policy." + "User is not authenticated. A authorization policy must come before the RBAC policy.", ); return HttpProblems.unauthorized(request, context); } @@ -27,12 +27,12 @@ export default async function ( // Check that the user has one of the allowed roles if ( - !options.allowedRoles.some((allowedRole) => - request.user?.data.roles.includes(allowedRole) + !options.allowedRoles.some( + (allowedRole) => request.user?.data.roles.includes(allowedRole), ) ) { context.log.error( - `The user '${request.user.sub}' is not authorized to perform this action.` + `The user '${request.user.sub}' is not authorized to perform this action.`, ); return HttpProblems.forbidden(request, context); } diff --git a/policies/readme-metrics-inbound/intro.md b/policies/readme-metrics-inbound/intro.md index 163ac84a..ad904422 100644 --- a/policies/readme-metrics-inbound/intro.md +++ b/policies/readme-metrics-inbound/intro.md @@ -1,3 +1,5 @@ -Readme ([readme.com](https://readme.com)) is a developer Documentation and metrics service. This policy pushes the request/response data to their ingestion endpoint so you can see your Zuplo API traffic in their API calls dashboard. +Readme ([readme.com](https://readme.com)) is a developer Documentation and +metrics service. This policy pushes the request/response data to their ingestion +endpoint so you can see your Zuplo API traffic in their API calls dashboard. ![Readme API Calls Dashboard](https://cdn.zuplo.com/assets/071b2ead-7769-413b-a66a-133ae6fd755d.png) diff --git a/policies/remove-headers-inbound/schema.json b/policies/remove-headers-inbound/schema.json index d793caa1..d4a98f27 100644 --- a/policies/remove-headers-inbound/schema.json +++ b/policies/remove-headers-inbound/schema.json @@ -25,7 +25,7 @@ "description": "The options for this policy", "required": ["headers"], "properties": { - "headers" : { + "headers": { "type": "array", "description": "A list of headers to be removed from the incoming request" } diff --git a/policies/require-origin-inbound/intro.md b/policies/require-origin-inbound/intro.md index 101ea46b..615bbc39 100644 --- a/policies/require-origin-inbound/intro.md +++ b/policies/require-origin-inbound/intro.md @@ -1,7 +1,12 @@ -The Require Origin policy is used to enforce that the client is sending an `origin` header that matches your allow-list specified in the policy options. +The Require Origin policy is used to enforce that the client is sending an +`origin` header that matches your allow-list specified in the policy options. This is useful if you want to stop any browser traffic from different domains. -However, it is important to note that it does not guarantee that traffic is only coming from a browser. Somebody could simulate a browser request from a backend server and set any origin they like. +However, it is important to note that it does not guarantee that traffic is only +coming from a browser. Somebody could simulate a browser request from a backend +server and set any origin they like. -If the incoming origin is missing, or not allowed - a 400 Forbidden Problem Response will be sent to the client. You can customize the `detail` property in the policy options. +If the incoming origin is missing, or not allowed - a 400 Forbidden Problem +Response will be sent to the client. You can customize the `detail` property in +the policy options. diff --git a/policies/set-body-inbound/intro.md b/policies/set-body-inbound/intro.md index ae62b505..e04b75af 100644 --- a/policies/set-body-inbound/intro.md +++ b/policies/set-body-inbound/intro.md @@ -1 +1,6 @@ -The Set Body policy allows you to set or override the incoming request body. [GET or HEAD requests do not support bodies on Zuplo](/docs/articles/zp-body-removed), so be sure to use the [Change Method](/docs/policies/change-method-inbound) policy to update the method to a `POST` or whatever is appropriate. You might also need to use the [Set Header](/docs/policies/set-headers-inbound) policy to set a `content-type`. +The Set Body policy allows you to set or override the incoming request body. +[GET or HEAD requests do not support bodies on Zuplo](/docs/articles/zp-body-removed), +so be sure to use the [Change Method](/docs/policies/change-method-inbound) +policy to update the method to a `POST` or whatever is appropriate. You might +also need to use the [Set Header](/docs/policies/set-headers-inbound) policy to +set a `content-type`. diff --git a/policies/set-headers-inbound/intro.md b/policies/set-headers-inbound/intro.md index e2325fdd..4f005ae9 100644 --- a/policies/set-headers-inbound/intro.md +++ b/policies/set-headers-inbound/intro.md @@ -1,4 +1,7 @@ -The set header policy adds a header to the request in the inbound pipeline. This can be used to set a security header required by the downstream service. For example, if your backend service uses basic authentication you might use this policy to attach the Basic auth header to the request: +The set header policy adds a header to the request in the inbound pipeline. This +can be used to set a security header required by the downstream service. For +example, if your backend service uses basic authentication you might use this +policy to attach the Basic auth header to the request: ```json { @@ -16,7 +19,8 @@ The set header policy adds a header to the request in the inbound pipeline. This } ``` -When doing this, you most likely want to set the secret as an environment variable, which can be accessed in the policy as follows +When doing this, you most likely want to set the secret as an environment +variable, which can be accessed in the policy as follows ```json { @@ -34,4 +38,5 @@ When doing this, you most likely want to set the secret as an environment variab } ``` -And you would set the environment variable `BASIC_AUTHORIZATION_HEADER_VALUE` to `Basic DIGEST_HERE`. +And you would set the environment variable `BASIC_AUTHORIZATION_HEADER_VALUE` to +`Basic DIGEST_HERE`. diff --git a/policies/supabase-jwt-auth-inbound/doc.md b/policies/supabase-jwt-auth-inbound/doc.md index 4ba103f6..5f3afc72 100644 --- a/policies/supabase-jwt-auth-inbound/doc.md +++ b/policies/supabase-jwt-auth-inbound/doc.md @@ -1,6 +1,8 @@ ## Authorization -You can also require certain claims to be valid by specifying this in the options. For example, if you require the claim `user_role` to be either `admin` or `supa_user`, you would configure the policy as follows: +You can also require certain claims to be valid by specifying this in the +options. For example, if you require the claim `user_role` to be either `admin` +or `supa_user`, you would configure the policy as follows: ```json { diff --git a/policies/supabase-jwt-auth-inbound/intro.md b/policies/supabase-jwt-auth-inbound/intro.md index 7facf338..56f0b871 100644 --- a/policies/supabase-jwt-auth-inbound/intro.md +++ b/policies/supabase-jwt-auth-inbound/intro.md @@ -1,8 +1,12 @@ The Supabase JWT Authentication policy allows you to authenticate incoming requests using a token created by [supabase.com](https://supabase.com). -When configured, you can have Zuplo check incoming requests for a JWT token and automatically populate the `ZuploRequest`'s `user` property with a user object. +When configured, you can have Zuplo check incoming requests for a JWT token and +automatically populate the `ZuploRequest`'s `user` property with a user object. -This `user` object will have a `sub` property - taking the `sub` id from the JWT token. It will also have a `data` property populated by other data returned in the JWT token - including all your claims, `user_metadata` and `app_metadata`. +This `user` object will have a `sub` property - taking the `sub` id from the JWT +token. It will also have a `data` property populated by other data returned in +the JWT token - including all your claims, `user_metadata` and `app_metadata`. -You can also require specific claims to have specific values to allow authentication to complete, providing a layer of authorization. +You can also require specific claims to have specific values to allow +authentication to complete, providing a layer of authorization. diff --git a/policies/transform-body-outbound/policy.ts b/policies/transform-body-outbound/policy.ts index 13429bbb..6d60e599 100644 --- a/policies/transform-body-outbound/policy.ts +++ b/policies/transform-body-outbound/policy.ts @@ -3,7 +3,7 @@ import { ZuploContext, ZuploRequest } from "@zuplo/runtime"; export default async function ( response: Response, request: ZuploRequest, - context: ZuploContext + context: ZuploContext, ) { // Get the outgoing body as an Object const obj = await response.json(); diff --git a/policies/upstream-azure-ad-service-auth-inbound/intro.md b/policies/upstream-azure-ad-service-auth-inbound/intro.md index f88f28b8..d4bcb92e 100644 --- a/policies/upstream-azure-ad-service-auth-inbound/intro.md +++ b/policies/upstream-azure-ad-service-auth-inbound/intro.md @@ -1,3 +1,9 @@ -This policy adds a `Authorization` header to the upstream request that allows using Azure AD to authenticate requests to your origin server. This is a useful means of securing your origin server so that only your Zuplo gateway can make requests against it. +This policy adds a `Authorization` header to the upstream request that allows +using Azure AD to authenticate requests to your origin server. This is a useful +means of securing your origin server so that only your Zuplo gateway can make +requests against it. -Using this policy allows you to delegate authentication and authorization to your gateway without writing any code on your origin service. For instructions on how to configure Azure AD authentication see [Azure's documentation](https://learn.microsoft.com/en-us/azure/app-service/configure-authentication-provider-aad). +Using this policy allows you to delegate authentication and authorization to +your gateway without writing any code on your origin service. For instructions +on how to configure Azure AD authentication see +[Azure's documentation](https://learn.microsoft.com/en-us/azure/app-service/configure-authentication-provider-aad). diff --git a/scripts/check-prepared.mjs b/scripts/check-prepared.mjs index 9ec22402..8170571e 100644 --- a/scripts/check-prepared.mjs +++ b/scripts/check-prepared.mjs @@ -8,8 +8,8 @@ function exitWithError(error) { console.error(chalk.redBright(`[ERROR] ${error}`)); console.warn( chalk.yellowBright( - "[WARN] You must run 'npm run local' before you can start the site." - ) + "[WARN] You must run 'npm run local' before you can start the site.", + ), ); process.exit(1); } @@ -18,7 +18,7 @@ async function run() { // Check bundle.json exist const bundleJsonPath = resolve( process.cwd(), - "./src/components/bundles.json" + "./src/components/bundles.json", ); if (!existsSync(bundleJsonPath)) { return exitWithError("Bundles have not been synced."); @@ -26,8 +26,8 @@ async function run() { console.warn( chalk.yellowBright( - "[WARN] Using cached local content, to update cache run 'npm run local'" - ) + "[WARN] Using cached local content, to update cache run 'npm run local'", + ), ); } diff --git a/scripts/update-bundles.mjs b/scripts/update-bundles.mjs index f2da8b2a..7febee89 100644 --- a/scripts/update-bundles.mjs +++ b/scripts/update-bundles.mjs @@ -6,6 +6,6 @@ fetch(`https://cdn.zuplo.com/types/@zuplo/bundled/bundles.v2.json`, { cache: "no-cache", }).then((response) => Readable.fromWeb(response.body).pipe( - createWriteStream(resolve(process.cwd(), "./src/components/bundles.json")) - ) + createWriteStream(resolve(process.cwd(), "./src/components/bundles.json")), + ), ); diff --git a/scripts/update-policies.tsx b/scripts/update-policies.tsx index 9da5146c..f963cfd2 100644 --- a/scripts/update-policies.tsx +++ b/scripts/update-policies.tsx @@ -1,7 +1,5 @@ import { dereference } from "@apidevtools/json-schema-ref-parser"; -import arg from "arg"; import chalk from "chalk"; -import chokidar from "chokidar"; import { existsSync } from "fs"; import { copyFile, mkdir, readdir, readFile, rm, writeFile } from "fs/promises"; import glob from "glob"; @@ -452,7 +450,7 @@ async function run() { policies, }; - const policiesV3Json = stringify(policyDataV3); + const policiesV3Json = await stringify(policyDataV3); await writeFile( path.resolve(policiesDir, "../policies.v3.json"), @@ -463,44 +461,6 @@ async function run() { console.info("Policies updated"); } -async function watch() { - await run(); - var watcher = chokidar.watch(policiesDir, { - ignored: /^\./, - persistent: true, - ignoreInitial: true, - }); - - function changed(path) { - run().catch(console.error); - } - - watcher - .on("add", changed) - .on("change", changed) - .on("unlink", changed) - .on("error", function (error) { - console.error("Error happened", error); - }); -} - -const args = arg({ - // Types - "--watch": Boolean, -}); - -if (args["--watch"]) { - watch().catch((err) => { - console.error(err); - process.exit(1); - }); -} else { - run().catch((err) => { - console.error(err); - process.exit(1); - }); -} - async function getExampleHtml( policyId: string, policyPath: string, diff --git a/vercel.json b/vercel.json index 8f03d2e0..b6b3b4ac 100644 --- a/vercel.json +++ b/vercel.json @@ -793,4 +793,4 @@ ] } ] -} \ No newline at end of file +}