11import {
22 getOpenApiDocument ,
33 healthResponseSchema ,
4+ sessionDemoRequestSchema ,
45 sessionLoginRequestSchema ,
56} from "@deepnotes/api" ;
67import type { ContentfulStatusCode } from "hono/utils/http-status" ;
78import { Hono } from "hono" ;
89
910import { getDbForConnectionString } from "./db-pool.js" ;
1011import { readCookieHeader } from "./cookies.js" ;
12+ import { getSessionRedisPort } from "./redis-port.js" ;
1113import { getSessionEnv , type WorkerSessionBindings } from "./session-env.js" ;
1214
1315type Bindings = WorkerSessionBindings & {
@@ -17,16 +19,10 @@ type Bindings = WorkerSessionBindings & {
1719
1820const app = new Hono < { Bindings : Bindings } > ( ) ;
1921
20- const sessionNotImplementedBody = {
21- code : "NOT_IMPLEMENTED" as const ,
22- message :
23- "Demo registration is not implemented yet. See OpenAPI for the contract." ,
24- } ;
25-
2622const serviceUnavailableBody = {
2723 code : "SERVICE_UNAVAILABLE" as const ,
2824 message :
29- "Session routes require ACCESS_SECRET, REFRESH_SECRET, USER_EMAIL_SECRET, USER_REHASHED_LOGIN_HASH_ENCRYPTION_KEY, USER_AUTHENTICATOR_SECRET_ENCRYPTION_KEY, and USER_RECOVERY_CODES_ENCRYPTION_KEY (e.g. Wrangler secrets / .dev.vars)." ,
25+ "Session routes require ACCESS_SECRET, REFRESH_SECRET, USER_EMAIL_SECRET, USER_EMAIL_ENCRYPTION_KEY, USER_REHASHED_LOGIN_HASH_ENCRYPTION_KEY, USER_AUTHENTICATOR_SECRET_ENCRYPTION_KEY, and USER_RECOVERY_CODES_ENCRYPTION_KEY (e.g. Wrangler secrets / .dev.vars)." ,
3026} ;
3127
3228function appendSetCookies ( res : Response , lines : string [ ] ) : void {
@@ -93,6 +89,7 @@ app.post("/api/sessions/login", async (c) => {
9389 }
9490
9591 const db = getDbForConnectionString ( hyper . connectionString ) ;
92+ const redis = getSessionRedisPort ( c . env ) ;
9693
9794 try {
9895 const { performSessionLogin } = await import ( "@deepnotes/session" ) ;
@@ -109,6 +106,7 @@ app.post("/api/sessions/login", async (c) => {
109106 } ,
110107 clientIp : c . req . header ( "CF-Connecting-IP" ) ?? "127.0.0.1" ,
111108 userAgent : c . req . header ( "User-Agent" ) ?? "" ,
109+ redis,
112110 } ) ;
113111 const res = c . json ( json , 200 ) ;
114112 appendSetCookies ( res , cookieLines ) ;
@@ -198,8 +196,103 @@ app.post("/api/sessions/logout", async (c) => {
198196 return res ;
199197} ) ;
200198
201- app . post ( "/api/sessions/demo" , ( c ) =>
202- c . json ( sessionNotImplementedBody , 501 ) ,
203- ) ;
199+ app . post ( "/api/sessions/demo" , async ( c ) => {
200+ const sessionEnv = getSessionEnv ( c . env ) ;
201+ if ( sessionEnv == null ) {
202+ return c . json ( serviceUnavailableBody , 503 ) ;
203+ }
204+ const hyper = c . env . HYPERDRIVE ;
205+ if ( hyper == null ) {
206+ return c . json (
207+ {
208+ code : "SERVICE_UNAVAILABLE" as const ,
209+ message : "HYPERDRIVE binding is not configured." ,
210+ } ,
211+ 503 ,
212+ ) ;
213+ }
214+
215+ let bodyJson : unknown ;
216+ try {
217+ bodyJson = await c . req . json ( ) ;
218+ } catch {
219+ return c . json ( { code : "BAD_REQUEST" , message : "Expected JSON body." } , 400 ) ;
220+ }
221+
222+ const parsed = sessionDemoRequestSchema . safeParse ( bodyJson ) ;
223+ if ( ! parsed . success ) {
224+ return c . json (
225+ {
226+ code : "VALIDATION_ERROR" ,
227+ message : parsed . error . flatten ( ) . formErrors . join ( "; " ) ,
228+ } ,
229+ 400 ,
230+ ) ;
231+ }
232+
233+ const db = getDbForConnectionString ( hyper . connectionString ) ;
234+
235+ try {
236+ const { performSessionStartDemo } = await import ( "@deepnotes/session" ) ;
237+ const { json, cookieLines } = await performSessionStartDemo ( {
238+ db,
239+ env : sessionEnv ,
240+ body : parsed . data ,
241+ clientIp : c . req . header ( "CF-Connecting-IP" ) ?? "127.0.0.1" ,
242+ userAgent : c . req . header ( "User-Agent" ) ?? "" ,
243+ } ) ;
244+ const res = c . json ( json , 200 ) ;
245+ appendSetCookies ( res , cookieLines ) ;
246+ return res ;
247+ } catch ( e ) {
248+ const { SessionError } = await import ( "@deepnotes/session" ) ;
249+ if ( e instanceof SessionError ) {
250+ return c . json (
251+ { code : e . code , message : e . message } ,
252+ e . status as ContentfulStatusCode ,
253+ ) ;
254+ }
255+ throw e ;
256+ }
257+ } ) ;
258+
259+ app . get ( "/api/users/me" , async ( c ) => {
260+ const sessionEnv = getSessionEnv ( c . env ) ;
261+ if ( sessionEnv == null ) {
262+ return c . json ( serviceUnavailableBody , 503 ) ;
263+ }
264+ const hyper = c . env . HYPERDRIVE ;
265+ if ( hyper == null ) {
266+ return c . json (
267+ {
268+ code : "SERVICE_UNAVAILABLE" as const ,
269+ message : "HYPERDRIVE binding is not configured." ,
270+ } ,
271+ 503 ,
272+ ) ;
273+ }
274+
275+ const db = getDbForConnectionString ( hyper . connectionString ) ;
276+ const cookieHeader = c . req . header ( "Cookie" ) ;
277+
278+ try {
279+ const { getAuthenticatedUserSummary } = await import ( "@deepnotes/session" ) ;
280+ const summary = await getAuthenticatedUserSummary ( {
281+ db,
282+ env : sessionEnv ,
283+ accessCookie : readCookieHeader ( cookieHeader , "accessToken" ) ,
284+ } ) ;
285+ return c . json ( summary , 200 ) ;
286+ } catch ( e ) {
287+ const { SessionError } = await import ( "@deepnotes/session" ) ;
288+ if ( e instanceof SessionError ) {
289+ return c . json (
290+ { code : e . code , message : e . message } ,
291+ e . status as ContentfulStatusCode ,
292+ ) ;
293+ }
294+ throw e ;
295+ }
296+ } ) ;
204297
205298export default app ;
0 commit comments