All files / Bandstand/src/lib guard.ts

83.67% Statements 41/49
100% Branches 20/20
50% Functions 5/10
83.67% Lines 41/49

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 701x 1x 1x 1x 1x                       12x 12x 12x 11x 11x 11x 12x 10x 10x   10x 10x 8x   8x 10x 3x 3x   10x 10x 10x   3x 3x 3x 2x 2x     1x 7x 7x   2x 2x 2x 1x 1x                     1x 1x 1x 1x  
import {getServerSession} from 'next-auth'
import {redirect} from 'next/navigation'
import {authOptions} from './auth'
import {prisma} from './db'
import {currentBand} from './band'
 
/**
 * Guards for API routes and pages. API routes let the thrown Response
 * through (see route()); pages use the page* versions, which redirect.
 *
 * - requireUser: signed in (account pages: password, photo, settings)
 * - requireSession: signed in AND in a current band; everything band-owned
 *   must be looked up with `band.id`, or another band's data would show
 * - requireAdmin: an admin of the current band
 */
 
export async function requireUser() {
  const session = await getServerSession(authOptions)
  if (!session?.user?.email) throw new Response('Unauthorized', {status: 401})
  const user = await prisma.user.findUnique({
    where: {email: session.user.email},
  })
  if (!user) throw new Response('Unauthorized', {status: 401})
  return {session, user}
}
 
export async function requireSession() {
  const {session, user} = await requireUser()
  const {band, bands} = await currentBand(user.id)
  // 409: signed in, but in several bands and none picked on this device
  if (!band)
    throw new Response(bands.length ? 'Choose a band' : 'Not in a band', {
      status: 409,
    })
  // The install owner can manage any band they are in
  const isAdmin = band.isAdmin || user.isOwner
  return {session, user, band, bands, isAdmin}
}
 
export async function requireAdmin() {
  const ctx = await requireSession()
  if (!ctx.isAdmin) throw new Response('Forbidden', {status: 403})
  return ctx
}
 
/** A band with scheduling off has no availability tool (or its API). */
export function requireScheduling(band: {scheduling: boolean}) {
  if (!band.scheduling) throw new Response('Not Found', {status: 404})
}
 
export async function requireOwner() {
  const ctx = await requireUser()
  if (!ctx.user.isOwner) throw new Response('Forbidden', {status: 403})
  return ctx
}
 
function toRedirect(e: unknown): never {
  if (e instanceof Response) {
    if (e.status === 401) redirect('/login')
    if (e.status === 409) redirect('/bands')
    if (e.status === 403) redirect('/songs')
  }
  throw e
}
 
export const pageSession = () => requireSession().catch(toRedirect)
export const pageAdmin = () => requireAdmin().catch(toRedirect)
export const pageUser = () => requireUser().catch(toRedirect)
export const pageOwner = () => requireOwner().catch(toRedirect)