Skip to content

Commit

Permalink
feat: adds concept of sessions and callbacks to Course Builder (#126)
Browse files Browse the repository at this point in the history
* feat: adds concept of sessions and callbacks to Course Builder

* fix package ordering

* fix types

* env validation

* does this peer dep work?

* appease the Lord of Lint

* update next-auth and auth-core to latest version

* add changeset
  • Loading branch information
joelhooks authored Mar 25, 2024
1 parent 5d8f48c commit 0b590f9
Show file tree
Hide file tree
Showing 26 changed files with 260 additions and 86 deletions.
8 changes: 8 additions & 0 deletions .changeset/swift-jokes-melt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@coursebuilder/adapter-drizzle": patch
"@coursebuilder/core": patch
"@coursebuilder/next": patch
"create-course-app": patch
---

updates next-auth and auth-core and gives a "session" to coursebuilder
2 changes: 1 addition & 1 deletion apps/course-builder-web/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

NEXT_PUBLIC_APP_NAME=app-name

COURSEBUILDER_URL="SITE_URL"
COURSEBUILDER_URL="http://example.com"

# Drizzle
# Get the Database URL from the "prisma" dropdown selector in PlanetScale
Expand Down
4 changes: 2 additions & 2 deletions apps/course-builder-web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
"@atlaskit/primitives": "^2.0.2",
"@atlaskit/select": "^17.1.0",
"@atlaskit/tokens": "^1.37.1",
"@auth/core": "^0.28.0",
"@auth/core": "^0.28.1",
"@aws-sdk/client-s3": "^3.525.0",
"@aws-sdk/client-textract": "^3.525.0",
"@aws-sdk/credential-providers": "^3.525.0",
Expand Down Expand Up @@ -120,7 +120,7 @@
"memoize-one": "^6.0.0",
"nanoid": "^5.0.2",
"next": "14.2.0-canary.40",
"next-auth": "5.0.0-beta.15",
"next-auth": "5.0.0-beta.16",
"next-axiom": "^1.1.1",
"next-mdx-remote": "^4.4.1",
"next-themes": "^0.2.1",
Expand Down
3 changes: 3 additions & 0 deletions apps/course-builder-web/src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as React from 'react'
import { type Metadata } from 'next'
import { Landing } from '@/app/_components/landing'
import { coursebuilder } from '@/coursebuilder/course-builder-config'

export const metadata: Metadata = {
title: 'Course Builder',
Expand All @@ -9,6 +10,8 @@ export const metadata: Metadata = {
}

export default async function PlaygroundPage() {
const cb = await coursebuilder()
console.log({ cb })
return (
<main>
<article className="prose sm:prose-lg dark:prose-invert mx-auto w-full max-w-2xl px-5 py-8 sm:py-16">
Expand Down
30 changes: 13 additions & 17 deletions apps/course-builder-web/src/coursebuilder/course-builder-config.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,22 @@
import { db } from '@/db'
import { mysqlTable } from '@/db/mysql-table'
import { env } from '@/env.mjs'
import { transcriptProvider } from '@/coursebuilder/transcript-provider'
import { courseBuilderAdapter } from '@/db'
import { inngest } from '@/inngest/inngest.server'

import { DrizzleAdapter } from '@coursebuilder/adapter-drizzle'
import { type CourseBuilderConfig } from '@coursebuilder/core'
import DeepgramProvider from '@coursebuilder/core/providers/deepgram'
import NextCourseBuilder from '@coursebuilder/next'
import NextCourseBuilder, {
type NextCourseBuilderConfig,
} from '@coursebuilder/next'

const callbackBase =
env.NODE_ENV === 'production' ? env.UPLOADTHING_URL : env.NEXT_PUBLIC_URL

export const transcriptProvider = DeepgramProvider({
apiKey: env.DEEPGRAM_API_KEY,
callbackUrl: `${callbackBase}/api/coursebuilder/webhook/deepgram`,
})

export const courseBuilderConfig: CourseBuilderConfig = {
adapter: DrizzleAdapter(db, mysqlTable),
export const courseBuilderConfig: NextCourseBuilderConfig = {
adapter: courseBuilderAdapter,
inngest,
providers: [transcriptProvider],
basePath: '/api/coursebuilder',
callbacks: {
session: async (req) => {
// TODO: there's nothing on the "session" but we can add whatever we want here
return { ...req, example: 'just an example' }
},
},
}

export const {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { env } from '@/env.mjs'

import DeepgramProvider from '@coursebuilder/core/providers/deepgram'

export const transcriptProvider = DeepgramProvider({
apiKey: env.DEEPGRAM_API_KEY,
callbackUrl: `${env.COURSEBUILDER_URL}/api/coursebuilder/webhook/deepgram`,
})
5 changes: 5 additions & 0 deletions apps/course-builder-web/src/db/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { mysqlTable } from '@/db/mysql-table'
import { env } from '@/env.mjs'
import { Client } from '@planetscale/database'
import { drizzle } from 'drizzle-orm/planetscale-serverless'

import { DrizzleAdapter } from '@coursebuilder/adapter-drizzle'

import * as schema from './schema'

export const db = drizzle(
Expand All @@ -10,3 +13,5 @@ export const db = drizzle(
}),
{ schema },
)

export const courseBuilderAdapter = DrizzleAdapter(db, mysqlTable)
9 changes: 9 additions & 0 deletions apps/course-builder-web/src/env.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ export const env = createEnv({
* isn't built with invalid env vars.
*/
server: {
COURSEBUILDER_URL: z.preprocess(
// This makes Vercel deployments not fail if you don't set NEXTAUTH_URL
// Since NextAuth.js automatically uses the VERCEL_URL if present.
(str) =>
process.env.VERCEL_URL ? `https://${process.env.VERCEL_URL}` : str,
// VERCEL_URL doesn't include `https` so it cant be validated as a URL
process.env.VERCEL ? z.string() : z.string(),
),
DATABASE_URL: z
.string()
.url()
Expand Down Expand Up @@ -77,6 +85,7 @@ export const env = createEnv({
* middlewares) or client-side so we need to destruct manually.
*/
runtimeEnv: {
COURSEBUILDER_URL: process.env.COURSEBUILDER_URL,
DATABASE_URL: process.env.DATABASE_URL,
NODE_ENV: process.env.NODE_ENV,
NEXTAUTH_SECRET: process.env.NEXTAUTH_SECRET,
Expand Down
4 changes: 2 additions & 2 deletions apps/course-builder-web/src/inngest/inngest.server.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { db } from '@/db'
import { courseBuilderAdapter, db } from '@/db'
import { env } from '@/env.mjs'
import {
EMAIL_SEND_BROADCAST,
Expand Down Expand Up @@ -62,7 +62,7 @@ const callbackBase =
env.NODE_ENV === 'production' ? env.UPLOADTHING_URL : env.NEXT_PUBLIC_URL

const middleware = createInngestMiddleware({
db: DrizzleAdapter(db, mysqlTable),
db: courseBuilderAdapter,
siteRootUrl: env.NEXT_PUBLIC_URL,
partyKitRootUrl: env.NEXT_PUBLIC_PARTY_KIT_URL,
mediaUploadProvider: new UTApi(),
Expand Down
5 changes: 2 additions & 3 deletions apps/course-builder-web/src/server/auth.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { getAbility } from '@/ability'
import { courseBuilderConfig } from '@/coursebuilder/course-builder-config'
import { db } from '@/db'
import { courseBuilderAdapter, db } from '@/db'
import { env } from '@/env.mjs'
import { USER_CREATED_EVENT } from '@/inngest/events/user-created'
import { inngest } from '@/inngest/inngest.server'
Expand Down Expand Up @@ -76,7 +75,7 @@ export const authOptions: NextAuthConfig = {
}
},
},
adapter: courseBuilderConfig.adapter,
adapter: courseBuilderAdapter,
providers: [
/**
* ...add more providers here.
Expand Down
2 changes: 1 addition & 1 deletion cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@
"@types/node": "^20.11.24",
"drizzle-kit": "^0.20.14",
"next": "canary",
"next-auth": "5.0.0-beta.15",
"next-auth": "5.0.0-beta.16",
"prettier": "^3.2.5",
"prettier-plugin-tailwindcss": "^0.5.12",
"prisma": "^5.6.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/adapter-drizzle/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
"date-fns": "^2.30.0"
},
"devDependencies": {
"@auth/core": "^0.28.0",
"@auth/core": "^0.28.1",
"@coursebuilder/core": "0.0.8",
"@libsql/client": "0.5.6",
"@types/better-sqlite3": "7.6.9",
Expand Down
14 changes: 8 additions & 6 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,24 @@
"types": "./dist/index.d.ts",
"files": [
"*.d.ts*",
"*.js",
"*.js*",
"lib",
"src",
"providers",
"inngest",
"dist"
"dist",
"dist/*.d.ts*",
"dist/*.js*"
],
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js",
"require": "./dist/index.js"
},
"./types": {
"types": "./dist/types.d.ts"
},
"./adapters": {
"types": "./dist/adapters.d.ts"
},
Expand Down Expand Up @@ -81,9 +86,6 @@
"types": "./dist/inngest/*.d.ts",
"import": "./dist/inngest/*.js",
"require": "./dist/inngest/*.js"
},
"./types": {
"types": "./dist/types.d.ts"
}
},
"private": false,
Expand All @@ -97,7 +99,7 @@
"srt-parser-2": "^1.2.3"
},
"devDependencies": {
"@auth/core": "^0.28.0",
"@auth/core": "^0.28.1",
"tsup": "6.7.0",
"zod": "^3.22.4"
}
Expand Down
14 changes: 9 additions & 5 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import type { CourseBuilderAdapter } from './adapters'
import { CourseBuilderInternal } from './lib'
import { assertConfig } from './lib/utils/assert'
import { createActionURL, setEnvDefaults } from './lib/utils/env.js'
import { logger, LoggerInstance, setLogger } from './lib/utils/logger'
import { logger, setLogger, type LoggerInstance } from './lib/utils/logger'
import { toInternalRequest, toResponse } from './lib/utils/web'
import type { Provider } from './providers'
import { CookiesOptions } from './types'
import type { CallbacksOptions, CookiesOptions } from './types'

export { createActionURL, setEnvDefaults }

Expand All @@ -19,15 +19,16 @@ export async function CourseBuilder(

const internalRequest = await toInternalRequest(request, config)
if (!internalRequest) {
return new Response('Bad request', { status: 400 })
return Response.json('Bad request', { status: 400 })
}

const warningsOrError = assertConfig(internalRequest, config)

if (Array.isArray(warningsOrError)) {
warningsOrError.forEach(logger.warn)
} else if (warningsOrError) {
logger.error(warningsOrError)
return new Response('Internal Server Error', { status: 500 })
return Response.json('Internal Server Error', { status: 500 })
}
const isRedirect = request.headers?.has('X-Course-Builder-Return-Redirect')

Expand All @@ -43,11 +44,14 @@ export async function CourseBuilder(
return Response.json({ url }, { headers: response.headers })
} catch (e) {
logger.error(e as Error)
return new Response('Internal Server Error', { status: 500 })
if (request.method === 'POST' && internalRequest.action === 'session')
return Response.json(null, { status: 400 })
return Response.json('bacon and eggs', { status: 400 })
}
}

export interface CourseBuilderConfig {
callbacks?: Partial<CallbacksOptions>
adapter?: CourseBuilderAdapter
providers: Provider[]
debug?: boolean
Expand Down
21 changes: 21 additions & 0 deletions packages/core/src/lib/actions/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,27 @@
import { InternalOptions, RequestInternal, ResponseInternal } from '../../types'
import { Cookie } from '../utils/cookie'

export async function session(
options: InternalOptions,
cookies: Cookie[],
): Promise<ResponseInternal<any | null>> {
const { callbacks, logger } = options

const response: ResponseInternal<any | null> = {
body: null,
headers: { 'Content-Type': 'application/json' },
cookies,
}

try {
response.body = await callbacks.session({})
} catch (e) {
logger.error(e as Error)
}

return response
}

export async function srt(
request: RequestInternal,
cookies: Cookie[],
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export async function CourseBuilderInternal(
case 'srt':
return await actions.srt(request, cookies, options)
case 'session':
return {}
return await actions.session(options, cookies)
}
} else {
switch (action) {
Expand Down
9 changes: 8 additions & 1 deletion packages/core/src/lib/init.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { AdapterError } from '../errors'
import { CourseBuilderConfig } from '../index'
import { InternalOptions, RequestInternal } from '../types'
import { CallbacksOptions, InternalOptions, RequestInternal } from '../types'
import * as cookie from './utils/cookie.js'
import { logger, LoggerInstance } from './utils/logger'
import { merge } from './utils/merge'
Expand Down Expand Up @@ -51,6 +51,7 @@ export async function init({
),
providers,
adapter: adapterErrorHandler(courseBuilderOptions.adapter, logger),
callbacks: { ...defaultCallbacks, ...courseBuilderOptions.callbacks },
logger,
}

Expand All @@ -61,6 +62,12 @@ export async function init({

type Method = (...args: any[]) => Promise<any>

export const defaultCallbacks: CallbacksOptions = {
session(payload) {
return payload
},
}

function adapterErrorHandler(
adapter: CourseBuilderConfig['adapter'],
logger: LoggerInstance,
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/lib/utils/actions.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { CourseBuilderAction } from '../../types'

const actions: CourseBuilderAction[] = ['webhook', 'srt']
const actions: CourseBuilderAction[] = ['webhook', 'srt', 'session']

export function isCourseBuilderAction(
action: string,
Expand Down
11 changes: 11 additions & 0 deletions packages/core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export interface InternalOptions<TProviderType = ProviderType> {
cookies: Record<keyof CookiesOptions, CookieOption>
basePath: string
inngest: Inngest
callbacks: CallbacksOptions
}

export interface CookieOption {
Expand All @@ -67,3 +68,13 @@ export interface CookieOption {
}

export interface CookiesOptions {}

export interface DefaultCourseBuilderSession {}

export interface CourseBuilderSession extends DefaultCourseBuilderSession {}

export interface CallbacksOptions {
session: (
params: any,
) => Awaitable<CourseBuilderSession | DefaultCourseBuilderSession>
}
3 changes: 3 additions & 0 deletions packages/next/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,14 @@
"inngest": "^3.16.1"
},
"devDependencies": {
"@auth/core": "^0.28.1",
"next": "14.2.0-canary.40",
"next-auth": "5.0.0-beta.16",
"react": "18.3.0-canary-03d6f7cf0-20240209"
},
"peerDependencies": {
"next": "^14",
"next-auth": "5.0.0-beta.16",
"react": "^18"
}
}
Loading

0 comments on commit 0b590f9

Please sign in to comment.