diff --git a/examples/nextjs-14-app-dir-validate-email/.eslintrc.json b/examples/nextjs-14-app-dir-validate-email/.eslintrc.json new file mode 100644 index 000000000..bffb357a7 --- /dev/null +++ b/examples/nextjs-14-app-dir-validate-email/.eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": "next/core-web-vitals" +} diff --git a/examples/nextjs-14-app-dir-validate-email/.gitignore b/examples/nextjs-14-app-dir-validate-email/.gitignore new file mode 100644 index 000000000..fd3dbb571 --- /dev/null +++ b/examples/nextjs-14-app-dir-validate-email/.gitignore @@ -0,0 +1,36 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js +.yarn/install-state.gz + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env*.local + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/examples/nextjs-14-app-dir-validate-email/README.md b/examples/nextjs-14-app-dir-validate-email/README.md new file mode 100644 index 000000000..76483ae7d --- /dev/null +++ b/examples/nextjs-14-app-dir-validate-email/README.md @@ -0,0 +1,30 @@ + + + + Arcjet Logo + + + +# Arcjet email verification with Next.js 14 using the App Router + +This example shows how to use Arcjet with a Next.js [route +handler](https://nextjs.org/docs/app/building-your-application/routing/route-handlers). + +## How to use + +1. From the root of the project, install the dependencies. + + ```bash + npm ci + ``` + +2. Enter this directory and start the dev server. + + ```bash + cd examples/nextjs-14-app-dir-validate-email + npm run dev + ``` + +3. Visit `http://localhost:3000/api/arcjet`. +4. Refresh the page to see the email verification fail due to no MX records + existing on the domain. diff --git a/examples/nextjs-14-app-dir-validate-email/app/api/arcjet/route.ts b/examples/nextjs-14-app-dir-validate-email/app/api/arcjet/route.ts new file mode 100644 index 000000000..05f95d49d --- /dev/null +++ b/examples/nextjs-14-app-dir-validate-email/app/api/arcjet/route.ts @@ -0,0 +1,33 @@ +import arcjet, { validateEmail } from "@arcjet/next"; +import { NextResponse } from "next/server"; + +const aj = arcjet({ + key: "ajkey_yourkey", + rules: [ + validateEmail({ + mode: "LIVE", + block: ["NO_MX_RECORDS"], + }), + ], +}); + +export async function GET(req: Request) { + const decision = await aj.protect(req, { + email: "test@arcjet.co", + }); + + if (decision.isDenied()) { + return NextResponse.json( + { + error: "Forbidden", + }, + { + status: 403, + }, + ); + } + + return NextResponse.json({ + message: "Hello world", + }); +} diff --git a/examples/nextjs-14-app-dir-validate-email/app/favicon.ico b/examples/nextjs-14-app-dir-validate-email/app/favicon.ico new file mode 100644 index 000000000..718d6fea4 Binary files /dev/null and b/examples/nextjs-14-app-dir-validate-email/app/favicon.ico differ diff --git a/examples/nextjs-14-app-dir-validate-email/app/globals.css b/examples/nextjs-14-app-dir-validate-email/app/globals.css new file mode 100644 index 000000000..fd81e8858 --- /dev/null +++ b/examples/nextjs-14-app-dir-validate-email/app/globals.css @@ -0,0 +1,27 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +:root { + --foreground-rgb: 0, 0, 0; + --background-start-rgb: 214, 219, 220; + --background-end-rgb: 255, 255, 255; +} + +@media (prefers-color-scheme: dark) { + :root { + --foreground-rgb: 255, 255, 255; + --background-start-rgb: 0, 0, 0; + --background-end-rgb: 0, 0, 0; + } +} + +body { + color: rgb(var(--foreground-rgb)); + background: linear-gradient( + to bottom, + transparent, + rgb(var(--background-end-rgb)) + ) + rgb(var(--background-start-rgb)); +} diff --git a/examples/nextjs-14-app-dir-validate-email/app/layout.tsx b/examples/nextjs-14-app-dir-validate-email/app/layout.tsx new file mode 100644 index 000000000..323bd9c95 --- /dev/null +++ b/examples/nextjs-14-app-dir-validate-email/app/layout.tsx @@ -0,0 +1,22 @@ +import type { Metadata } from "next"; +import { Inter } from "next/font/google"; +import "./globals.css"; + +const inter = Inter({ subsets: ["latin"] }); + +export const metadata: Metadata = { + title: "Create Next App", + description: "Generated by create next app", +}; + +export default function RootLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( + + {children} + + ); +} diff --git a/examples/nextjs-14-app-dir-validate-email/app/page.tsx b/examples/nextjs-14-app-dir-validate-email/app/page.tsx new file mode 100644 index 000000000..f870fe109 --- /dev/null +++ b/examples/nextjs-14-app-dir-validate-email/app/page.tsx @@ -0,0 +1,113 @@ +import Image from "next/image"; + +export default function Home() { + return ( +
+
+

+ Get started by editing  + app/page.tsx +

+
+ + By{" "} + Vercel Logo + +
+
+ +
+ Next.js Logo +
+ +
+ +

+ Docs{" "} + + -> + +

+

+ Find in-depth information about Next.js features and API. +

+
+ + +

+ Learn{" "} + + -> + +

+

+ Learn about Next.js in an interactive course with quizzes! +

+
+ + +

+ Templates{" "} + + -> + +

+

+ Explore starter templates for Next.js. +

+
+ + +

+ Deploy{" "} + + -> + +

+

+ Instantly deploy your Next.js site to a shareable URL with Vercel. +

+
+
+
+ ); +} diff --git a/examples/nextjs-14-app-dir-validate-email/next.config.js b/examples/nextjs-14-app-dir-validate-email/next.config.js new file mode 100644 index 000000000..658404ac6 --- /dev/null +++ b/examples/nextjs-14-app-dir-validate-email/next.config.js @@ -0,0 +1,4 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = {}; + +module.exports = nextConfig; diff --git a/examples/nextjs-14-app-dir-validate-email/package.json b/examples/nextjs-14-app-dir-validate-email/package.json new file mode 100644 index 000000000..8ef213f01 --- /dev/null +++ b/examples/nextjs-14-app-dir-validate-email/package.json @@ -0,0 +1,28 @@ +{ + "name": "nextjs-14-app-dir-validate-email", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "next lint" + }, + "dependencies": { + "@arcjet/next": "*", + "react": "^18", + "react-dom": "^18", + "next": "14.0.4" + }, + "devDependencies": { + "typescript": "^5", + "@types/node": "^20", + "@types/react": "^18", + "@types/react-dom": "^18", + "autoprefixer": "^10.0.1", + "postcss": "^8", + "tailwindcss": "^3.3.0", + "eslint": "^8", + "eslint-config-next": "14.0.4" + } +} diff --git a/examples/nextjs-14-app-dir-validate-email/postcss.config.js b/examples/nextjs-14-app-dir-validate-email/postcss.config.js new file mode 100644 index 000000000..12a703d90 --- /dev/null +++ b/examples/nextjs-14-app-dir-validate-email/postcss.config.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/examples/nextjs-14-app-dir-validate-email/public/next.svg b/examples/nextjs-14-app-dir-validate-email/public/next.svg new file mode 100644 index 000000000..db773047f --- /dev/null +++ b/examples/nextjs-14-app-dir-validate-email/public/next.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/examples/nextjs-14-app-dir-validate-email/public/vercel.svg b/examples/nextjs-14-app-dir-validate-email/public/vercel.svg new file mode 100644 index 000000000..4bc795bda --- /dev/null +++ b/examples/nextjs-14-app-dir-validate-email/public/vercel.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/examples/nextjs-14-app-dir-validate-email/tailwind.config.ts b/examples/nextjs-14-app-dir-validate-email/tailwind.config.ts new file mode 100644 index 000000000..7e4bd91a0 --- /dev/null +++ b/examples/nextjs-14-app-dir-validate-email/tailwind.config.ts @@ -0,0 +1,20 @@ +import type { Config } from "tailwindcss"; + +const config: Config = { + content: [ + "./pages/**/*.{js,ts,jsx,tsx,mdx}", + "./components/**/*.{js,ts,jsx,tsx,mdx}", + "./app/**/*.{js,ts,jsx,tsx,mdx}", + ], + theme: { + extend: { + backgroundImage: { + "gradient-radial": "radial-gradient(var(--tw-gradient-stops))", + "gradient-conic": + "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))", + }, + }, + }, + plugins: [], +}; +export default config; diff --git a/examples/nextjs-14-app-dir-validate-email/tsconfig.json b/examples/nextjs-14-app-dir-validate-email/tsconfig.json new file mode 100644 index 000000000..c71469637 --- /dev/null +++ b/examples/nextjs-14-app-dir-validate-email/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "target": "es5", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": ["./*"] + } + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "exclude": ["node_modules"] +} diff --git a/package-lock.json b/package-lock.json index 4cb242671..b4cbe4253 100644 --- a/package-lock.json +++ b/package-lock.json @@ -352,6 +352,35 @@ } } }, + "examples/nextjs-14-app-dir-validate-email": { + "version": "0.1.0", + "dependencies": { + "@arcjet/next": "*", + "next": "14.0.4", + "react": "^18", + "react-dom": "^18" + }, + "devDependencies": { + "@types/node": "^20", + "@types/react": "^18", + "@types/react-dom": "^18", + "autoprefixer": "^10.0.1", + "eslint": "^8", + "eslint-config-next": "14.0.4", + "postcss": "^8", + "tailwindcss": "^3.3.0", + "typescript": "^5" + } + }, + "examples/nextjs-14-app-dir-validate-email/node_modules/@types/node": { + "version": "20.10.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.4.tgz", + "integrity": "sha512-D08YG6rr8X90YB56tSIuBaddy/UXAA9RKJoFvrsnogAum/0pmjkgi4+2nx96A330FmioegBWmEYQ+syqCFaveg==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, "examples/nextjs-14-pages-wrap": { "version": "0.1.0", "dependencies": { @@ -6315,6 +6344,10 @@ "resolved": "examples/nextjs-14-app-dir-rl", "link": true }, + "node_modules/nextjs-14-app-dir-validate-email": { + "resolved": "examples/nextjs-14-app-dir-validate-email", + "link": true + }, "node_modules/nextjs-14-pages-wrap": { "resolved": "examples/nextjs-14-pages-wrap", "link": true