Skip to content

Commit

Permalink
Migrate handler to use config
Browse files Browse the repository at this point in the history
refctor: Migrate handler to use config
  • Loading branch information
Brayden authored Dec 16, 2024
2 parents 460bc21 + 0a02342 commit ec1ee16
Show file tree
Hide file tree
Showing 13 changed files with 139 additions and 89 deletions.
6 changes: 3 additions & 3 deletions src/allowlist/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Env } from "..";
import { HandlerConfig } from "../handler";
import { DataSource } from "../types";

const parser = new (require('node-sql-parser').Parser)();
Expand All @@ -25,15 +25,15 @@ async function loadAllowlist(dataSource?: DataSource): Promise<string[]> {
}
}

export async function isQueryAllowed(sql: string, isEnabled: boolean, dataSource?: DataSource, env?: Env): Promise<boolean | Error> {
export async function isQueryAllowed(sql: string, isEnabled: boolean, dataSource?: DataSource, config?: HandlerConfig): Promise<boolean | Error> {
// If the feature is not turned on then by default the query is allowed
if (!isEnabled) return true;

// If we are using the administrative AUTHORIZATION token value, this request is allowed.
// We want database UI's to be able to have more free reign to run queries so we can load
// tables, run queries, and more. If you want to block queries with the allowlist then we
// advise you to do so by implementing user authentication with JWT.
if (dataSource?.request.headers.get('Authorization') === `Bearer ${env?.ADMIN_AUTHORIZATION_TOKEN}`) {
if (dataSource?.request.headers.get('Authorization') === `Bearer ${config?.adminAuthorizationToken}`) {
return true;
}

Expand Down
2 changes: 1 addition & 1 deletion src/export/csv.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { getTableData, createExportResponse } from './index';
import { createResponse } from '../utils';
import { DataSource } from '..';
import { DataSource } from '../types';

export async function exportTableToCsvRoute(
tableName: string,
Expand Down
2 changes: 1 addition & 1 deletion src/export/dump.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { executeOperation } from '.';
import { DataSource } from '..';
import { DataSource } from '../types';
import { createResponse } from '../utils';

export async function dumpDatabaseRoute(
Expand Down
2 changes: 1 addition & 1 deletion src/export/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { DataSource } from "..";
import { DataSource } from "../types";
import { executeTransaction } from "../operation";

export async function executeOperation(queries: { sql: string, params?: any[] }[], dataSource: DataSource): Promise<any> {
Expand Down
2 changes: 1 addition & 1 deletion src/export/json.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { getTableData, createExportResponse } from './index';
import { createResponse } from '../utils';
import { DataSource } from '..';
import { DataSource } from '../types';

export async function exportTableToJsonRoute(
tableName: string,
Expand Down
40 changes: 34 additions & 6 deletions src/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,45 @@ import { importDumpRoute } from "./import/dump";
import { importTableFromJsonRoute } from "./import/json";
import { importTableFromCsvRoute } from "./import/csv";

export interface HandlerConfig {
adminAuthorizationToken: string;
outerbaseApiKey?: string;
enableAllowlist?: boolean;
enableRls?: boolean;

externalDbType?: string;

externalDbHost?: string;
externalDbPort?: number;
externalDbUser?: string;
externalDbPassword?: string;
externalDbName?: string;
externalDbDefaultSchema?: string;

externalDbMongodbUri?: string;

externalDbTursoUri?: string;
externalDbTursoToken?: string;

externalDbCloudflareApiKey?: string;
externalDbCloudflareAccountId?: string;
externalDbCloudflareDatabaseId?: string;

externalDbStarbaseUri?: string;
externalDbStarbaseToken?: string;
}

export class Handler {
private liteREST?: LiteREST;
private dataSource?: DataSource;
private env?: Env;
private config?: HandlerConfig;

constructor() { }

public async handle(request: Request, dataSource: DataSource, env: Env): Promise<Response> {
public async handle(request: Request, dataSource: DataSource, config: HandlerConfig): Promise<Response> {
this.dataSource = dataSource;
this.liteREST = new LiteREST(dataSource, env);
this.env = env;
this.liteREST = new LiteREST(dataSource, config);
this.config = config;
const url = new URL(request.url);

if (request.method === 'POST' && url.pathname === '/query/raw') {
Expand Down Expand Up @@ -113,15 +141,15 @@ export class Handler {
return { sql, params };
});

const response = await executeTransaction(queries, isRaw, this.dataSource, this.env);
const response = await executeTransaction(queries, isRaw, this.dataSource, this.config);
return createResponse(response, undefined, 200);
} else if (typeof sql !== 'string' || !sql.trim()) {
return createResponse(undefined, 'Invalid or empty "sql" field.', 400);
} else if (params !== undefined && !Array.isArray(params) && typeof params !== 'object') {
return createResponse(undefined, 'Invalid "params" field. Must be an array or object.', 400);
}

const response = await executeQuery(sql, params, isRaw, this.dataSource, this.env);
const response = await executeQuery(sql, params, isRaw, this.dataSource, this.config);
return createResponse(response, undefined, 200);
} catch (error: any) {
console.error('Query Route Error:', error);
Expand Down
2 changes: 1 addition & 1 deletion src/import/csv.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { createResponse } from '../utils';
import { DataSource } from '..';
import { DataSource } from '../types';
import { executeOperation } from '../export';

interface ColumnMapping {
Expand Down
2 changes: 1 addition & 1 deletion src/import/dump.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { createResponse } from '../utils';
import { DataSource } from '..';
import { DataSource } from '../types';
import { executeOperation } from '../export';

function parseSqlStatements(sqlContent: string): string[] {
Expand Down
2 changes: 1 addition & 1 deletion src/import/json.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { createResponse } from '../utils';
import { executeOperation } from '../export';
import { DataSource } from '..';
import { DataSource } from '../types';

interface ColumnMapping {
[key: string]: string;
Expand Down
26 changes: 24 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { createResponse } from './utils';
import handleStudioRequest from "./studio";
import { Handler } from "./handler";
import { Handler, HandlerConfig } from "./handler";
import { DatabaseStub, DataSource, RegionLocationHint, Source } from './types';
import { corsPreflight } from './cors';
export { DatabaseDurableObject } from './do';
Expand Down Expand Up @@ -163,8 +163,30 @@ export default {
// Non-blocking operation to remove expired cache entries from our DO
expireCache(dataSource)

const config = {
adminAuthorizationToken: env.ADMIN_AUTHORIZATION_TOKEN,
outerbaseApiKey: env.OUTERBASE_API_KEY,
enableAllowlist: env.ENABLE_ALLOWLIST,
enableRls: env.ENABLE_RLS,
externalDbType: env.EXTERNAL_DB_TYPE,
externalDbHost: env.EXTERNAL_DB_HOST,
externalDbPort: env.EXTERNAL_DB_PORT,
externalDbUser: env.EXTERNAL_DB_USER,
externalDbPassword: env.EXTERNAL_DB_PASS,
externalDbName: env.EXTERNAL_DB_DATABASE,
externalDbDefaultSchema: env.EXTERNAL_DB_DEFAULT_SCHEMA,
externalDbMongodbUri: env.EXTERNAL_DB_MONGODB_URI,
externalDbTursoUri: env.EXTERNAL_DB_TURSO_URI,
externalDbTursoToken: env.EXTERNAL_DB_TURSO_TOKEN,
externalDbCloudflareApiKey: env.EXTERNAL_DB_CLOUDFLARE_API_KEY,
externalDbCloudflareAccountId: env.EXTERNAL_DB_CLOUDFLARE_ACCOUNT_ID,
externalDbCloudflareDatabaseId: env.EXTERNAL_DB_CLOUDFLARE_DATABASE_ID,
externalDbStarbaseUri: env.EXTERNAL_DB_STARBASEDB_URI,
externalDbStarbaseToken: env.EXTERNAL_DB_STARBASEDB_TOKEN,
} satisfies HandlerConfig;

// Return the final response to our user
return await new Handler().handle(request, dataSource, env);
return await new Handler().handle(request, dataSource, config);
} catch (error) {
// Return error response to client
return createResponse(
Expand Down
18 changes: 9 additions & 9 deletions src/literest/index.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import { createResponse } from '../utils';
import { DataSource, Source } from "../types";
import { executeQuery, executeTransaction } from "../operation";
import { Env } from "../index"
import { HandlerConfig } from "../handler"

export class LiteREST {
private dataSource: DataSource;
private env: Env;
private config: HandlerConfig;

constructor(
dataSource: DataSource,
env: Env
config: HandlerConfig
) {
this.dataSource = dataSource;
this.env = env;
this.config = config;
}

/**
Expand All @@ -33,7 +33,7 @@ export class LiteREST {
let query = `PRAGMA table_info(${tableName});`;

if (this.dataSource.source === Source.external) {
if (this.env.EXTERNAL_DB_TYPE?.toLowerCase() === "postgres") {
if (this.config.externalDbType?.toLowerCase() === "postgres") {
query = `
SELECT kcu.column_name AS name
FROM information_schema.table_constraints tc
Expand All @@ -44,7 +44,7 @@ export class LiteREST {
WHERE tc.constraint_type = 'PRIMARY KEY'
AND tc.table_name = '${tableName}'
AND tc.table_schema = '${schemaName ?? 'public'}';`;
} else if (this.env.EXTERNAL_DB_TYPE?.toLowerCase() === "mysql") {
} else if (this.config.externalDbType?.toLowerCase() === "mysql") {
query = `
SELECT COLUMN_NAME AS name FROM information_schema.key_column_usage
WHERE table_name = '${tableName}'
Expand All @@ -54,8 +54,8 @@ export class LiteREST {
}
}

const isSQLite = this.dataSource.source === Source.internal || this.env.EXTERNAL_DB_TYPE?.toLowerCase() === 'sqlite'
const schemaInfo = (await executeQuery(query, this.dataSource.source === Source.external ? {} : [], false, this.dataSource, this.env)) as any[];
const isSQLite = this.dataSource.source === Source.internal || this.config.externalDbType?.toLowerCase() === 'sqlite'
const schemaInfo = (await executeQuery(query, this.dataSource.source === Source.external ? {} : [], false, this.dataSource, this.config)) as any[];

let pkColumns = []

Expand Down Expand Up @@ -138,7 +138,7 @@ export class LiteREST {
* @param queries - The operations to execute.
*/
private async executeOperation(queries: { sql: string, params: any[] }[]): Promise<{ result?: any, error?: string | undefined, status: number }> {
const results: any[] = (await executeTransaction(queries, false, this.dataSource, this.env)) as any[];
const results: any[] = (await executeTransaction(queries, false, this.dataSource, this.config)) as any[];
return { result: results?.length > 0 ? results[0] : undefined, status: 200 };
}

Expand Down
Loading

0 comments on commit ec1ee16

Please sign in to comment.