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' ;
@@ -35,43 +35,9 @@ export interface Env {
3535 EXTERNAL_DB_CLOUDFLARE_DATABASE_ID ?: string ;
3636
3737 // ## DO NOT REMOVE: TEMPLATE INTERFACE ##
38- }
39-
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
38+ RLS : {
39+ applyRLS ( sql : string , dialect ?: string ) : Promise < string | Error >
40+ }
7541}
7642
7743export default {
@@ -84,67 +50,73 @@ export default {
8450 * @returns The response to be sent back to the client
8551 */
8652 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- }
53+ try {
54+ const url = new URL ( request . url ) ;
55+ const pathname = url . pathname ;
56+ const isWebSocket = request . headers . get ( "Upgrade" ) === "websocket" ;
10457
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 ) {
11358 /**
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.
59+ * If the request is a GET request to the /studio endpoint, we can handle the request
60+ * directly in the Worker to avoid the need to deploy a separate Worker for the Studio.
61+ * Studio provides a user interface to interact with the SQLite database in the Durable
62+ * Object.
11663 */
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 } ) ;
64+ if ( env . STUDIO_USER && env . STUDIO_PASS && request . method === 'GET' && pathname === '/studio' ) {
65+ return handleStudioRequest ( request , {
66+ username : env . STUDIO_USER ,
67+ password : env . STUDIO_PASS ,
68+ apiToken : env . AUTHORIZATION_TOKEN
69+ } ) ;
12170 }
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 ) ;
13171
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 ?? ''
72+ /**
73+ * Prior to proceeding to the Durable Object, we can perform any necessary validation or
74+ * authorization checks here to ensure the request signature is valid and authorized to
75+ * interact with the Durable Object.
76+ */
77+ if ( request . headers . get ( 'Authorization' ) !== `Bearer ${ env . AUTHORIZATION_TOKEN } ` && ! isWebSocket ) {
78+ return createResponse ( undefined , 'Unauthorized request' , 401 )
79+ } else if ( isWebSocket ) {
80+ /**
81+ * Web socket connections cannot pass in an Authorization header into their requests,
82+ * so we can use a query parameter to validate the connection.
83+ */
84+ const token = url . searchParams . get ( 'token' ) ;
85+
86+ if ( token !== env . AUTHORIZATION_TOKEN ) {
87+ return new Response ( 'WebSocket connections are not supported at this endpoint.' , { status : 440 } ) ;
88+ }
14189 }
142- } ;
14390
144- const response = await new Handler ( ) . handle ( request , dataSource , env ) ;
145-
146- // ## DO NOT REMOVE: TEMPLATE ROUTING ##
147-
148- return response ;
91+ /**
92+ * Retrieve the Durable Object identifier from the environment bindings and instantiate a
93+ * Durable Object stub to interact with the Durable Object.
94+ */
95+ const region = env . REGION ?? RegionLocationHint . AUTO ;
96+ const id : DurableObjectId = env . DATABASE_DURABLE_OBJECT . idFromName ( DURABLE_OBJECT_ID ) ;
97+ const stub = region !== RegionLocationHint . AUTO ? env . DATABASE_DURABLE_OBJECT . get ( id , { locationHint : region as DurableObjectLocationHint } ) : env . DATABASE_DURABLE_OBJECT . get ( id ) ;
98+
99+ const source : Source = request . headers . get ( 'X-Starbase-Source' ) as Source ?? url . searchParams . get ( 'source' ) as Source ?? 'internal' ;
100+ const dataSource : DataSource = {
101+ source : source ,
102+ request : request . clone ( ) ,
103+ internalConnection : {
104+ durableObject : stub as unknown as DatabaseStub ,
105+ } ,
106+ externalConnection : {
107+ outerbaseApiKey : env . OUTERBASE_API_KEY ?? ''
108+ }
109+ } ;
110+
111+ const response = await new Handler ( ) . handle ( request , dataSource , env ) ;
112+ return response ;
113+ } catch ( error ) {
114+ // Return error response to client
115+ return createResponse (
116+ undefined ,
117+ error instanceof Error ? error . message : 'An unexpected error occurred' ,
118+ 400
119+ ) ;
120+ }
149121 } ,
150122} satisfies ExportedHandler < Env > ;
0 commit comments