Skip to content

Commit

Permalink
support cloudflare worker analytic engine
Browse files Browse the repository at this point in the history
  • Loading branch information
invisal committed Mar 3, 2025
1 parent 39830fb commit 0fee61b
Show file tree
Hide file tree
Showing 8 changed files with 348 additions and 251 deletions.
5 changes: 5 additions & 0 deletions src/app/(outerbase)/new-resource-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ export function getCreateResourceTypeList(
icon: CloudflareIcon,
href: workspaceId ? "" : "/local/new-base/cloudflare-d1",
},
{
name: "Worker Analytics Engine",
icon: CloudflareIcon,
href: workspaceId ? "" : "/local/new-base/cloudflare-wae",
},
{
name: "Neon",
icon: NeonIcon,
Expand Down
251 changes: 1 addition & 250 deletions src/app/(theme)/connect/saved-connection-storage.ts
Original file line number Diff line number Diff line change
@@ -1,253 +1,12 @@
import {
CloudflareIcon,
RqliteIcon,
SQLiteIcon,
StarbaseIcon,
TursoIcon,
ValtownIcon,
} from "@/components/icons/outerbase-icon";
import { ApiUser } from "@/lib/api/api-database-response";
import { FunctionComponent } from "react";

export interface DriverDetailField {
name: keyof SavedConnectionItemConfigConfig;
type: "text" | "textarea" | "password" | "filehandler";
secret?: boolean;
required?: boolean;
title?: string;
placeholder?: string;
description?: string;
prefill?: string;
invalidate?: (value: string) => string | null;
}

export interface DriverDetail {
displayName: string;
name: string;
icon: FunctionComponent<{ className: string }>;
disableRemote?: boolean;
fields: DriverDetailField[];
}

export const DRIVER_DETAIL: Record<SupportedDriver, DriverDetail> =
Object.freeze({
"sqlite-filehandler": {
displayName: "SQLite",
name: "sqlite-filehandler",
icon: SQLiteIcon,
disableRemote: true,
fields: [
{
name: "filehandler",
required: true,
type: "filehandler",
title: "File",
description: "",
},
],
},
turso: {
name: "turso",
displayName: "Turso",
icon: TursoIcon,
fields: [
{
name: "url",
required: true,
type: "text",
title: "URL",
description: "Example: libsql://example.turso.io",
invalidate: (url: string): null | string => {
const trimmedUrl = url.trim();
const valid =
trimmedUrl.startsWith("https://") ||
trimmedUrl.startsWith("http://") ||
trimmedUrl.startsWith("ws://") ||
trimmedUrl.startsWith("wss://") ||
trimmedUrl.startsWith("libsql://");

if (!valid) {
return "Endpoint must start with libsql://, https://, http://, wss:// or ws://";
}

return null;
},
},
{ name: "token", title: "Token", type: "textarea", secret: true },
],
},
valtown: {
name: "valtown",
displayName: "Valtown",
icon: ValtownIcon,
prefill: "",
fields: [
{
name: "token",
title: "API Token",
required: true,
type: "text",
secret: true,
},
],
},
starbase: {
name: "starbase",
displayName: "StarbaseDB",
icon: StarbaseIcon,
disableRemote: true,
prefill: "",
fields: [
{
name: "url",
title: "Endpoint",
required: true,
type: "text",
secret: false,
invalidate: (url: string): null | string => {
const trimmedUrl = url.trim();
const valid =
trimmedUrl.startsWith("https://") ||
trimmedUrl.startsWith("http://");

if (!valid) {
return "Endpoint must start with https:// or http://";
}

return null;
},
},
{
name: "token",
title: "API Token",
required: true,
type: "text",
secret: true,
},
],
},
"cloudflare-d1": {
name: "cloudflare-d1",
displayName: "Cloudflare D1",
icon: CloudflareIcon,
fields: [
{
name: "username",
type: "text",
title: "Account ID",
required: true,
placeholder: "Account ID",
},
{
name: "database",
type: "text",
title: "Database ID",
required: true,
placeholder: "Database ID",
},
{
name: "token",
title: "API Token",
required: true,
type: "text",
secret: true,
},
],
},
rqlite: {
name: "rqlite",
displayName: "rqlite",
icon: RqliteIcon,
fields: [
{
name: "url",
required: true,
type: "text",
title: "URL",
prefill: "http://localhost:4001",
description: "Example: http://localhost:4001",
invalidate: (url: string): null | string => {
const trimmedUrl = url.trim();
const valid =
trimmedUrl.startsWith("https://") ||
trimmedUrl.startsWith("http://");

if (!valid) {
return "Endpoint must start with https://, http://";
}

return null;
},
},
{
name: "username",
type: "text",
title: "Username",
placeholder: "Username",
},
{
name: "password",
type: "password",
title: "Password",
secret: true,
placeholder: "Password",
},
],
},
});

export function validateConnectionString(
driver: DriverDetail,
connectionString?: SavedConnectionItemConfigConfig
) {
if (!connectionString) return false;

for (const field of driver.fields) {
if (
field.invalidate &&
field.invalidate(connectionString[field.name] ?? "")
) {
return false;
}
}

return true;
}

export function prefillConnectionString(
driver: DriverDetail,
defaultValue?: SavedConnectionItemConfigConfig
): SavedConnectionItemConfigConfig {
return {
url:
defaultValue?.url ??
driver.fields.find((f) => f.name === "url")?.prefill ??
"",
token:
defaultValue?.token ??
driver.fields.find((f) => f.name === "token")?.prefill ??
"",
database:
defaultValue?.database ??
driver.fields.find((f) => f.name === "database")?.prefill ??
"",
username:
defaultValue?.username ??
driver.fields.find((f) => f.name === "username")?.prefill ??
"",
password:
defaultValue?.token ??
driver.fields.find((f) => f.name === "password")?.prefill ??
"",
};
}

export type SupportedDriver =
| "turso"
| "rqlite"
| "valtown"
| "starbase"
| "cloudflare-d1"
| "cloudflare-wae"
| "sqlite-filehandler";

export type SavedConnectionStorage = "remote" | "local";
Expand Down Expand Up @@ -290,14 +49,6 @@ export interface SavedConnectionItemConfig {
config: SavedConnectionItemConfigConfig;
}

export type SavedConnectionItemWithoutId = {
storage: SavedConnectionStorage;
} & SavedConnectionItemConfig;

export type SavedConnectionItemDetail = {
id: string;
} & SavedConnectionItemWithoutId;

export interface SavedConnectionRawLocalStorage {
id?: string;
name: string;
Expand Down
60 changes: 60 additions & 0 deletions src/app/proxy/wae/route.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { HttpStatus } from "@/constants/http-status";
import { headers } from "next/headers";
import { NextRequest, NextResponse } from "next/server";

export async function POST(req: NextRequest) {
const headerStore = await headers();

// Get the account id and database id from header
const accountId = headerStore.get("x-account-id");

if (!accountId) {
return NextResponse.json(
{
error: "Please provide account id or database id",
},
{ status: HttpStatus.BAD_REQUEST }
);
}

const authorizationHeader = headerStore.get("Authorization");
if (!authorizationHeader) {
return NextResponse.json(
{
error: "Please provide authorization header",
},
{ status: HttpStatus.BAD_REQUEST }
);
}

try {
const url = `https://api.cloudflare.com/client/v4/accounts/${accountId}/analytics_engine/sql`;

const response = await fetch(url, {
method: "POST",
headers: {
Authorization: authorizationHeader,
"Content-Type": "text/plain",
},
body: await req.text(),
});

if (!response.ok) {
return NextResponse.json(
{
error: await response.text(),
},
{ status: response.status }
);
}

return NextResponse.json(await response.json());
} catch (e) {
return NextResponse.json(
{
error: (e as Error).message,
},
{ status: HttpStatus.BAD_REQUEST }
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { ConnectionTemplateList } from "@/app/(outerbase)/base-template";
import { CommonConnectionConfigTemplate } from "..";

const template: CommonConnectionConfigTemplate = [
{
columns: [
{
name: "username",
label: "Account ID",
type: "text",
required: true,
placeholder: "Account ID",
},
],
},
{
columns: [
{
name: "token",
label: "API Token",
type: "textarea",
required: true,
placeholder: "API Token",
},
],
},
];

export const CloudflareWAEConnectionTemplate: ConnectionTemplateList = {
localFrom: (value) => {
return {
name: value.name,
database: value.database,
token: value.token,
username: value.username,
};
},
localTo: (value) => {
return {
name: value.name,
driver: "cloudflare-wae",
database: value.database,
token: value.token,
username: value.username,
};
},
template,
instruction: <div></div>,
};
Loading

0 comments on commit 0fee61b

Please sign in to comment.