11import { createResponse } from './utils' ;
22import handleStudioRequest from "./studio" ;
33import { Handler } from "./handler" ;
4- import { QueryResponse } from "./operation" ;
4+ import { DatabaseStub , DataSource , RegionLocationHint , Source } from './types' ;
55export { DatabaseDurableObject } from './do' ;
66
77const DURABLE_OBJECT_ID = 'sql-durable-object' ;
@@ -37,43 +37,6 @@ export interface Env {
3737 // ## DO NOT REMOVE: TEMPLATE INTERFACE ##
3838}
3939
40- export enum Source {
41- internal = 'internal' , // Durable Object's SQLite instance
42- external = 'external' // External data source (e.g. Outerbase)
43- }
44-
45- export type DataSource = {
46- source : Source ;
47- request : Request ;
48- internalConnection ?: InternalConnection ;
49- externalConnection ?: {
50- outerbaseApiKey : string ;
51- } ;
52- }
53-
54- interface InternalConnection {
55- durableObject : DatabaseStub ;
56- }
57-
58- type DatabaseStub = DurableObjectStub & {
59- fetch : ( init ?: RequestInit | Request ) => Promise < Response > ;
60- executeQuery ( sql : string , params : any [ ] | undefined , isRaw : boolean ) : QueryResponse ;
61- executeTransaction ( queries : { sql : string ; params ?: any [ ] } [ ] , isRaw : boolean ) : any [ ] ;
62- } ;
63-
64- enum RegionLocationHint {
65- AUTO = 'auto' ,
66- WNAM = 'wnam' , // Western North America
67- ENAM = 'enam' , // Eastern North America
68- SAM = 'sam' , // South America
69- WEUR = 'weur' , // Western Europe
70- EEUR = 'eeur' , // Eastern Europe
71- APAC = 'apac' , // Asia Pacific
72- OC = 'oc' , // Oceania
73- AFR = 'afr' , // Africa
74- ME = 'me' , // Middle East
75- }
76-
7740export default {
7841 /**
7942 * This is the standard fetch handler for a Cloudflare Worker
@@ -84,67 +47,73 @@ export default {
8447 * @returns The response to be sent back to the client
8548 */
8649 async fetch ( request , env , ctx ) : Promise < Response > {
87- const url = new URL ( request . url ) ;
88- const pathname = url . pathname ;
89- const isWebSocket = request . headers . get ( "Upgrade" ) === "websocket" ;
90-
91- /**
92- * If the request is a GET request to the /studio endpoint, we can handle the request
93- * directly in the Worker to avoid the need to deploy a separate Worker for the Studio.
94- * Studio provides a user interface to interact with the SQLite database in the Durable
95- * Object.
96- */
97- if ( env . STUDIO_USER && env . STUDIO_PASS && request . method === 'GET' && pathname === '/studio' ) {
98- return handleStudioRequest ( request , {
99- username : env . STUDIO_USER ,
100- password : env . STUDIO_PASS ,
101- apiToken : env . AUTHORIZATION_TOKEN
102- } ) ;
103- }
50+ try {
51+ const url = new URL ( request . url ) ;
52+ const pathname = url . pathname ;
53+ const isWebSocket = request . headers . get ( "Upgrade" ) === "websocket" ;
10454
105- /**
106- * Prior to proceeding to the Durable Object, we can perform any necessary validation or
107- * authorization checks here to ensure the request signature is valid and authorized to
108- * interact with the Durable Object.
109- */
110- if ( request . headers . get ( 'Authorization' ) !== `Bearer ${ env . AUTHORIZATION_TOKEN } ` && ! isWebSocket ) {
111- return createResponse ( undefined , 'Unauthorized request' , 401 )
112- } else if ( isWebSocket ) {
11355 /**
114- * Web socket connections cannot pass in an Authorization header into their requests,
115- * so we can use a query parameter to validate the connection.
56+ * If the request is a GET request to the /studio endpoint, we can handle the request
57+ * directly in the Worker to avoid the need to deploy a separate Worker for the Studio.
58+ * Studio provides a user interface to interact with the SQLite database in the Durable
59+ * Object.
11660 */
117- const token = url . searchParams . get ( 'token' ) ;
118-
119- if ( token !== env . AUTHORIZATION_TOKEN ) {
120- return new Response ( 'WebSocket connections are not supported at this endpoint.' , { status : 440 } ) ;
61+ if ( env . STUDIO_USER && env . STUDIO_PASS && request . method === 'GET' && pathname === '/studio' ) {
62+ return handleStudioRequest ( request , {
63+ username : env . STUDIO_USER ,
64+ password : env . STUDIO_PASS ,
65+ apiToken : env . AUTHORIZATION_TOKEN
66+ } ) ;
12167 }
122- }
123-
124- /**
125- * Retrieve the Durable Object identifier from the environment bindings and instantiate a
126- * Durable Object stub to interact with the Durable Object.
127- */
128- const region = env . REGION ?? RegionLocationHint . AUTO ;
129- const id : DurableObjectId = env . DATABASE_DURABLE_OBJECT . idFromName ( DURABLE_OBJECT_ID ) ;
130- const stub = region !== RegionLocationHint . AUTO ? env . DATABASE_DURABLE_OBJECT . get ( id , { locationHint : region as DurableObjectLocationHint } ) : env . DATABASE_DURABLE_OBJECT . get ( id ) ;
13168
132- const source : Source = request . headers . get ( 'X-Starbase-Source' ) as Source ?? url . searchParams . get ( 'source' ) as Source ?? 'internal' ;
133- const dataSource : DataSource = {
134- source : source ,
135- request : request . clone ( ) ,
136- internalConnection : {
137- durableObject : stub as unknown as DatabaseStub ,
138- } ,
139- externalConnection : {
140- outerbaseApiKey : env . OUTERBASE_API_KEY ?? ''
69+ /**
70+ * Prior to proceeding to the Durable Object, we can perform any necessary validation or
71+ * authorization checks here to ensure the request signature is valid and authorized to
72+ * interact with the Durable Object.
73+ */
74+ if ( request . headers . get ( 'Authorization' ) !== `Bearer ${ env . AUTHORIZATION_TOKEN } ` && ! isWebSocket ) {
75+ return createResponse ( undefined , 'Unauthorized request' , 401 )
76+ } else if ( isWebSocket ) {
77+ /**
78+ * Web socket connections cannot pass in an Authorization header into their requests,
79+ * so we can use a query parameter to validate the connection.
80+ */
81+ const token = url . searchParams . get ( 'token' ) ;
82+
83+ if ( token !== env . AUTHORIZATION_TOKEN ) {
84+ return new Response ( 'WebSocket connections are not supported at this endpoint.' , { status : 440 } ) ;
85+ }
14186 }
142- } ;
14387
144- const response = await new Handler ( ) . handle ( request , dataSource , env ) ;
145-
146- // ## DO NOT REMOVE: TEMPLATE ROUTING ##
147-
148- return response ;
88+ /**
89+ * Retrieve the Durable Object identifier from the environment bindings and instantiate a
90+ * Durable Object stub to interact with the Durable Object.
91+ */
92+ const region = env . REGION ?? RegionLocationHint . AUTO ;
93+ const id : DurableObjectId = env . DATABASE_DURABLE_OBJECT . idFromName ( DURABLE_OBJECT_ID ) ;
94+ const stub = region !== RegionLocationHint . AUTO ? env . DATABASE_DURABLE_OBJECT . get ( id , { locationHint : region as DurableObjectLocationHint } ) : env . DATABASE_DURABLE_OBJECT . get ( id ) ;
95+
96+ const source : Source = request . headers . get ( 'X-Starbase-Source' ) as Source ?? url . searchParams . get ( 'source' ) as Source ?? 'internal' ;
97+ const dataSource : DataSource = {
98+ source : source ,
99+ request : request . clone ( ) ,
100+ internalConnection : {
101+ durableObject : stub as unknown as DatabaseStub ,
102+ } ,
103+ externalConnection : {
104+ outerbaseApiKey : env . OUTERBASE_API_KEY ?? ''
105+ }
106+ } ;
107+
108+ const response = await new Handler ( ) . handle ( request , dataSource , env ) ;
109+ return response ;
110+ } catch ( error ) {
111+ // Return error response to client
112+ return createResponse (
113+ undefined ,
114+ error instanceof Error ? error . message : 'An unexpected error occurred' ,
115+ 400
116+ ) ;
117+ }
149118 } ,
150119} satisfies ExportedHandler < Env > ;
0 commit comments