-
Notifications
You must be signed in to change notification settings - Fork 88
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add init support for testing hono deployments on CF with edge * Fix the directory path in hono workflow * Fix the deploy command * Fix tests to use assert instead * Remove unused build command * Update hono edge tests to test session endpoint as well * Update README to contain proper details * Drop brotli decompression support * Remove brotli as a dependency
- Loading branch information
1 parent
2f2fb62
commit 58ab0e5
Showing
21 changed files
with
3,674 additions
and
81 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
name: "Test edge function compatibility for Hono on Cloudflare Workers" | ||
on: push | ||
jobs: | ||
test: | ||
runs-on: ubuntu-latest | ||
env: | ||
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} | ||
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} | ||
APP_URL: ${{ secrets.CLOUDFLARE_HONO_APP_URL }} | ||
CLOUDFLARE_PROJECT_NAME: ${{ secrets.CLOUDFLARE_HONO_PROJECT_NAME }} | ||
TEST_DEPLOYED_VERSION: true | ||
defaults: | ||
run: | ||
working-directory: examples/cloudflare-workers/with-be-emailpassword | ||
steps: | ||
- uses: actions/checkout@v2 | ||
- run: echo $GITHUB_SHA | ||
- run: npm install git+https://github.com:supertokens/supertokens-node.git#$GITHUB_SHA | ||
- run: npm install | ||
- run: npm install [email protected] [email protected] puppeteer@^11.0.0 isomorphic-fetch@^3.0.0 | ||
|
||
- name: Replace APP_URL with deployed URL value | ||
run: | | ||
sed -i "s|process.env.REACT_APP_API_URL|\"${{ env.APP_URL }}\"|" config.ts | ||
sed -i "s|process.env.REACT_APP_WEBSITE_URL|\"${{ env.APP_URL }}\"|" config.ts | ||
- name: Deploy the changes | ||
run: npx wrangler deploy --name ${{ env.CLOUDFLARE_PROJECT_NAME }} index.ts | ||
|
||
- name: Run tests | ||
run: | | ||
( \ | ||
(echo "=========== Test attempt 1 ===========" && npx mocha --no-config --timeout 80000 test/**/*.test.js) || \ | ||
(echo "=========== Test attempt 2 ===========" && npx mocha --no-config --timeout 80000 test/**/*.test.js) || \ | ||
(echo "=========== Test attempt 3 ===========" && npx mocha --no-config --timeout 80000 test/**/*.test.js) \ | ||
) | ||
- name: The job has failed | ||
if: ${{ failure() }} | ||
uses: actions/upload-artifact@v3 | ||
with: | ||
name: screenshots | ||
path: | | ||
./**/*screenshot.jpeg |
25 changes: 25 additions & 0 deletions
25
examples/cloudflare-workers/with-be-emailpassword/README.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
 | ||
|
||
# SuperTokens EmailPassword with Cloudflare Workers (HonoJS) on Edge Runtime | ||
|
||
This demo app uses HonoJS with Cloudflare Workers for the backend server. We use [Wrangler](https://developers.cloudflare.com/workers/wrangler/) in the backend server to simulate the Cloudflare Worker runtime. This is a pure Edge runtime implementation (works without `nodejs_compat` flag). | ||
|
||
## Project setup | ||
|
||
Clone the repo, enter the directory, and use `npm` to install the project dependencies: | ||
|
||
```bash | ||
git clone https://github.com/supertokens/supertokens-node | ||
cd supertokens-node/examples/cloudflare-workers/with-be-emailpassword | ||
npm install | ||
``` | ||
|
||
## Run the demo app | ||
|
||
This compiles and serves the React app and starts the backend API server on port 3001. | ||
|
||
```bash | ||
npm run start | ||
``` | ||
|
||
The app will start on `http://localhost:3000` |
33 changes: 33 additions & 0 deletions
33
examples/cloudflare-workers/with-be-emailpassword/config.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import EmailPassword from "supertokens-node/recipe/emailpassword"; | ||
import Session from "supertokens-node/recipe/session"; | ||
import { TypeInput } from "supertokens-node/types"; | ||
import process from "process"; | ||
|
||
export const runtime = "edge"; | ||
|
||
export function getApiDomain() { | ||
const apiPort = process.env.REACT_APP_API_PORT || 3001; | ||
const apiUrl = process.env.REACT_APP_API_URL || `http://localhost:${apiPort}`; | ||
return apiUrl; | ||
} | ||
|
||
export function getWebsiteDomain() { | ||
const websitePort = process.env.REACT_APP_WEBSITE_PORT || 3000; | ||
const websiteUrl = process.env.REACT_APP_WEBSITE_URL || `http://localhost:${websitePort}`; | ||
return websiteUrl; | ||
} | ||
|
||
export const SuperTokensConfig: TypeInput = { | ||
supertokens: { | ||
// this is the location of the SuperTokens core. | ||
connectionURI: "https://try.supertokens.com", | ||
}, | ||
appInfo: { | ||
appName: "SuperTokens Demo App", | ||
apiDomain: getApiDomain(), | ||
websiteDomain: getWebsiteDomain(), | ||
}, | ||
// recipeList contains all the modules that you want to | ||
// use from SuperTokens. See the full list here: https://supertokens.com/docs/guides | ||
recipeList: [EmailPassword.init(), Session.init()], | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import { SessionContainer } from "supertokens-node/recipe/session"; | ||
|
||
declare module "hono" { | ||
interface HonoRequest { | ||
session?: SessionContainer; | ||
} | ||
} |
41 changes: 41 additions & 0 deletions
41
examples/cloudflare-workers/with-be-emailpassword/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import { Hono } from "hono"; | ||
import { cors } from "hono/cors"; | ||
import supertokens from "supertokens-node"; | ||
import { middleware } from "./middleware"; | ||
import { getWebsiteDomain, SuperTokensConfig } from "./config"; | ||
import type { PageConfig } from "next"; | ||
|
||
export const config: PageConfig = { | ||
runtime: "edge", | ||
}; | ||
|
||
supertokens.init(SuperTokensConfig); | ||
|
||
const app = new Hono(); | ||
|
||
app.use("*", async (c, next) => { | ||
return await cors({ | ||
origin: getWebsiteDomain(), | ||
credentials: true, | ||
allowHeaders: ["Content-Type", ...supertokens.getAllCORSHeaders()], | ||
allowMethods: ["GET", "POST", "PUT", "PATCH", "DELETE"], | ||
})(c, next); | ||
}); | ||
|
||
// This exposes all the APIs from SuperTokens to the client. | ||
app.use("*", middleware()); | ||
|
||
// An example API that requires session verification | ||
app.get("/sessioninfo", (c) => { | ||
let session = c.req.session; | ||
if (!session) { | ||
return c.text("Unauthorized", 401); | ||
} | ||
return c.json({ | ||
sessionHandle: session!.getHandle(), | ||
userId: session!.getUserId(), | ||
accessTokenPayload: session!.getAccessTokenPayload(), | ||
}); | ||
}); | ||
|
||
export default app; |
88 changes: 88 additions & 0 deletions
88
examples/cloudflare-workers/with-be-emailpassword/middleware.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
import { Context, Next } from "hono"; | ||
import { getCookie } from "hono/cookie"; | ||
import { | ||
CollectingResponse, | ||
PreParsedRequest, | ||
middleware as customMiddleware, | ||
} from "supertokens-node/framework/custom"; | ||
import Session from "supertokens-node/recipe/session"; | ||
import { HTTPMethod } from "supertokens-node/types"; | ||
import { serialize } from "cookie"; | ||
|
||
export const runtime = "edge"; | ||
|
||
function setCookiesInHeaders(headers: Headers, cookies: CollectingResponse["cookies"]) { | ||
for (const cookie of cookies) { | ||
headers.append( | ||
"Set-Cookie", | ||
serialize(cookie.key, cookie.value, { | ||
domain: cookie.domain, | ||
expires: new Date(cookie.expires), | ||
httpOnly: cookie.httpOnly, | ||
path: cookie.path, | ||
sameSite: cookie.sameSite, | ||
secure: cookie.secure, | ||
}) | ||
); | ||
} | ||
} | ||
|
||
function copyHeaders(source: Headers, destination: Headers): void { | ||
for (const [key, value] of source.entries()) { | ||
destination.append(key, value); | ||
} | ||
} | ||
|
||
export const middleware = () => { | ||
return async function (c: Context, next: Next) { | ||
const request = new PreParsedRequest({ | ||
method: c.req.method as HTTPMethod, | ||
url: c.req.url, | ||
query: Object.fromEntries(new URL(c.req.url).searchParams.entries()), | ||
cookies: getCookie(c), | ||
headers: c.req.raw.headers as Headers, | ||
getFormBody: () => c.req.formData(), | ||
getJSONBody: () => c.req.json(), | ||
}); | ||
const baseResponse = new CollectingResponse(); | ||
|
||
const stMiddleware = customMiddleware(() => request); | ||
|
||
const { handled, error } = await stMiddleware(request, baseResponse); | ||
|
||
if (error) { | ||
throw error; | ||
} | ||
|
||
if (handled) { | ||
setCookiesInHeaders(baseResponse.headers, baseResponse.cookies); | ||
return new Response(baseResponse.body, { | ||
status: baseResponse.statusCode, | ||
headers: baseResponse.headers, | ||
}); | ||
} | ||
|
||
// Add session to c.req if it exists | ||
try { | ||
c.req.session = await Session.getSession(request, baseResponse, { | ||
sessionRequired: false, | ||
}); | ||
|
||
await next(); | ||
|
||
// Add cookies that were set by `getSession` to response | ||
setCookiesInHeaders(c.res.headers, baseResponse.cookies); | ||
// Copy headers that were set by `getSession` to response | ||
copyHeaders(baseResponse.headers, c.res.headers); | ||
return c.res; | ||
} catch (err) { | ||
if (Session.Error.isErrorFromSuperTokens(err)) { | ||
if (err.type === Session.Error.TRY_REFRESH_TOKEN || err.type === Session.Error.INVALID_CLAIMS) { | ||
return new Response("Unauthorized", { | ||
status: err.type === Session.Error.INVALID_CLAIMS ? 403 : 401, | ||
}); | ||
} | ||
} | ||
} | ||
}; | ||
}; |
Oops, something went wrong.