From df1a3dff12a1d0e5f39679bd2179ebbad16bfa60 Mon Sep 17 00:00:00 2001 From: kaow-dev-2024 Date: Tue, 7 Apr 2026 11:36:39 +0700 Subject: [PATCH] . --- .env.local.example | 27 +- .github/FUNDING.yml | 4 - .gitignore | 23 +- .vscode/settings.json | 31 + Dockerfile | 58 - LICENSE | 15 - README.md | 136 +- app/[...proxy]/route.tsx | 34 - app/admin/dashboard/page.tsx | 1 + app/admin/layout.tsx | 9 + app/admin/reports/page.tsx | 1 + app/admin/users/page.tsx | 1 + app/admin/verify/page.tsx | 1 + app/api-example/page.tsx | 38 - app/api/auth/[...nextauth]/route.ts | 6 + app/api/protected/route.ts | 9 - app/auth/[...nextauth]/route.ts | 2 - app/buyer/dashboard/page.tsx | 1 + app/buyer/deliveries/page.tsx | 1 + app/buyer/layout.tsx | 9 + app/buyer/market/page.tsx | 1 + app/buyer/orders/page.tsx | 1 + app/client-example/page.tsx | 22 - app/delivery/dashboard/page.tsx | 1 + app/delivery/history/page.tsx | 1 + app/delivery/layout.tsx | 11 + app/delivery/routes/page.tsx | 1 + app/delivery/tasks/page.tsx | 1 + app/farmer/dashboard/page.tsx | 1 + app/farmer/insights/page.tsx | 1 + app/farmer/inventory/page.tsx | 1 + app/farmer/layout.tsx | 9 + app/farmer/orders/page.tsx | 1 + app/favicon.ico | Bin 2191 -> 0 bytes app/globals.css | 50 +- app/layout.tsx | 35 +- app/middleware-example/page.tsx | 20 - app/page.tsx | 518 +- app/policy/page.tsx | 32 - app/server-example/page.tsx | 24 - auth.ts | 137 - components.json | 16 - components/access-denied.tsx | 20 - components/auth-components.tsx | 34 - components/client-example.tsx | 110 - components/custom-link.tsx | 43 - components/footer.module.css | 14 - components/footer.tsx | 29 - components/header.module.css | 92 - components/header.tsx | 13 - components/layout.tsx | 13 - components/main-nav.tsx | 91 - components/providers.tsx | 29 + components/register-credentials-form.tsx | 259 + components/register-role-stepper.tsx | 498 ++ components/role-folder-layout.tsx | 66 + components/role-user-layout.tsx | 528 ++ components/session-data.tsx | 38 - components/ui/avatar.tsx | 50 - components/ui/button.tsx | 56 - components/ui/dropdown-menu.tsx | 200 - components/ui/input.tsx | 25 - components/ui/navigation-menu.tsx | 128 - components/user-button.tsx | 53 - docker-compose.yml | 13 - lib/auth.ts | 292 + lib/axios.ts | 13 + lib/utils.ts | 6 - middleware.ts | 11 - next-env.d.ts | 3 +- next.config.js | 4 - next.config.mjs | 6 + package-lock.json | 7234 ++++++++++++++++++++++ package.json | 65 +- postcss.config.js | 6 +- public/brand/kuku-market.png | Bin 0 -> 1704627 bytes public/logo.png | Bin 391267 -> 0 bytes tailwind.config.js | 76 - tailwind.config.ts | 15 + test-docker.sh | 30 - tsconfig.json | 23 +- tsconfig.tsbuildinfo | 1 + types/next-auth.d.ts | 40 + 83 files changed, 9657 insertions(+), 1861 deletions(-) delete mode 100644 .github/FUNDING.yml create mode 100644 .vscode/settings.json delete mode 100644 Dockerfile delete mode 100644 LICENSE delete mode 100644 app/[...proxy]/route.tsx create mode 100644 app/admin/dashboard/page.tsx create mode 100644 app/admin/layout.tsx create mode 100644 app/admin/reports/page.tsx create mode 100644 app/admin/users/page.tsx create mode 100644 app/admin/verify/page.tsx delete mode 100644 app/api-example/page.tsx create mode 100644 app/api/auth/[...nextauth]/route.ts delete mode 100644 app/api/protected/route.ts delete mode 100644 app/auth/[...nextauth]/route.ts create mode 100644 app/buyer/dashboard/page.tsx create mode 100644 app/buyer/deliveries/page.tsx create mode 100644 app/buyer/layout.tsx create mode 100644 app/buyer/market/page.tsx create mode 100644 app/buyer/orders/page.tsx delete mode 100644 app/client-example/page.tsx create mode 100644 app/delivery/dashboard/page.tsx create mode 100644 app/delivery/history/page.tsx create mode 100644 app/delivery/layout.tsx create mode 100644 app/delivery/routes/page.tsx create mode 100644 app/delivery/tasks/page.tsx create mode 100644 app/farmer/dashboard/page.tsx create mode 100644 app/farmer/insights/page.tsx create mode 100644 app/farmer/inventory/page.tsx create mode 100644 app/farmer/layout.tsx create mode 100644 app/farmer/orders/page.tsx delete mode 100644 app/favicon.ico delete mode 100644 app/middleware-example/page.tsx delete mode 100644 app/policy/page.tsx delete mode 100644 app/server-example/page.tsx delete mode 100644 auth.ts delete mode 100644 components.json delete mode 100644 components/access-denied.tsx delete mode 100644 components/auth-components.tsx delete mode 100644 components/client-example.tsx delete mode 100644 components/custom-link.tsx delete mode 100644 components/footer.module.css delete mode 100644 components/footer.tsx delete mode 100644 components/header.module.css delete mode 100644 components/header.tsx delete mode 100644 components/layout.tsx delete mode 100644 components/main-nav.tsx create mode 100644 components/providers.tsx create mode 100644 components/register-credentials-form.tsx create mode 100644 components/register-role-stepper.tsx create mode 100644 components/role-folder-layout.tsx create mode 100644 components/role-user-layout.tsx delete mode 100644 components/session-data.tsx delete mode 100644 components/ui/avatar.tsx delete mode 100644 components/ui/button.tsx delete mode 100644 components/ui/dropdown-menu.tsx delete mode 100644 components/ui/input.tsx delete mode 100644 components/ui/navigation-menu.tsx delete mode 100644 components/user-button.tsx delete mode 100644 docker-compose.yml create mode 100644 lib/auth.ts create mode 100644 lib/axios.ts delete mode 100644 lib/utils.ts delete mode 100644 middleware.ts delete mode 100644 next.config.js create mode 100644 next.config.mjs create mode 100644 package-lock.json create mode 100644 public/brand/kuku-market.png delete mode 100644 public/logo.png delete mode 100644 tailwind.config.js create mode 100644 tailwind.config.ts delete mode 100755 test-docker.sh create mode 100644 tsconfig.tsbuildinfo create mode 100644 types/next-auth.d.ts diff --git a/.env.local.example b/.env.local.example index b754b2f5..11245f53 100644 --- a/.env.local.example +++ b/.env.local.example @@ -1,21 +1,6 @@ -AUTH_SECRET= # `npx auth secret` or `openssl rand -hex 32` - -AUTH_AUTH0_ID= -AUTH_AUTH0_SECRET= -AUTH_AUTH0_ISSUER= - -AUTH_FACEBOOK_ID= -AUTH_FACEBOOK_SECRET= - -AUTH_GITHUB_ID= -AUTH_GITHUB_SECRET= - -AUTH_GOOGLE_ID= -AUTH_GOOGLE_SECRET= - -AUTH_TWITTER_ID= -AUTH_TWITTER_SECRET= - -# THIRD_PARTY_API_EXAMPLE_BACKEND= # Read more at https://authjs.dev/guides/integrating-third-party-backends - -# AUTH_TRUST_HOST=1 # Read more at https://authjs.dev/getting-started/deployment#auth_trust_host \ No newline at end of file +NEXT_PUBLIC_BASE_API=http://localhost:5001/api/v1 +NEXTAUTH_URL=http://localhost:3000 +NEXTAUTH_SECRET=replace-with-a-long-random-secret +GOOGLE_CLIENT_ID=your-google-client-id +GOOGLE_CLIENT_SECRET=your-google-client-secret +AUTH_SOCIAL_GOOGLE_ENDPOINT=/auth/oauth/google diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml deleted file mode 100644 index 813b8ad3..00000000 --- a/.github/FUNDING.yml +++ /dev/null @@ -1,4 +0,0 @@ -# https://docs.github.com/en/github/administering-a-repository/displaying-a-sponsor-button-in-your-repository - -open_collective: nextauth -github: [balazsorban44, ThangHuuVu] diff --git a/.gitignore b/.gitignore index e6b58d6c..6d8b69a6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,20 +1,9 @@ -.DS_Store - -node_modules/ -logs -*.log +node_modules +.next +out +.env.local +.env npm-debug.log* yarn-debug.log* yarn-error.log* -lerna-debug.log* -.yarn-integrity -.npm - -.eslintcache - -*.tsbuildinfo -next-env.d.ts - -.next -.vercel -.env*.local \ No newline at end of file +pnpm-debug.log* diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..0ffd86b9 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,31 @@ +{ + "workbench.colorCustomizations": { + "statusBarItem.warningBackground": "#5edb0a", + "statusBarItem.warningForeground": "#000000", + "statusBarItem.warningHoverBackground": "#5edb0a", + "statusBarItem.warningHoverForeground": "#00000090", + "statusBarItem.remoteBackground": "#6be817", + "statusBarItem.remoteForeground": "#000000", + "statusBarItem.remoteHoverBackground": "#78f524", + "statusBarItem.remoteHoverForeground": "#00000090", + "statusBar.background": "#5edb0a", + "statusBar.foreground": "#000000", + "statusBar.border": "#5edb0a", + "statusBar.debuggingBackground": "#5edb0a", + "statusBar.debuggingForeground": "#000000", + "statusBar.debuggingBorder": "#5edb0a", + "statusBar.noFolderBackground": "#5edb0a", + "statusBar.noFolderForeground": "#000000", + "statusBar.noFolderBorder": "#5edb0a", + "statusBar.prominentBackground": "#5edb0a", + "statusBar.prominentForeground": "#000000", + "statusBar.prominentHoverBackground": "#5edb0a", + "statusBar.prominentHoverForeground": "#00000090", + "titleBar.activeBackground": "#5edb0a", + "titleBar.activeForeground": "#000000", + "titleBar.inactiveBackground": "#5edb0a", + "titleBar.inactiveForeground": "#00000090" + }, + "window.title": "frontend", + "editor.formatOnSave": true +} \ No newline at end of file diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 52e84d19..00000000 --- a/Dockerfile +++ /dev/null @@ -1,58 +0,0 @@ -# syntax=docker/dockerfile:1 -FROM node:20-alpine AS base - -# Install dependencies only when needed -FROM base AS deps -# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed. -RUN apk add --no-cache libc6-compat -WORKDIR /app - -# Install dependencies -COPY package.json pnpm-lock.yaml* ./ -RUN corepack enable pnpm && pnpm i --frozen-lockfile - -# Rebuild the source code only when needed -FROM base AS builder -WORKDIR /app -COPY --from=deps /app/node_modules ./node_modules -COPY . . - -# Next.js collects completely anonymous telemetry data about general usage. -# Learn more here: https://nextjs.org/telemetry -# Uncomment the following line in case you want to disable telemetry during the build. -# ENV NEXT_TELEMETRY_DISABLED 1 - -RUN corepack enable pnpm && pnpm build - -# Production image, copy all the files and run next -FROM base AS runner -WORKDIR /app - -ENV NODE_ENV production -# Uncomment the following line in case you want to disable telemetry during runtime. -# ENV NEXT_TELEMETRY_DISABLED 1 - -RUN addgroup --system --gid 1001 nodejs -RUN adduser --system --uid 1001 nextjs - -COPY --from=builder /app/public ./public - -# Set the correct permission for prerender cache -RUN mkdir .next -RUN chown nextjs:nodejs .next - -# Automatically leverage output traces to reduce image size -# https://nextjs.org/docs/advanced-features/output-file-tracing -COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ -COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static - -USER nextjs - -EXPOSE 3000 - -ENV PORT 3000 -ENV HOSTNAME "0.0.0.0" - -# server.js is created by next build from the standalone output -# https://nextjs.org/docs/pages/api-reference/next-config-js/output -CMD ["node", "server.js"] diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 360d3576..00000000 --- a/LICENSE +++ /dev/null @@ -1,15 +0,0 @@ -ISC License - -Copyright (c) 2022-2024, Balázs Orbán - -Permission to use, copy, modify, and/or distribute this software for any -purpose with or without fee is hereby granted, provided that the above -copyright notice and this permission notice appear in all copies. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/README.md b/README.md index 003116af..93e231b8 100644 --- a/README.md +++ b/README.md @@ -1,105 +1,31 @@ -> The example repository is maintained from a [monorepo](https://github.com/nextauthjs/next-auth/tree/main/apps/examples/nextjs). Pull Requests should be opened against [`nextauthjs/next-auth`](https://github.com/nextauthjs/next-auth). - -

-
- -

NextAuth.js Example App

-

- Open Source. Full Stack. Own Your Data. -

-

- - npm - - - Bundle Size - - - Downloads - - - TypeScript - -

-

- -## Overview - -NextAuth.js is a complete open source authentication solution. - -This is an example application that shows how `next-auth` is applied to a basic Next.js app. - -The deployed version can be found at [`next-auth-example.vercel.app`](https://next-auth-example.vercel.app) - -### About NextAuth.js - -NextAuth.js is an easy to implement, full-stack (client/server) open source authentication library originally designed for [Next.js](https://nextjs.org) and [Serverless](https://vercel.com). Our goal is to [support even more frameworks](https://github.com/nextauthjs/next-auth/issues/2294) in the future. - -Go to [next-auth.js.org](https://authjs.dev) for more information and documentation. - -> _NextAuth.js is not officially associated with Vercel or Next.js._ - -## Getting Started - -### 1. Clone the repository and install dependencies - -``` -git clone https://github.com/nextauthjs/next-auth-example.git -cd next-auth-example -pnpm install -``` - -### 2. Configure your local environment - -Copy the .env.local.example file in this directory to .env.local (which will be ignored by Git): - -``` -cp .env.local.example .env.local -``` - -Add details for one or more providers (e.g. Google, Twitter, GitHub, Email, etc). - -#### Database - -A database is needed to persist user accounts and to support email sign in. However, you can still use NextAuth.js for authentication without a database by using OAuth for authentication. If you do not specify a database, [JSON Web Tokens](https://jwt.io/introduction) will be enabled by default. - -You **can** skip configuring a database and come back to it later if you want. - -For more information about setting up a database, please check out the following links: - -- Docs: [authjs.dev/reference/core/adapters](https://authjs.dev/reference/core/adapters) - -### 3. Configure Authentication Providers - -1. Review and update options in `auth.ts` as needed. - -2. When setting up OAuth, in the developer admin page for each of your OAuth services, you should configure the callback URL to use a callback path of `{server}/api/auth/callback/{provider}`. - -e.g. For Google OAuth you would use: `http://localhost:3000/api/auth/callback/google` - -A list of configured providers and their callback URLs is available from the endpoint `api/auth/providers`. You can find more information at https://authjs.dev/getting-started/providers/oauth-tutorial - -1. You can also choose to specify an SMTP server for passwordless sign in via email. - -### 4. Start the application - -To run your site locally, use: - -``` -pnpm run dev -``` - -To run it in production mode, use: - -``` -pnpm run build -pnpm run start -``` - -### 5. Preparing for Production - -Follow the [Deployment documentation](https://authjs.dev/getting-started/deployment) - -## License - -ISC +## Auth Client (Next.js + Tailwind + MUI + NextAuth + Axios) + +### Providers +- Credentials +- Google + +### API base URL +- `NEXT_PUBLIC_BASE_API=http://localhost:5001/api/v1` + +### Setup +1. Install dependencies: + ```bash + npm install + ``` +2. Create env file: + ```bash + cp .env.local.example .env.local + ``` +3. Run dev server: + ```bash + npm run dev + ``` + +### Notes +- Credentials provider posts to `/auth/login` on your configured base API. +- NextAuth endpoint is `/api/auth/[...nextauth]`. +- `GOOGLE_CLIENT_SECRET` must be the OAuth Client Secret from Google Cloud Console credentials, not an access token (tokens often start with `ya29.`). +- Social login flow (Google): + - After OAuth callback, frontend exchanges Google tokens with backend via: + - `AUTH_SOCIAL_GOOGLE_ENDPOINT` (default: `/auth/oauth/google`) + - Backend should return app user + app access token in one of these shapes: `{ token, data }`, `{ accessToken, user }`, or equivalent. diff --git a/app/[...proxy]/route.tsx b/app/[...proxy]/route.tsx deleted file mode 100644 index bd16f70e..00000000 --- a/app/[...proxy]/route.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { auth } from "@/auth" -import { NextRequest } from "next/server" - -// Review if we need this, and why -function stripContentEncoding(result: Response) { - const responseHeaders = new Headers(result.headers) - responseHeaders.delete("content-encoding") - - return new Response(result.body, { - status: result.status, - statusText: result.statusText, - headers: responseHeaders, - }) -} - -async function handler(request: NextRequest) { - const session = await auth() - - const headers = new Headers(request.headers) - headers.set("Authorization", `Bearer ${session?.accessToken}`) - - let backendUrl = - process.env.THIRD_PARTY_API_EXAMPLE_BACKEND ?? - "https://third-party-backend.authjs.dev" - - let url = request.nextUrl.href.replace(request.nextUrl.origin, backendUrl) - let result = await fetch(url, { headers, body: request.body }) - - return stripContentEncoding(result) -} - -export const dynamic = "force-dynamic" - -export { handler as GET, handler as POST } diff --git a/app/admin/dashboard/page.tsx b/app/admin/dashboard/page.tsx new file mode 100644 index 00000000..47e61858 --- /dev/null +++ b/app/admin/dashboard/page.tsx @@ -0,0 +1 @@ +export { default } from "../../page"; diff --git a/app/admin/layout.tsx b/app/admin/layout.tsx new file mode 100644 index 00000000..2d41885f --- /dev/null +++ b/app/admin/layout.tsx @@ -0,0 +1,9 @@ +import RoleFolderLayout from "@/components/role-folder-layout"; + +export default function AdminLayout({ + children, +}: { + children: React.ReactNode; +}) { + return {children}; +} diff --git a/app/admin/reports/page.tsx b/app/admin/reports/page.tsx new file mode 100644 index 00000000..47e61858 --- /dev/null +++ b/app/admin/reports/page.tsx @@ -0,0 +1 @@ +export { default } from "../../page"; diff --git a/app/admin/users/page.tsx b/app/admin/users/page.tsx new file mode 100644 index 00000000..47e61858 --- /dev/null +++ b/app/admin/users/page.tsx @@ -0,0 +1 @@ +export { default } from "../../page"; diff --git a/app/admin/verify/page.tsx b/app/admin/verify/page.tsx new file mode 100644 index 00000000..47e61858 --- /dev/null +++ b/app/admin/verify/page.tsx @@ -0,0 +1 @@ +export { default } from "../../page"; diff --git a/app/api-example/page.tsx b/app/api-example/page.tsx deleted file mode 100644 index 690f7dd1..00000000 --- a/app/api-example/page.tsx +++ /dev/null @@ -1,38 +0,0 @@ -"use client" -import CustomLink from "@/components/custom-link" -import { useEffect, useState } from "react" - -export default function Page() { - const [data, setData] = useState() - useEffect(() => { - ;(async () => { - const res = await fetch("/api/protected") - const json = await res.json() - setData(json) - })() - }, []) - return ( -
-

Route Handler Usage

-

- This page fetches data from an API{" "} - - Route Handler - - . The API is protected using the universal{" "} - - auth() - {" "} - method. -

-
-
- Data from API Route -
-
-          {JSON.stringify(data, null, 2)}
-        
-
-
- ) -} diff --git a/app/api/auth/[...nextauth]/route.ts b/app/api/auth/[...nextauth]/route.ts new file mode 100644 index 00000000..7b38c1bb --- /dev/null +++ b/app/api/auth/[...nextauth]/route.ts @@ -0,0 +1,6 @@ +import NextAuth from "next-auth"; +import { authOptions } from "@/lib/auth"; + +const handler = NextAuth(authOptions); + +export { handler as GET, handler as POST }; diff --git a/app/api/protected/route.ts b/app/api/protected/route.ts deleted file mode 100644 index 99eb7f35..00000000 --- a/app/api/protected/route.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { auth } from "auth" - -export const GET = auth((req) => { - if (req.auth) { - return Response.json({ data: "Protected data" }) - } - - return Response.json({ message: "Not authenticated" }, { status: 401 }) -}) diff --git a/app/auth/[...nextauth]/route.ts b/app/auth/[...nextauth]/route.ts deleted file mode 100644 index d655d009..00000000 --- a/app/auth/[...nextauth]/route.ts +++ /dev/null @@ -1,2 +0,0 @@ -import { handlers } from "auth" -export const { GET, POST } = handlers diff --git a/app/buyer/dashboard/page.tsx b/app/buyer/dashboard/page.tsx new file mode 100644 index 00000000..47e61858 --- /dev/null +++ b/app/buyer/dashboard/page.tsx @@ -0,0 +1 @@ +export { default } from "../../page"; diff --git a/app/buyer/deliveries/page.tsx b/app/buyer/deliveries/page.tsx new file mode 100644 index 00000000..47e61858 --- /dev/null +++ b/app/buyer/deliveries/page.tsx @@ -0,0 +1 @@ +export { default } from "../../page"; diff --git a/app/buyer/layout.tsx b/app/buyer/layout.tsx new file mode 100644 index 00000000..816abcd2 --- /dev/null +++ b/app/buyer/layout.tsx @@ -0,0 +1,9 @@ +import RoleFolderLayout from "@/components/role-folder-layout"; + +export default function BuyerLayout({ + children, +}: { + children: React.ReactNode; +}) { + return {children}; +} diff --git a/app/buyer/market/page.tsx b/app/buyer/market/page.tsx new file mode 100644 index 00000000..47e61858 --- /dev/null +++ b/app/buyer/market/page.tsx @@ -0,0 +1 @@ +export { default } from "../../page"; diff --git a/app/buyer/orders/page.tsx b/app/buyer/orders/page.tsx new file mode 100644 index 00000000..47e61858 --- /dev/null +++ b/app/buyer/orders/page.tsx @@ -0,0 +1 @@ +export { default } from "../../page"; diff --git a/app/client-example/page.tsx b/app/client-example/page.tsx deleted file mode 100644 index cccfeddb..00000000 --- a/app/client-example/page.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { auth } from "auth" -import ClientExample from "@/components/client-example" -import { SessionProvider } from "next-auth/react" - -export default async function ClientPage() { - const session = await auth() - if (session?.user) { - // TODO: Look into https://react.dev/reference/react/experimental_taintObjectReference - // filter out sensitive data before passing to client. - session.user = { - name: session.user.name, - email: session.user.email, - image: session.user.image, - } - } - - return ( - - - - ) -} diff --git a/app/delivery/dashboard/page.tsx b/app/delivery/dashboard/page.tsx new file mode 100644 index 00000000..47e61858 --- /dev/null +++ b/app/delivery/dashboard/page.tsx @@ -0,0 +1 @@ +export { default } from "../../page"; diff --git a/app/delivery/history/page.tsx b/app/delivery/history/page.tsx new file mode 100644 index 00000000..47e61858 --- /dev/null +++ b/app/delivery/history/page.tsx @@ -0,0 +1 @@ +export { default } from "../../page"; diff --git a/app/delivery/layout.tsx b/app/delivery/layout.tsx new file mode 100644 index 00000000..12be122f --- /dev/null +++ b/app/delivery/layout.tsx @@ -0,0 +1,11 @@ +import RoleFolderLayout from "@/components/role-folder-layout"; + +export default function DeliveryLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( + {children} + ); +} diff --git a/app/delivery/routes/page.tsx b/app/delivery/routes/page.tsx new file mode 100644 index 00000000..47e61858 --- /dev/null +++ b/app/delivery/routes/page.tsx @@ -0,0 +1 @@ +export { default } from "../../page"; diff --git a/app/delivery/tasks/page.tsx b/app/delivery/tasks/page.tsx new file mode 100644 index 00000000..47e61858 --- /dev/null +++ b/app/delivery/tasks/page.tsx @@ -0,0 +1 @@ +export { default } from "../../page"; diff --git a/app/farmer/dashboard/page.tsx b/app/farmer/dashboard/page.tsx new file mode 100644 index 00000000..47e61858 --- /dev/null +++ b/app/farmer/dashboard/page.tsx @@ -0,0 +1 @@ +export { default } from "../../page"; diff --git a/app/farmer/insights/page.tsx b/app/farmer/insights/page.tsx new file mode 100644 index 00000000..47e61858 --- /dev/null +++ b/app/farmer/insights/page.tsx @@ -0,0 +1 @@ +export { default } from "../../page"; diff --git a/app/farmer/inventory/page.tsx b/app/farmer/inventory/page.tsx new file mode 100644 index 00000000..47e61858 --- /dev/null +++ b/app/farmer/inventory/page.tsx @@ -0,0 +1 @@ +export { default } from "../../page"; diff --git a/app/farmer/layout.tsx b/app/farmer/layout.tsx new file mode 100644 index 00000000..27526398 --- /dev/null +++ b/app/farmer/layout.tsx @@ -0,0 +1,9 @@ +import RoleFolderLayout from "@/components/role-folder-layout"; + +export default function FarmerLayout({ + children, +}: { + children: React.ReactNode; +}) { + return {children}; +} diff --git a/app/farmer/orders/page.tsx b/app/farmer/orders/page.tsx new file mode 100644 index 00000000..47e61858 --- /dev/null +++ b/app/farmer/orders/page.tsx @@ -0,0 +1 @@ +export { default } from "../../page"; diff --git a/app/favicon.ico b/app/favicon.ico deleted file mode 100644 index f52f61de00610a85328c3ed94a4c755a50d02e9c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2191 zcmV;A2ypj_P)Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91AfN*P1ONa40RR91AOHXW0IY^$^8f$``AI}UR9Fd>S8a?{M;U%+&bfQ< zet-*G*e#%tRz)Ejn|2LC&1t zbCtY@>#0{42H!~Dw^5tUCl@D=j&mPBT$17A=t-kf_<`06>x=uz-ZO>`RDGDbUlZ4Rz*gR{mbi-5+Z% zenryD=hZ{=$NzNBv?d_yK$5F=!$2@r#7{Kv5>KqlU3%C70Q^fd|U0A3QHZ0>wqwK5Yy_uM+=RbUFh zh9nHu(4j*LlqV8McvZY204D)9knKh;vZ-$6-hVV@>syGr(*te-32T+fjGc?ur_uc7 zK`~G@MhgYW)RPDrNifH~1?i*py90A5Vl2iyc_3=+ris%iwBxB%8`einx$^>;Dj4M7 z7{n}UChkwxeBIsordJc zav8c#iHj&{eWL=18hsL=0@lW}S<}|zk+%n0O9T0|uDMaF3QQk8i_sIofCZrtX+gr1 zArVBqq~yR+Cp*uW5H$fj5I_q+rogayaFRep+3dA7i}h50chBwwY50pq4np4H7qa?v zPz|cUlTiZz;|}+1F$nIN)^hM?O71#|HEjT+*xuC=0CPMm8v3?_?56Iold=y7KSCS5 zUOfsK({KP0yhwq!++t7wHO@4!zi~;+6EF73V=q)>-4)1*e6Ikf1VDo|9A^puZb^Xm zI0vA{a%R;rIK^9&SF3e=UAuB29Y{YMc0zYIcru8XXF`nE@9ycEJaS}EHY`Na$-o*8 zEkG>fwUC*%sB-kfJuT%gEq3&Y&h^FxLPG(O;iI2k8g51}qWWDaJ$C_*W)?cz#^qSP z^ozA}@W*Rp>=NFVDV!IP1sm=i32~fcN=t%84dBg5D!)D?_q10COin8M{;PdIAIzhs z&p+fYcR>}Ew&%=%JpIf@x&O~YQd!2Hhm>^gAT?VcP=Er=F>!LWrQA_fIX4I1uh>XJO1quiEcP!zkf&+yo75-0p8wrEt}eYvHzuy z{Cn+#`;U&OzIX6-1Yqx&&H)ktVKL4q88zQo{_>y!jQ9SvM5XZ`eQYQ9%d9c`&#*ZB zEgtC(z4pz8@!^Y=m-3d`oMS6xY}n&jVx*A zW1M5(I(?~Q^ZUM2-#OhyD|SKez_=Zqj-2}z_Cv=0ar3T6|g&l$Q)fKEe^`KLr4+@y!|f<;t5|RLjZ)@%fnw zNGo>CBQ`=e0J=L9&}H{ebHfjus{jKfs@HwlDWZC_dcY6uQIKoBPO$t zKYe9<@rLCqx3?s~726JNn9pj-V{w1#>xc^Fe+@?Fr$tN}?H*!9g`3UwNvr z{Xbl@+SN;bwRN0RCmx*NnqQ9|PnzZnY2r2~_|eF1Xz9Kwe R2ZjIu002ovPDHLkV1mft1Ec@| diff --git a/app/globals.css b/app/globals.css index 8066cf65..1c445e7e 100644 --- a/app/globals.css +++ b/app/globals.css @@ -2,46 +2,18 @@ @tailwind components; @tailwind utilities; -@layer base { - :root { - --background: 0 0% 100%; - --foreground: 222.2 84% 4.9%; - - --card: 0 0% 100%; - --card-foreground: 222.2 84% 4.9%; - - --popover: 0 0% 100%; - --popover-foreground: 222.2 84% 4.9%; - - --primary: 222.2 47.4% 11.2%; - --primary-foreground: 210 40% 98%; - - --secondary: 210 40% 96.1%; - --secondary-foreground: 222.2 47.4% 11.2%; - - --muted: 210 40% 96.1%; - --muted-foreground: 215.4 16.3% 46.9%; - - --accent: 210 40% 96.1%; - --accent-foreground: 222.2 47.4% 11.2%; - - --destructive: 0 84.2% 60.2%; - --destructive-foreground: 210 40% 98%; - - --border: 214.3 31.8% 91.4%; - --input: 214.3 31.8% 91.4%; - --ring: 222.2 84% 4.9%; - - --radius: 0.5rem; - } +:root { + color-scheme: light; } -@layer base { - * { - @apply border-border; - } +html, +body { + min-height: 100%; +} - body { - @apply bg-background text-foreground; - } +body { + @apply bg-slate-50 text-slate-900; + margin: 0; + -webkit-font-smoothing: antialiased; + text-rendering: optimizeLegibility; } diff --git a/app/layout.tsx b/app/layout.tsx index 3205f22a..02c5b9d2 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,29 +1,22 @@ -import "./globals.css" -import type { Metadata } from "next" -import { Inter } from "next/font/google" -import Footer from "@/components/footer" -import Header from "@/components/header" - -const inter = Inter({ subsets: ["latin"] }) +import type { Metadata } from "next"; +import "./globals.css"; +import Providers from "@/components/providers"; export const metadata: Metadata = { - title: "NextAuth.js Example", - description: - "This is an example site to demonstrate how to use NextAuth.js for authentication", -} + title: "Kuku Market Auth", + description: "Client-side auth with NextAuth" +}; -export default function RootLayout({ children }: React.PropsWithChildren) { +export default function RootLayout({ + children +}: Readonly<{ + children: React.ReactNode; +}>) { return ( - -
-
-
- {children} -
-
-
+ + {children} - ) + ); } diff --git a/app/middleware-example/page.tsx b/app/middleware-example/page.tsx deleted file mode 100644 index 370e0cf7..00000000 --- a/app/middleware-example/page.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import CustomLink from "@/components/custom-link" - -export default function Page() { - return ( -
-

Middleware usage

-

- This page is protected by using the universal{" "} - - auth() - {" "} - method in{" "} - - Next.js Middleware - - . -

-
- ) -} diff --git a/app/page.tsx b/app/page.tsx index 5d6c802c..8a3d0120 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,38 +1,488 @@ -import CustomLink from "@/components/custom-link" -import { auth } from "auth" +"use client"; -export default async function Index() { - const session = await auth() +import { useState, useEffect } from "react"; +import { signIn, signOut, useSession } from "next-auth/react"; +import { + Alert, + Avatar, + Box, + Button, + Card, + CardContent, + Chip, + CircularProgress, + Divider, + InputAdornment, + Stack, + TextField, + Typography, +} from "@mui/material"; +import BusinessCenterOutlinedIcon from "@mui/icons-material/BusinessCenterOutlined"; +import EmailOutlinedIcon from "@mui/icons-material/EmailOutlined"; +import GoogleIcon from "@mui/icons-material/Google"; +import LockOutlinedIcon from "@mui/icons-material/LockOutlined"; +import ShieldOutlinedIcon from "@mui/icons-material/ShieldOutlined"; + +import apiClient from "@/lib/axios"; +import RegisterCredentialsForm from "@/components/register-credentials-form"; +import RegisterRoleStepper, { + type UserRole, +} from "@/components/register-role-stepper"; +import RoleUserLayout from "@/components/role-user-layout"; + +type SocialProvider = "google"; +type Profile = { + user_id?: string | number; + [key: string]: unknown; +}; + +const findUserById = (profiles: Profile[], userId?: string | number | null) => { + if (!userId) return null; + return ( + profiles.find((profile) => String(profile.user_id) === String(userId)) ?? + null + ); +}; + +const BRAND_GREEN = "#4b7f2a"; +const BRAND_GREEN_DARK = "#2f5f21"; +const BRAND_GREEN_SOFT = "#95b54c"; +const BRAND_GOLD = "#b9923b"; + +export default function HomePage() { + const { data: session, status, update } = useSession(); + const [identifier, setIdentifier] = useState(""); + const [password, setPassword] = useState(""); + const [error, setError] = useState(null); + const [loading, setLoading] = useState(false); + const [socialLoading, setSocialLoading] = useState( + null, + ); + const [registerOpen, setRegisterOpen] = useState(false); + + const getRoleEndpoint = (role?: string | null) => { + switch (role) { + case "buyer": + return "/buyers"; + case "farmer": + return "/farmers"; + case "delivery": + return "/deliveries"; + default: + return null; + } + }; + + const extractRows = (responseData: unknown): Profile[] => { + if (Array.isArray(responseData)) return responseData as Profile[]; + if ( + responseData && + typeof responseData === "object" && + Array.isArray((responseData as { data?: unknown }).data) + ) { + return (responseData as { data: Profile[] }).data; + } + return []; + }; + + const checkUserRole = async (user: { + id?: string; + role?: string | null; + accessToken?: string; + }) => { + const endpoint = getRoleEndpoint(user.role); + if (!endpoint || !user.id || !user.accessToken) return false; + + try { + const response = await apiClient.get(endpoint, { + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${user.accessToken}`, + }, + }); + + const rows = extractRows(response.data); + return Boolean(findUserById(rows, user.id)); + } catch (roleCheckError) { + console.error("Failed to check role profile:", roleCheckError); + return false; + } + }; + + useEffect(() => { + if (session?.user.role === "admin") { + return; + } + let mounted = true; + + const checkRegistrationStatus = async () => { + if (!session?.user) { + if (mounted) setRegisterOpen(false); + return; + } + + const hasProfile = await checkUserRole(session.user); + console.log("hasProfile", hasProfile); + + if (mounted) setRegisterOpen(!hasProfile); + }; + + void checkRegistrationStatus(); + + return () => { + mounted = false; + }; + }, [session]); + + const handleCredentialsLogin = async ( + event: Parameters["onSubmit"]>>[0], + ) => { + event.preventDefault(); + setError(null); + setLoading(true); + + const result = await signIn("credentials", { + identifier, + password, + redirect: false, + }); + + setLoading(false); + + if (result?.error) { + setError("Invalid username/email or password"); + } + }; + + const handleSocialLogin = async (provider: SocialProvider) => { + setError(null); + setSocialLoading(provider); + await signIn(provider, { callbackUrl: "/" }); + setSocialLoading(null); + }; + + if (status === "loading") { + return ( +
+ + + + Loading secure session... + + +
+ ); + } + + if (session) { + return ( +
+ signOut()} /> + setRegisterOpen(false)} + accessToken={session?.user?.accessToken} + userId={session?.user?.id} + initialRole={session?.user?.role} + onSuccess={async (role: UserRole) => { + await update({ + user: { + ...session?.user, + role, + }, + }); + setRegisterOpen(false); + }} + /> +
+ ); + } return ( -
-

NextAuth.js Example

-
- This is an example site to demonstrate how to use{" "} - NextAuth.js{" "} - for authentication. Check out the{" "} - - Server - {" "} - and the{" "} - - Client - {" "} - examples to see how to secure pages and get session data. -
-
- WebAuthn users are reset on every deploy, don't expect your test user(s) - to still be available after a few days. It is designed to only - demonstrate registration, login, and logout briefly. -
-
-
- Current Session -
-
-          {JSON.stringify(session, null, 2)}
-        
-
-
- ) +
+ + + + + + + + + + + + + Kuku Market Portal + + + Premium Fruit Ecosystem Access + + + + + + KUKU Market: +
+ Premium Fruit Platform +
+ + + Business Model & Operational Flow + + + + Source to marketplace to power buyers and delivery partner with + secure account access, exclusive products, and certified + quality. + + + + } + label="Token-based sessions" + size="small" + sx={{ + color: BRAND_GREEN_DARK, + bgcolor: "#e7f1d1", + border: `1px solid ${BRAND_GREEN_SOFT}`, + borderRadius: 2, + }} + /> + + +
+
+
+ + + + + + + + KUKU MARKET + + {/* + KUKU Market business access for staff and power buyers. + */} + + + {error && {error}} + + + + setIdentifier(e.target.value)} + required + fullWidth + size="medium" + InputProps={{ + startAdornment: ( + + + + ), + }} + /> + setPassword(e.target.value)} + required + fullWidth + size="medium" + InputProps={{ + startAdornment: ( + + + + ), + }} + /> + + + + + OR + + + + REGISTER + setError(null)} + /> + {/* } + sx={{ mt: 0.5 }} + > + Protected access. Activity may be logged for security + auditing. + */} + + + + +
+
+ ); } diff --git a/app/policy/page.tsx b/app/policy/page.tsx deleted file mode 100644 index 3cd7d7d8..00000000 --- a/app/policy/page.tsx +++ /dev/null @@ -1,32 +0,0 @@ -export default function PolicyPage() { - return ( -
-
-

Terms of Service

-

- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -

-
-
-

Privacy Policy

-

- This site uses JSON Web Tokens and a Key-Value database for sessions - and WebAuthn authenticators which resets every 2 hours. -

-

- Data provided to this site is exclusively used to support signing in - and is not passed to any third party services, other than via SMTP or - OAuth for the purposes of authentication. And Vercel KV / Upstash for - hosting the Key Value store. This data is deleted every 2 hours via - cron job. -

-
-
- ) -} diff --git a/app/server-example/page.tsx b/app/server-example/page.tsx deleted file mode 100644 index 3cb8ad74..00000000 --- a/app/server-example/page.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import CustomLink from "@/components/custom-link" -import SessionData from "@/components/session-data" -import { auth } from "auth" - -export default async function Page() { - const session = await auth() - return ( -
-

React Server Component Usage

-

- This page is server-rendered as a{" "} - - React Server Component - - . It gets the session data on the server using{" "} - - auth() - {" "} - method. -

- -
- ) -} diff --git a/auth.ts b/auth.ts deleted file mode 100644 index fa7c049e..00000000 --- a/auth.ts +++ /dev/null @@ -1,137 +0,0 @@ -import NextAuth from "next-auth" -import "next-auth/jwt" - -import Apple from "next-auth/providers/apple" -// import Atlassian from "next-auth/providers/atlassian" -import Auth0 from "next-auth/providers/auth0" -import AzureB2C from "next-auth/providers/azure-ad-b2c" -import BankIDNorway from "next-auth/providers/bankid-no" -import BoxyHQSAML from "next-auth/providers/boxyhq-saml" -import Cognito from "next-auth/providers/cognito" -import Coinbase from "next-auth/providers/coinbase" -import Discord from "next-auth/providers/discord" -import Dropbox from "next-auth/providers/dropbox" -import Facebook from "next-auth/providers/facebook" -import GitHub from "next-auth/providers/github" -import GitLab from "next-auth/providers/gitlab" -import Google from "next-auth/providers/google" -import Hubspot from "next-auth/providers/hubspot" -import Keycloak from "next-auth/providers/keycloak" -import LinkedIn from "next-auth/providers/linkedin" -import MicrosoftEntraId from "next-auth/providers/microsoft-entra-id" -import Netlify from "next-auth/providers/netlify" -import Okta from "next-auth/providers/okta" -import Passage from "next-auth/providers/passage" -import Passkey from "next-auth/providers/passkey" -import Pinterest from "next-auth/providers/pinterest" -import Reddit from "next-auth/providers/reddit" -import Slack from "next-auth/providers/slack" -import Salesforce from "next-auth/providers/salesforce" -import Spotify from "next-auth/providers/spotify" -import Twitch from "next-auth/providers/twitch" -import Twitter from "next-auth/providers/twitter" -import Vipps from "next-auth/providers/vipps" -import WorkOS from "next-auth/providers/workos" -import Zoom from "next-auth/providers/zoom" -import { createStorage } from "unstorage" -import memoryDriver from "unstorage/drivers/memory" -import vercelKVDriver from "unstorage/drivers/vercel-kv" -import { UnstorageAdapter } from "@auth/unstorage-adapter" - -const storage = createStorage({ - driver: process.env.VERCEL - ? vercelKVDriver({ - url: process.env.AUTH_KV_REST_API_URL, - token: process.env.AUTH_KV_REST_API_TOKEN, - env: false, - }) - : memoryDriver(), -}) - -export const { handlers, auth, signIn, signOut } = NextAuth({ - debug: !!process.env.AUTH_DEBUG, - theme: { logo: "https://authjs.dev/img/logo-sm.png" }, - adapter: UnstorageAdapter(storage), - providers: [ - Apple, - // Atlassian, - Auth0, - AzureB2C, - BankIDNorway, - BoxyHQSAML({ - clientId: "dummy", - clientSecret: "dummy", - issuer: process.env.AUTH_BOXYHQ_SAML_ISSUER, - }), - Cognito, - Coinbase, - Discord, - Dropbox, - Facebook, - GitHub, - GitLab, - Google, - Hubspot, - Keycloak({ name: "Keycloak (bob/bob)" }), - LinkedIn, - MicrosoftEntraId, - Netlify, - Okta, - Passkey({ - formFields: { - email: { - label: "Username", - required: true, - autocomplete: "username webauthn", - }, - }, - }), - Passage, - Pinterest, - Reddit, - Salesforce, - Slack, - Spotify, - Twitch, - Twitter, - Vipps({ - issuer: "https://apitest.vipps.no/access-management-1.0/access/", - }), - WorkOS({ connection: process.env.AUTH_WORKOS_CONNECTION! }), - Zoom, - ], - basePath: "/auth", - session: { strategy: "jwt" }, - callbacks: { - authorized({ request, auth }) { - const { pathname } = request.nextUrl - if (pathname === "/middleware-example") return !!auth - return true - }, - jwt({ token, trigger, session, account }) { - if (trigger === "update") token.name = session.user.name - if (account?.provider === "keycloak") { - return { ...token, accessToken: account.access_token } - } - return token - }, - async session({ session, token }) { - if (token?.accessToken) session.accessToken = token.accessToken - - return session - }, - }, - experimental: { enableWebAuthn: true }, -}) - -declare module "next-auth" { - interface Session { - accessToken?: string - } -} - -declare module "next-auth/jwt" { - interface JWT { - accessToken?: string - } -} diff --git a/components.json b/components.json deleted file mode 100644 index fd5076fb..00000000 --- a/components.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "$schema": "https://ui.shadcn.com/schema.json", - "style": "default", - "rsc": true, - "tsx": true, - "tailwind": { - "config": "tailwind.config.js", - "css": "app/globals.css", - "baseColor": "slate", - "cssVariables": true - }, - "aliases": { - "components": "@/components", - "utils": "@/lib/utils" - } -} diff --git a/components/access-denied.tsx b/components/access-denied.tsx deleted file mode 100644 index 4c9c0d79..00000000 --- a/components/access-denied.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { signIn } from "next-auth/react" - -export default function AccessDenied() { - return ( - <> -

Access Denied

-

- { - e.preventDefault() - signIn() - }} - > - You must be signed in to view this page - -

- - ) -} diff --git a/components/auth-components.tsx b/components/auth-components.tsx deleted file mode 100644 index 4baac3de..00000000 --- a/components/auth-components.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { signIn, signOut } from "auth" -import { Button } from "./ui/button" - -export function SignIn({ - provider, - ...props -}: { provider?: string } & React.ComponentPropsWithRef) { - return ( -
{ - "use server" - await signIn(provider) - }} - > - -
- ) -} - -export function SignOut(props: React.ComponentPropsWithRef) { - return ( -
{ - "use server" - await signOut() - }} - className="w-full" - > - -
- ) -} diff --git a/components/client-example.tsx b/components/client-example.tsx deleted file mode 100644 index 13ec5681..00000000 --- a/components/client-example.tsx +++ /dev/null @@ -1,110 +0,0 @@ -"use client" - -import { useSession } from "next-auth/react" -import { Button } from "./ui/button" -import { Input } from "./ui/input" -import { useState } from "react" -import SessionData from "./session-data" -import CustomLink from "./custom-link" - -const UpdateForm = () => { - const { data: session, update } = useSession() - const [name, setName] = useState(`New ${session?.user?.name}`) - - if (!session?.user) return null - return ( - <> -

Updating the session client-side

-
- { - setName(e.target.value) - }} - /> - -
- - ) -} - -export default function ClientExample() { - const { data: session, status } = useSession() - const [apiResponse, setApiResponse] = useState("") - - const makeRequestWithToken = async () => { - try { - const response = await fetch("/api/authenticated/greeting") - const data = await response.json() - setApiResponse(JSON.stringify(data, null, 2)) - } catch (error) { - setApiResponse("Failed to fetch data: " + error) - } - } - - return ( -
-

Client Side Rendering

-

- This page fetches session data client side using the{" "} - - useSession - {" "} - React Hook. -

-

- It needs the{" "} - - 'use client' - {" "} - directive at the top of the file to enable client side rendering, and - the{" "} - - SessionProvider - {" "} - component in{" "} - - client-example/page.tsx - {" "} - to provide the session data. -

- -
-

Third-party backend integration

-

- Press the button to send a request to our{" "} - - example backend - - . Read more{" "} - - here - -

-
- -
-
{apiResponse}
-

- Note: This example only works when using the Keycloak provider. -

-
- - {status === "loading" ? ( -
Loading...
- ) : ( - - )} - -
- ) -} diff --git a/components/custom-link.tsx b/components/custom-link.tsx deleted file mode 100644 index 31c1f5ba..00000000 --- a/components/custom-link.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import { cn } from "@/lib/utils" -import { ExternalLink } from "lucide-react" -import Link from "next/link" - -interface CustomLinkProps extends React.LinkHTMLAttributes { - href: string -} - -const CustomLink = ({ - href, - children, - className, - ...rest -}: CustomLinkProps) => { - const isInternalLink = href.startsWith("/") - const isAnchorLink = href.startsWith("#") - - if (isInternalLink || isAnchorLink) { - return ( - - {children} - - ) - } - - return ( - - {children} - - - ) -} - -export default CustomLink diff --git a/components/footer.module.css b/components/footer.module.css deleted file mode 100644 index 020bec64..00000000 --- a/components/footer.module.css +++ /dev/null @@ -1,14 +0,0 @@ -.footer { - margin-top: 2rem; -} - -.navItems { - margin-bottom: 1rem; - padding: 0; - list-style: none; -} - -.navItem { - display: inline-block; - margin-right: 1rem; -} diff --git a/components/footer.tsx b/components/footer.tsx deleted file mode 100644 index ec4d1386..00000000 --- a/components/footer.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import CustomLink from "./custom-link" -import packageJSON from "next-auth/package.json" - -export default function Footer() { - return ( -
-
- Documentation - - NPM - - - Source on GitHub - - Policy -
-
- Auth.js Logo - - {packageJSON.version} - -
-
- ) -} diff --git a/components/header.module.css b/components/header.module.css deleted file mode 100644 index 773478c6..00000000 --- a/components/header.module.css +++ /dev/null @@ -1,92 +0,0 @@ -/* Set min-height to avoid page reflow while session loading */ -.signedInStatus { - display: block; - min-height: 4rem; - width: 100%; -} - -.loading, -.loaded { - position: relative; - top: 0; - opacity: 1; - overflow: hidden; - border-radius: 0 0 0.6rem 0.6rem; - padding: 0.6rem 1rem; - margin: 0; - background-color: rgba(0, 0, 0, 0.05); - transition: all 0.2s ease-in; -} - -.loading { - top: -2rem; - opacity: 0; -} - -.signedInText, -.notSignedInText { - position: absolute; - padding-top: 0.8rem; - left: 1rem; - right: 6.5rem; - white-space: nowrap; - text-overflow: ellipsis; - overflow: hidden; - display: inherit; - z-index: 1; - line-height: 1.3rem; -} - -.signedInText { - padding-top: 0rem; - left: 4.6rem; -} - -.avatar { - border-radius: 2rem; - float: left; - height: 2.8rem; - width: 2.8rem; - background-color: white; - background-size: cover; - background-repeat: no-repeat; -} - -.button, -.buttonPrimary { - float: right; - margin-right: -0.4rem; - font-weight: 500; - border-radius: 0.3rem; - cursor: pointer; - font-size: 1rem; - line-height: 1.4rem; - padding: 0.7rem 0.8rem; - position: relative; - z-index: 10; - background-color: transparent; - color: #555; -} - -.buttonPrimary { - background-color: #346df1; - border-color: #346df1; - color: #fff; - text-decoration: none; - padding: 0.7rem 1.4rem; -} - -.buttonPrimary:hover { - box-shadow: inset 0 0 5rem rgba(0, 0, 0, 0.2); -} - -.navItems { - margin-bottom: 2rem; - padding: 0; - list-style: none; -} - -.navItem { - display: inline-block; - margin-right: 1rem; -} diff --git a/components/header.tsx b/components/header.tsx deleted file mode 100644 index 8110a459..00000000 --- a/components/header.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import { MainNav } from "./main-nav" -import UserButton from "./user-button" - -export default function Header() { - return ( -
-
- - -
-
- ) -} diff --git a/components/layout.tsx b/components/layout.tsx deleted file mode 100644 index 9ee0bb28..00000000 --- a/components/layout.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import Header from "./header" -import Footer from "./footer" -import type { ReactNode } from "react" - -export default function Layout({ children }: { children: ReactNode }) { - return ( - <> -
-
{children}
-