Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(astro): experimental middleware #6721

Merged
merged 55 commits into from
May 3, 2023
Merged
Show file tree
Hide file tree
Changes from 48 commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
e6f821b
feat(astro): experimental middleware
ematipico Mar 31, 2023
db4e21a
changeset
ematipico Mar 31, 2023
9d54c5d
chore: export sequence, load middleware as file
ematipico Apr 3, 2023
c4591a3
chore: format
ematipico Apr 4, 2023
2467e04
chore: don't throw an error for missing `onRequest`
ematipico Apr 4, 2023
1d9e277
feat: support middlewares in SSG for endpoints and pages
ematipico Apr 5, 2023
04e163b
feat: support middleware in SSR endpoints
ematipico Apr 11, 2023
24d8d39
chore: add tests
ematipico Apr 11, 2023
1fb83c0
chore: rename node middleware file
ematipico Apr 11, 2023
8d315ec
chore: fix middleware export
ematipico Apr 12, 2023
ad86e02
chore: fixes, feedback and more tests
ematipico Apr 13, 2023
9523589
fix: returning a new `Response` should work
ematipico Apr 13, 2023
97786ed
fix: use correct paths and experimental setting
ematipico Apr 18, 2023
6a445f3
fix: types
ematipico Apr 18, 2023
dcc013c
chore: change middleware specs
ematipico Apr 25, 2023
f862403
test: new cases and added experimental flag
ematipico Apr 25, 2023
f471f2d
fix: update bundled code
ematipico Apr 25, 2023
7b69bb1
fix: node import
ematipico Apr 25, 2023
bf16a3d
chore: more tests
ematipico Apr 25, 2023
6f1a04e
chore: do not use Node.js builtins
ematipico Apr 25, 2023
c821f5f
examples: middleware
ematipico Apr 25, 2023
f34c57a
add `--experimental-middleware`
ematipico Apr 25, 2023
a333904
fix type issue and try `Locals` expand
ematipico Apr 26, 2023
68ce642
cleanup
ematipico Apr 26, 2023
7b6c810
fix: type expansion
ematipico Apr 26, 2023
d3a0008
fix: regressions around endpoints
ematipico Apr 26, 2023
48417ee
fix: example type errors
ematipico Apr 26, 2023
596c7b1
fix: regression inside tests
ematipico Apr 26, 2023
d5c7a99
fix: global generation of `AstroMiddleware.Locals`
ematipico Apr 27, 2023
ee26bff
fix: sequence API
ematipico Apr 27, 2023
617c9fa
chore: changed the name of the global namespace
ematipico Apr 27, 2023
eba8c6e
chore: remove the unneeded import
ematipico Apr 28, 2023
dc83670
chore: split tests
ematipico Apr 28, 2023
49bcedd
chore: try manually
ematipico Apr 28, 2023
7770ff6
chore: try again?
ematipico Apr 28, 2023
35a8f6e
chore: skip middleware for simple endpoints
ematipico Apr 28, 2023
fb2d2bf
chore: skip SSR tests (for now)
ematipico Apr 28, 2023
42fb731
chore: update test adapter
ematipico May 2, 2023
2e65f42
fix: tweak plugin SSR to not leak warnings
ematipico May 2, 2023
9993b92
Apply suggestions from code review
ematipico May 2, 2023
bca1b6a
revert: change name
ematipico May 2, 2023
c509da6
refactor: call `renderPage` with props and params
ematipico May 2, 2023
f9ae85a
fix: middleware casting
ematipico May 2, 2023
b190c5e
chore: code suggestions
ematipico May 2, 2023
1daa109
revert: hook parameter rename
ematipico May 2, 2023
a97d9d9
refactor: move `props` and `params` inside `RenderContext`
ematipico May 3, 2023
bde4d69
Merge remote-tracking branch 'origin/main' into feat/middleware-api
ematipico May 3, 2023
f0e3f45
chore: address code suggestions
ematipico May 3, 2023
bbe4c22
Apply suggestions from code review
ematipico May 3, 2023
23f8aa1
Update packages/astro/src/core/errors/errors-data.ts
ematipico May 3, 2023
a211126
chore: fix message
ematipico May 3, 2023
95f2248
Update packages/astro/src/core/errors/errors-data.ts
ematipico May 3, 2023
d45d8cb
chore: code suggestion
ematipico May 3, 2023
5835f57
Merge remote-tracking branch 'origin/main' into feat/middleware-api
ematipico May 3, 2023
e8e5007
chore: update lock file
ematipico May 3, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/pretty-bears-deliver.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'astro': minor
---

New middleware API
13 changes: 13 additions & 0 deletions examples/middleware/astro.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { defineConfig } from 'astro/config';
import node from '@astrojs/node';

// https://astro.build/config
export default defineConfig({
output: 'server',
adapter: node({
mode: 'standalone',
}),
experimental: {
middleware: true,
},
});
23 changes: 23 additions & 0 deletions examples/middleware/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"name": "@example/middleware",
"type": "module",
"version": "0.0.1",
"private": true,
"scripts": {
"dev": "astro dev",
"start": "astro dev",
"build": "astro build",
"preview": "astro preview",
"astro": "astro",
"server": "node dist/server/entry.mjs"
},
"dependencies": {
"astro": "workspace:*",
"svelte": "^3.48.0",
"@astrojs/node": "workspace:*",
"concurrently": "^7.2.1",
"unocss": "^0.15.6",
"vite-imagetools": "^4.0.4",
"html-minifier": "^4.0.0"
}
}
63 changes: 63 additions & 0 deletions examples/middleware/src/components/Card.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
---
export interface Props {
title: string;
body: string;
href: string;
}

const { href, title, body } = Astro.props;
---

<li class="link-card">
<a href={href}>
<h2>
{title}
<span>&rarr;</span>
</h2>
<p>
{body}
</p>
</a>
</li>
<style>
.link-card {
list-style: none;
display: flex;
padding: 0.25rem;
background-color: white;
background-image: none;
background-size: 400%;
border-radius: 0.6rem;
background-position: 100%;
transition: background-position 0.6s cubic-bezier(0.22, 1, 0.36, 1);
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1);
}

.link-card > a {
width: 100%;
text-decoration: none;
line-height: 1.4;
padding: 1rem 1.3rem;
border-radius: 0.35rem;
color: #111;
background-color: white;
opacity: 0.8;
}
h2 {
margin: 0;
font-size: 1.25rem;
transition: color 0.6s cubic-bezier(0.22, 1, 0.36, 1);
}
p {
margin-top: 0.5rem;
margin-bottom: 0;
color: #444;
}
.link-card:is(:hover, :focus-within) {
background-position: 0;
background-image: var(--accent-gradient);
}
.link-card:is(:hover, :focus-within) h2 {
color: rgb(var(--accent));
}
</style>
13 changes: 13 additions & 0 deletions examples/middleware/src/env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/// <reference types="astro/client" />
declare global {
namespace AstroMiddleware {
interface Locals {
user: {
name: string;
surname: string;
};
}
}
}

export {};
35 changes: 35 additions & 0 deletions examples/middleware/src/layouts/Layout.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
---
export interface Props {
title: string;
}

const { title } = Astro.props;
---

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="generator" content={Astro.generator} />
<title>{title}</title>
</head>
<body>
<slot />
</body>
</html>
<style is:global>
:root {
--accent: 124, 58, 237;
--accent-gradient: linear-gradient(45deg, rgb(var(--accent)), #da62c4 30%, white 60%);
}
html {
font-family: system-ui, sans-serif;
background-color: #f6f6f6;
}
code {
font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono,
Bitstream Vera Sans Mono, Courier New, monospace;
}
</style>
71 changes: 71 additions & 0 deletions examples/middleware/src/middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { defineMiddleware, sequence } from 'astro/middleware';
import htmlMinifier from 'html-minifier';

const limit = 50;

const loginInfo = {
token: undefined,
currentTime: undefined,
};

export const minifier = defineMiddleware(async (context, next) => {
const response = await next();
// check if the response is returning some HTML
if (response.headers.get('content-type') === 'text/html') {
let headers = response.headers;
let html = await response.text();
let newHtml = htmlMinifier.minify(html, {
removeAttributeQuotes: true,
collapseWhitespace: true,
});
return new Response(newHtml, {
status: 200,
headers,
});
}
return response;
});

const validation = defineMiddleware(async (context, next) => {
if (context.request.url.endsWith('/admin')) {
if (loginInfo.currentTime) {
const difference = new Date().getTime() - loginInfo.currentTime;
if (difference > limit) {
console.log('hit threshold');
loginInfo.token = undefined;
loginInfo.currentTime = undefined;
return context.redirect('/login');
}
}
// we naively check if we have a token
if (loginInfo.token && loginInfo.token === 'loggedIn') {
// we fill the locals with user-facing information
context.locals.user = {
name: 'AstroUser',
surname: 'AstroSurname',
};
return await next();
} else {
loginInfo.token = undefined;
loginInfo.currentTime = undefined;
return context.redirect('/login');
}
} else if (context.request.url.endsWith('/api/login')) {
const response = await next();
// the login endpoint will return to us a JSON with username and password
const data = await response.json();
// we naively check if username and password are equals to some string
if (data.username === 'astro' && data.password === 'astro') {
// we store the token somewhere outside of locals because the `locals` object is attached to the request
// and when doing a redirect, we lose that information
loginInfo.token = 'loggedIn';
loginInfo.currentTime = new Date().getTime();
return context.redirect('/admin');
}
}
// we don't really care about awaiting the response in this case
next();
return;
});

export const onRequest = sequence(validation, minifier);
55 changes: 55 additions & 0 deletions examples/middleware/src/pages/admin.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
---
import Layout from '../layouts/Layout.astro';
const user = Astro.locals.user;
---

<Layout title="Welcome back!!">
<main>
<h1>Welcome back <span class="text-gradient">{user.name} {user.surname}</span></h1>
</main>
</Layout>

<style>
main {
margin: auto;
padding: 1.5rem;
max-width: 60ch;
}
h1 {
font-size: 3rem;
font-weight: 800;
margin: 0;
}
.text-gradient {
background-image: var(--accent-gradient);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-size: 400%;
background-position: 0%;
}
.instructions {
line-height: 1.6;
margin: 1rem 0;
border: 1px solid rgba(var(--accent), 25%);
background-color: white;
padding: 1rem;
border-radius: 0.4rem;
}
.instructions code {
font-size: 0.875em;
font-weight: bold;
background: rgba(var(--accent), 12%);
color: rgb(var(--accent));
border-radius: 4px;
padding: 0.3em 0.45em;
}
.instructions strong {
color: rgb(var(--accent));
}
.link-card-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(24ch, 1fr));
gap: 1rem;
padding: 0;
}
</style>
18 changes: 18 additions & 0 deletions examples/middleware/src/pages/api/login.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { APIRoute } from 'astro';

export const post: APIRoute = async ({ request }) => {
const data = await request.formData();
const username = data.get('username');
const password = data.get('password');
return new Response(
JSON.stringify({
username,
password,
}),
{
headers: {
'content-type': 'application/json',
},
}
);
};
63 changes: 63 additions & 0 deletions examples/middleware/src/pages/index.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
---
import Layout from '../layouts/Layout.astro';
import Card from '../components/Card.astro';
---

<Layout title="Welcome to Astro.">
<main>
<h1>Welcome to <span class="text-gradient">Astro</span></h1>
<p class="instructions">
To get started, open the directory <code>src/pages</code> in your project.<br />
<strong>Code Challenge:</strong> Tweak the "Welcome to Astro" message above.
</p>
{}
<ul role="list" class="link-card-grid">
<Card href="/login" title="Login" body="Try the login" />
</ul>
</main>
</Layout>

<style>
main {
margin: auto;
padding: 1.5rem;
max-width: 60ch;
}
h1 {
font-size: 3rem;
font-weight: 800;
margin: 0;
}
.text-gradient {
background-image: var(--accent-gradient);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-size: 400%;
background-position: 0%;
}
.instructions {
line-height: 1.6;
margin: 1rem 0;
border: 1px solid rgba(var(--accent), 25%);
background-color: white;
padding: 1rem;
border-radius: 0.4rem;
}
.instructions code {
font-size: 0.875em;
font-weight: bold;
background: rgba(var(--accent), 12%);
color: rgb(var(--accent));
border-radius: 4px;
padding: 0.3em 0.45em;
}
.instructions strong {
color: rgb(var(--accent));
}
.link-card-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(24ch, 1fr));
gap: 1rem;
padding: 0;
}
</style>
Loading