Skip to content

Latest commit

 

History

History
321 lines (244 loc) · 6.65 KB

README.md

File metadata and controls

321 lines (244 loc) · 6.65 KB

nextjs14-authjs5-dashboard

This is a dashboard starter template for the NextJS 14 app router based on Auth.js v5.

Screenshots

screenshot

Table of Contents

Denpendencies

  • Next.js 14
  • Auth.js v5 + Prisma Adapter
  • Tailwindcss
  • Shadcn
  • Prisma
  • Zustand
  • React Query

Folder and file Structure

The folder and file structure is based on nextjs app router next.js project structure.

.
├── actions/                    # Server Actions
├── app/                        # App Router
│   └── api/
│       ├── auth/               # Authentication
│       └── v1/                 # Public APIs
├── components/                 # React components
├── config/                     # Configuration for site
├── context/                    # Context
├── docs/                       # Documents
├── hooks/                      # Hooks
├── lib/                        # Utility functions
├── prisma/                     # Prisma Schema Location and Configuration
├── public/                     # Static assets to be served
│   └── [locales]/              # Internationalization
├── queries/                    # API
├── schemas/                    # Schema validations
├── screenshots/                # Screenshots
├── store/                      # State
├── types/                      # Type definitions
└── package.json

Getting Started

Clone the repository to the current directory.

git clone https://github.com/w3labkr/nextjs14-authjs5-dashboard.git .

Install all modules listed as dependencies.

npm install

Copy of the .env.example if the .env doesn't exist.

cp .env.example .env

Create an SQL migration file and execute it.

npx prisma migrate dev --name init

Start the development server.

npm run dev

Documents

Examples

ApiResponse

success

import { ApiResponse } from '@/lib/http'

export async function POST(req) {
  return ApiResponse.json({ user: null })
}

// output
// { status: 'success', message: 'OK', success: true, data: { user: null } }

fail

import { ApiResponse } from '@/lib/http'

export async function POST(req) {
  return ApiResponse.json({ user: null }, { status: 400 })
}

// output
// { status: 'fail', message: 'Bad Request', success: false, data: { user: null } }

xhr

Change URL address to absolute address and return as json response.

import { xhr } from '@/lib/http'

const res = await xhr.get('/api', init)
const res = await xhr.head('/api', init)
const res = await xhr.post('/api', init)
const res = await xhr.put('/api', init)
const res = await xhr.delete('/api', init)
const res = await xhr.patch('/api', init)

http-status-codes

import { 
  STATUS_CODES,
  STATUS_TEXTS,
  STATUS_CODE_TO_TEXT,
  STATUS_TEXT_TO_CODE
} from '@/lib/http-status-codes/en'

STATUS_CODES.OK // 200
STATUS_TEXTS.OK // "OK"
STATUS_CODE_TO_TEXT["200"] // "OK"
STATUS_TEXT_TO_CODE["OK"] // "200"

bcrypt

import { generateHash, compareHash } from '@/lib/bcrypt'

const hashed = await generateHash('hash')

if (await compareHash('hash', hashed)) {
  // isMatch
}

JWT

import { 
  decodeJwt,
  verifyJwt,
  jwtSign,
  generateRecoveryToken,
  generateAccessToken,
  generateRefreshToken,
  generateTokenExpiresAt,
  isTokenExpired
} from '@/lib/jose'

decodeJwt(jwt: string)
verifyJwt(jwt: string | Uint8Array, options?: JWTVerifyOptions)
jwtSign(sub: string, exp: number | string | Date = '1h', payload?: JWTPayload)
generateRecoveryToken(sub: string, payload?: JWTPayload)
generateAccessToken(sub: string)
generateRefreshToken(sub: string, jwt?: string | null)
generateTokenExpiresAt(expiresIn: number = 60 * 60)
isTokenExpired(
  expiresAt: number, 
  options?: { expiresIn?: number; expiresBefore?: number }
)

CSRF

Route Handlers

import { verifyCSRFToken } from '@/lib/csrf'

export async function POST(req) {
  const { csrfToken, ...body } = await req.json()

  if (!verifyCSRFToken(csrfToken)) {
    return new Response('Unauthorized', { status: 401 })
  }
}

Client Side

'use client'

import { useCSRFToken } from '@/hooks/use-csrf-token'

export function Component() {
  const { csrfToken } = useCSRFToken()

  async function onSubmit() {
    const res = await fetch('/api', {
      method: 'POST',
      body: JSON.stringify({ csrfToken }),
    })
  }

  return <button onClick={onSubmit}>Submit</button>
}

Nodemailer

Sendmail

import { transporter, sender } from '@/lib/nodemailer'

try {
  const info = await transporter.sendMail({
    from: `"${sender?.name}" <${sender?.email}>`,
    to: '[email protected]',
    subject: `[${sender?.name}] Reset your password`,
    text: 'Hello World!',
    html: '<h2>Hello World!<h2>',
  })
} catch (e) {
  console.log(e)
}

dayjs

The time zone and localized format are set.

import dayjs from '@/lib/dayjs'

dayjs().toISOString()

LucideIcon

import { LucideIcon } from '@/lib/lucide-icon'

<LucideIcon name="Heart"/>

Math

import {
  getRandom,
  getRandomArbitrary,
  getRandomInt,
  getRandomIntInclusive
} from '@/lib/math'

getRandom()
getRandomArbitrary(min: number, max: number)
getRandomInt(min: number, max: number)
getRandomIntInclusive(min: number, max: number)

Utils

import {
  cn,
  uuidv4,
  sleep,
  fetcher,
  absoluteUrl,
  relativeUrl
} from '@/lib/utils'

cn(...inputs: ClassValue[])
uuidv4()
sleep(ms: number)
fetcher(input: RequestInfo | URL, init?: RequestInit)
absoluteUrl(url: string | URL, base?: string | URL)
relativeUrl(url: string | URL, base?: string | URL)

License

This software license under the MIT License.