Skip to content

Commit

Permalink
feat: use remote connection instead of pass real credential to client (
Browse files Browse the repository at this point in the history
  • Loading branch information
invisal authored Jul 4, 2024
1 parent cc5cdc4 commit e2bd729
Show file tree
Hide file tree
Showing 8 changed files with 136 additions and 63 deletions.
34 changes: 34 additions & 0 deletions src/app/api/ops/[database_id]/handler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { RequestOperationBody } from "@/lib/api/api-request-types";
import { DatabaseOperationHandler } from "@/lib/with-database-ops";
import handleBatchRequest from "./handle-batch";
import handleQueryRequest from "./handle-query";
import handleSchemasRequest from "./handle-schemas";
import handleSelectTableRequest from "./handle-select-table";
import handleUpdateTableDataRequest from "./handle-update-table-data";
import handleSchemaRequest from "./handle-schema";
import handleTriggerRequest from "./handle-trigger";
import { NextResponse } from "next/server";

export const databaseOperationHandler: DatabaseOperationHandler<
RequestOperationBody
> = async (props) => {
const body = props.body;

if (body.type === "batch") {
return await handleBatchRequest({ ...props, body });
} else if (body.type === "query") {
return await handleQueryRequest({ ...props, body });
} else if (body.type === "schemas") {
return await handleSchemasRequest(props);
} else if (body.type === "select-table") {
return await handleSelectTableRequest({ ...props, body });
} else if (body.type === "update-table-data") {
return await handleUpdateTableDataRequest({ ...props, body });
} else if (body.type === "schema") {
return await handleSchemaRequest({ ...props, body });
} else if (body.type === "trigger") {
return await handleTriggerRequest({ ...props, body });
}

return NextResponse.json({ error: "Unknown command" }, { status: 500 });
};
31 changes: 2 additions & 29 deletions src/app/api/ops/[database_id]/route.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,9 @@
import { NextResponse } from "next/server";
import withDatabaseOperation from "@/lib/with-database-ops";
import handleBatchRequest from "./handle-batch";
import handleQueryRequest from "./handle-query";
import handleSchemasRequest from "./handle-schemas";
import handleSelectTableRequest from "./handle-select-table";
import handleUpdateTableDataRequest from "./handle-update-table-data";
import handleSchemaRequest from "./handle-schema";
import { RequestOperationBody } from "@/lib/api/api-request-types";
import handleTriggerRequest from "./handle-trigger";
import { databaseOperationHandler } from "./handler";

export const runtime = "edge";

export const POST = withDatabaseOperation<RequestOperationBody>(
async function (props) {
const body = props.body;

if (body.type === "batch") {
return await handleBatchRequest({ ...props, body });
} else if (body.type === "query") {
return await handleQueryRequest({ ...props, body });
} else if (body.type === "schemas") {
return await handleSchemasRequest(props);
} else if (body.type === "select-table") {
return await handleSelectTableRequest({ ...props, body });
} else if (body.type === "update-table-data") {
return await handleUpdateTableDataRequest({ ...props, body });
} else if (body.type === "schema") {
return await handleSchemaRequest({ ...props, body });
} else if (body.type === "trigger") {
return await handleTriggerRequest({ ...props, body });
}

return NextResponse.json({ error: "Unknown command" }, { status: 500 });
}
databaseOperationHandler
);
78 changes: 78 additions & 0 deletions src/app/api/temp_ops/[session_id]/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { headers } from "next/headers";
import { databaseOperationHandler } from "../../ops/[database_id]/handler";
import { get_database } from "@/db";
import { eq } from "drizzle-orm";
import { dbTempSession } from "@/db/schema-temp-session";
import withErrorHandler from "@/lib/with-error-handler";
import { ApiError } from "@/lib/api-error";
import { HttpStatus } from "@/constants/http-status";
import parseSafeJson from "@/lib/json-safe";
import { SavedConnectionItemConfigConfig } from "@/app/connect/saved-connection-storage";

export const runtime = "edge";

export const POST = withErrorHandler<{ params: { session_id: string } }>(
async function ({ req, params }) {
const db = get_database();
const session = await db.query.dbTempSession.findFirst({
where: eq(dbTempSession.id, params.params.session_id),
});

if (!session) {
throw new ApiError({
message: "Session does not exist",
status: HttpStatus.BAD_REQUEST,
});
}

const now = Math.floor(Date.now() / 1000);
if ((session.expiredAt ?? 0) < now) {
throw new ApiError({
message: "Session has expired",
status: HttpStatus.BAD_REQUEST,
});
}

const body =
headers().get("Content-Type") === "application/json"
? await req.json()
: {};

const config = parseSafeJson<SavedConnectionItemConfigConfig | null>(
session.credential ?? "",
null
);

if (config === null) {
throw new ApiError({
message: "Problem parsing the credential",
status: HttpStatus.BAD_REQUEST,
});
}

return await databaseOperationHandler({
body,
permission: { canExecuteQuery: true, isOwner: true, roles: [] },
database: {
driver: session.driver,
host: config.url,
password: config.password ?? null,
token: config.token,
color: null,
createdAt: null,
deletedAt: null,
description: "",
id: "",
name: "",
userId: "",
username: config.username ?? null,
},
user: {
id: "0",
name: "Guest",
picture: "",
storageUsage: 0,
},
});
}
);
7 changes: 6 additions & 1 deletion src/app/api/temp_session/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ import { HttpStatus } from "@/constants/http-status";
import { get_database } from "@/db";
import { dbTempSession } from "@/db/schema";
import { ApiError } from "@/lib/api-error";
import { encrypt } from "@/lib/encryption-edge";
import withErrorHandler from "@/lib/with-error-handler";
import { generateId } from "lucia";
import { NextResponse } from "next/server";
import zod from "zod";
import { env } from "@/env";

export const runtime = "edge";

Expand Down Expand Up @@ -52,7 +54,10 @@ export const POST = withErrorHandler(async ({ req }) => {
id: tempSessionId,
name: body.name ?? "Temp Session",
driver: body.driver,
credential: JSON.stringify({ url: body.url, token: body.token }),
credential: JSON.stringify({
url: body.url,
token: await encrypt(env.ENCRYPTION_KEY, body.token ?? ""),
}),
createdAt: now,
expiredAt: now + (body.duration ?? 60 * 60), // default to expire in one hour
});
Expand Down
2 changes: 1 addition & 1 deletion src/app/client/r/page-client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export default function ClientPageBody({
if (!databaseId) return { driver: null };

return {
driver: new RemoteDriver(databaseId, token),
driver: new RemoteDriver("remote", databaseId, token),
collaborator: new CollaborationDriver(databaseId, token),
};
}, [params, token]);
Expand Down
23 changes: 5 additions & 18 deletions src/app/client/temp_sess/page-client.tsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,22 @@
"use client";
import {
SavedConnectionItemConfigConfig,
SupportedDriver,
} from "@/app/connect/saved-connection-storage";
import { useMemo } from "react";
import MyStudio from "@/components/my-studio";
import RqliteDriver from "@/drivers/rqlite-driver";
import TursoDriver from "@/drivers/turso-driver";
import ValtownDriver from "@/drivers/valtown-driver";
import TemporarySession from "@/components/sidebar/temp-session-countdown";
import FeatureRequestSidebar from "@/components/sidebar/feature-request.tsx";
import RemoteDriver from "@/drivers/remote-driver";

export default function ClientPageBody({
config,
sessionId,
name,
expired,
}: Readonly<{
config: SavedConnectionItemConfigConfig & {
driver: SupportedDriver;
};
sessionId: string;
name: string;
expired: number;
}>) {
const driver = useMemo(() => {
if (config.driver === "rqlite") {
return new RqliteDriver(config.url, config.username, config.password);
} else if (config.driver === "valtown") {
return new ValtownDriver(config.token);
}
return new TursoDriver(config.url, config.token as string, true);
}, [config]);
return new RemoteDriver("temporary", sessionId, "");
}, [sessionId]);

const sidebar = useMemo(() => {
return (
Expand Down
13 changes: 1 addition & 12 deletions src/app/client/temp_sess/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
import type {
SavedConnectionItemConfigConfig,
SupportedDriver,
} from "@/app/connect/saved-connection-storage";
import { get_database } from "@/db";
import { dbTempSession } from "@/db/schema";
import { getSessionFromCookie } from "@/lib/auth";
Expand Down Expand Up @@ -39,18 +35,11 @@ export default async function SessionPage({
return <div className="p-4">Session Expired</div>;
}

const credential: SavedConnectionItemConfigConfig = JSON.parse(
sessionInfo.credential ?? ""
);

return (
<ClientPageBody
name={sessionInfo.name ?? "Temporary Session"}
expired={sessionInfo.expiredAt ?? 0}
config={{
driver: sessionInfo.driver as SupportedDriver,
...credential,
}}
sessionId={sessionId}
/>
);
}
11 changes: 9 additions & 2 deletions src/drivers/remote-driver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,25 @@ import { RequestOperationBody } from "@/lib/api/api-request-types";
export default class RemoteDriver implements BaseDriver {
protected id: string = "";
protected authToken = "";
protected type: "temporary" | "remote" = "remote";

constructor(id: string, authToken: string) {
constructor(type: "temporary" | "remote", id: string, authToken: string) {
this.id = id;
this.authToken = authToken;
this.type = type;
}

supportBigInt(): boolean {
return false;
}

protected async request<T = unknown>(body: RequestOperationBody) {
const r = await fetch(`/api/ops/${this.id}`, {
const url =
this.type === "temporary"
? `/api/temp_ops/${this.id}`
: `/api/ops/${this.id}`;

const r = await fetch(url, {
method: "POST",
headers: {
Authorization: "Bearer " + this.authToken,
Expand Down

0 comments on commit e2bd729

Please sign in to comment.