Skip to content

Commit

Permalink
feat: save local sqlite session (#133)
Browse files Browse the repository at this point in the history
* feat: local sqlite session

* ask for permission if there is no permission before

* restructure and optimize page that does not require theme
  • Loading branch information
invisal authored Aug 7, 2024
1 parent 4a490d3 commit d54044d
Show file tree
Hide file tree
Showing 59 changed files with 281 additions and 48 deletions.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
22 changes: 22 additions & 0 deletions src/app/(public)/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Analytics } from "@vercel/analytics/react";
import { Inter } from "next/font/google";
import { Toaster } from "@/components/ui/sonner";
import { Fragment } from "react";
import Script from "next/script";

const inter = Inter({ subsets: ["latin"] });

export default async function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<body className={inter.className} suppressHydrationWarning>
<Fragment>{children}</Fragment>
<Toaster />
<Analytics />
<Script async defer src="https://buttons.github.io/buttons.js" />
</body>
);
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"use client";
import { SavedConnectionItem } from "@/app/connect/saved-connection-storage";
import { SavedConnectionItem } from "@/app/(theme)/connect/saved-connection-storage";
import CollaborationDriver from "@/drivers/collaboration-driver";
import RemoteDriver from "@/drivers/remote-driver";
import { useSearchParams } from "next/navigation";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { SavedConnectionLabel } from "@/app/connect/saved-connection-storage";
import type { SavedConnectionLabel } from "@/app/(theme)/connect/saved-connection-storage";
import { get_database } from "@/db";
import { database } from "@/db/schema";
import { getSessionFromCookie } from "@/lib/auth";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"use client";
import { SavedConnectionLocalStorage } from "@/app/connect/saved-connection-storage";
import { SavedConnectionLocalStorage } from "@/app/(theme)/connect/saved-connection-storage";
import RqliteDriver from "@/drivers/rqlite-driver";
import TursoDriver from "@/drivers/turso-driver";
import ValtownDriver from "@/drivers/valtown-driver";
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
SavedConnectionItem,
SavedConnectionLocalStorage,
SupportedDriver,
} from "@/app/connect/saved-connection-storage";
} from "@/app/(theme)/connect/saved-connection-storage";
import EditSavedConnection from "./saved-edit-connection";
import RemoveSavedConnection from "./saved-remove-connection";
import ConnectionItemCard from "./saved-connection-card";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
SupportedDriver,
} from "./saved-connection-storage";
import { Input } from "@/components/ui/input";
import FileHandlerPicker from "@/components/filehandler-picker";

export default function ConnectionStringInput({
value,
Expand Down Expand Up @@ -65,6 +66,15 @@ export default function ConnectionStringInput({
}}
/>
);
} else if (field.type === "filehandler") {
inputDom = (
<FileHandlerPicker
value={value[field.name]}
onChange={(fileId) => {
onChange({ ...value, [field.name]: fileId });
}}
/>
);
}

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,18 +80,32 @@ export default function DriverDropdown({
</div>
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem
onClick={() => {
onSelect("sqlite-filehandler");
}}
>
<div className="flex gap-4 px-2 items-center h-16">
<img src="/sqlite-icon.svg" alt="sqlite" className="w-9 h-9" />
<div>
<div className="font-bold">Open SQLite File</div>
<div className="text-xs opacity-50">
Open SQLite file database directly in your browser.
</div>
</div>
</div>
</DropdownMenuItem>
<DropdownMenuItem
onClick={() => {
router.push("/playground/client");
}}
>
<div className="flex gap-4 px-2 items-center h-16">
<img src="/sqlite-icon.svg" alt="rqlite" className="w-9 h-9" />
<img src="/sqlite-icon.svg" alt="sqlite" className="w-9 h-9" />
<div>
<div className="font-bold">SQLite</div>
<div className="font-bold">Blank SQLite</div>
<div className="text-xs opacity-50">
Open an SQLite file or start a new SQLite database directly in
your browser.
Start a new SQLite database directly in your browser.
</div>
</div>
</div>
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,9 @@ export default function ConnectionItemCard({
)}
href={
conn.storage === "local"
? `/client/s/${conn.driver ?? "turso"}?p=${conn.id}`
? conn.driver === "sqlite-filehandler"
? `/playground/client?s=${conn.id}`
: `/client/s/${conn.driver ?? "turso"}?p=${conn.id}`
: `/client/r?p=${conn.id}`
}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
SupportedDriver,
prefillConnectionString,
validateConnectionString,
} from "@/app/connect/saved-connection-storage";
} from "@/app/(theme)/connect/saved-connection-storage";
import { cn } from "@/lib/utils";
import { useMemo, useState } from "react";
import { LucideLoader } from "lucide-react";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import parseSafeJson from "@/lib/json-safe";

export interface DriverDetailField {
name: keyof SavedConnectionItemConfigConfig;
type: "text" | "textarea" | "password";
type: "text" | "textarea" | "password" | "filehandler";
secret?: boolean;
required?: boolean;
title?: string;
Expand All @@ -16,11 +16,26 @@ export interface DriverDetailField {
export interface DriverDetail {
name: string;
icon: string;
disableRemote?: boolean;
fields: DriverDetailField[];
}

export const DRIVER_DETAIL: Record<SupportedDriver, DriverDetail> =
Object.freeze({
"sqlite-filehandler": {
name: "sqlite-filehandler",
icon: "/sqlite-icon.svg",
disableRemote: true,
fields: [
{
name: "filehandler",
required: true,
type: "filehandler",
title: "File",
description: "",
},
],
},
turso: {
name: "turso",
icon: "/turso.jpeg",
Expand Down Expand Up @@ -178,7 +193,12 @@ export function prefillConnectionString(
};
}

export type SupportedDriver = "turso" | "rqlite" | "valtown" | "cloudflare-d1";
export type SupportedDriver =
| "turso"
| "rqlite"
| "valtown"
| "cloudflare-d1"
| "sqlite-filehandler";
export type SavedConnectionStorage = "remote" | "local";
export type SavedConnectionLabel = "gray" | "red" | "yellow" | "green" | "blue";

Expand Down Expand Up @@ -208,6 +228,7 @@ export interface SavedConnectionItemConfigConfig {
username?: string;
password?: string;
database?: string;
filehandler?: string;
}

export interface SavedConnectionItemConfig {
Expand Down Expand Up @@ -236,6 +257,7 @@ interface SavedConnectionRawLocalStorage {
database: string;
driver?: SupportedDriver;
label?: SavedConnectionLabel;
file_handler?: string;
description?: string;
last_used: number;
}
Expand All @@ -250,6 +272,7 @@ function configToRaw(
driver: data.driver ?? "turso",
url: data.config?.url ?? "",
token: data.config?.token ?? "",
file_handler: data.config?.filehandler,
username: data.config?.username ?? "",
password: data.config?.password ?? "",
database: data.config?.database ?? "",
Expand Down Expand Up @@ -286,6 +309,7 @@ function mapDetailRaw(
password: data.password,
username: data.username,
database: data.database,
filehandler: data.file_handler,
},
};
}
Expand All @@ -305,6 +329,13 @@ export class SavedConnectionLocalStorage {
).map(mapRaw);
}

static getDetailList(): SavedConnectionItemDetail[] {
return parseSafeJson<SavedConnectionRawLocalStorage[]>(
localStorage.getItem("connections"),
[]
).map(mapDetailRaw);
}

static remove(id: string) {
const tmp = getAllConnections().filter((conn) => conn.id !== id);
localStorage.setItem("connections", JSON.stringify(tmp));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Button } from "@/components/ui/button";
import { SavedConnectionStorage } from "@/app/connect/saved-connection-storage";
import { SavedConnectionStorage } from "@/app/(theme)/connect/saved-connection-storage";
import { cn } from "@/lib/utils";
import { useState } from "react";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ import { useCallback, useState } from "react";
import ConnectionDialogContent from "./saved-connection-content";
import SaveConnectionType from "./saved-connection-type";
import {
DRIVER_DETAIL,
SavedConnectionItem,
SavedConnectionItemConfig,
SavedConnectionItemWithoutId,
SavedConnectionLocalStorage,
SavedConnectionStorage,
SupportedDriver,
} from "@/app/connect/saved-connection-storage";
} from "@/app/(theme)/connect/saved-connection-storage";
import SavedConnectionConfig from "./saved-connection-config";
import { createDatabase } from "@/lib/api/fetch-databases";
import { User } from "lucia";
Expand Down Expand Up @@ -51,9 +52,11 @@ export default function SaveConnection({
const [storage, setStorage] = useState<SavedConnectionStorage | undefined>(
user ? undefined : "local"
);
const [step, setStep] = useState<SaveConnectionStep>(
user ? "storage" : "config"
);
const [step, setStep] = useState<SaveConnectionStep>(() => {
if (!user) return "config";
if (DRIVER_DETAIL[driver].disableRemote) return "config";
return "storage";
});
const [loading, setLoading] = useState(false);

const onConnectionTypeSelected = useCallback(
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
30 changes: 30 additions & 0 deletions src/app/(theme)/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Analytics } from "@vercel/analytics/react";
import { Inter } from "next/font/google";
import ThemeProvider from "@/context/theme-provider";
import { cookies } from "next/headers";
import { Toaster } from "@/components/ui/sonner";
import { Fragment } from "react";
import Script from "next/script";
import { cn } from "@/lib/utils";

const inter = Inter({ subsets: ["latin"] });

export default async function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
const cookieStore = cookies();
const theme = cookieStore.get("theme")?.value === "dark" ? "dark" : "light";

return (
<body className={cn(inter.className, theme)} suppressHydrationWarning>
<ThemeProvider defaultTheme={theme}>
<Fragment>{children}</Fragment>
<Toaster />
</ThemeProvider>
<Analytics />
<Script async defer src="https://buttons.github.io/buttons.js" />
</body>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,17 @@ import {
TooltipContent,
TooltipTrigger,
} from "@/components/ui/tooltip";
import { useSearchParams } from "next/navigation";
import { localDb } from "@/indexdb";
import { SavedConnectionLocalStorage } from "@/app/(theme)/connect/saved-connection-storage";

export default function PlaygroundEditorBody({
preloadDatabase,
}: {
preloadDatabase?: string | null;
}) {
const [sqlInit, setSqlInit] = useState<SqlJsStatic>();
const searchParams = useSearchParams();
const [databaseLoading, setDatabaseLoading] = useState(!!preloadDatabase);
const [rawDb, setRawDb] = useState<Database>();
const [db, setDb] = useState<SqljsDriver>();
Expand All @@ -46,13 +50,36 @@ export default function PlaygroundEditorBody({
setDb(new SqljsDriver(sqljsDatabase));
})
.finally(() => setDatabaseLoading(false));
} else if (searchParams.get("s")) {
const sessionId = searchParams.get("s");
if (!sessionId) return;

const session = SavedConnectionLocalStorage.get(sessionId);
if (!session) return;

const fileHandlerId = session?.config.filehandler;
if (!fileHandlerId) return;

localDb.file_handler.get(fileHandlerId).then((sessionData) => {
if (sessionData?.handler) {
sessionData.handler.queryPermission().then((permission) => {
if (permission !== "granted") {
sessionData.handler.requestPermission().then(() => {
setHandler(sessionData.handler);
});
} else {
setHandler(sessionData.handler);
}
});
}
});
} else {
const sqljsDatabase = new sqlInit.Database();
setRawDb(sqljsDatabase);
setDb(new SqljsDriver(sqljsDatabase));
}
}
}, [sqlInit, preloadDatabase]);
}, [sqlInit, preloadDatabase, searchParams]);

useEffect(() => {
if (handler && sqlInit) {
Expand Down Expand Up @@ -203,7 +230,7 @@ export default function PlaygroundEditorBody({
</div>
</div>
);
}, [rawDb, handler, db, fileName]);
}, [rawDb, handler, db, fileName, onReloadDatabase]);

useEffect(() => {
if (handler && db) {
Expand Down
File renamed without changes.
2 changes: 1 addition & 1 deletion src/app/api/database/[database_id]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { database } from "@/db/schema";
import { eq } from "drizzle-orm";
import { encrypt } from "@/lib/encryption-edge";
import { env } from "@/env";
import { SavedConnectionItemConfig } from "@/app/connect/saved-connection-storage";
import { SavedConnectionItemConfig } from "@/app/(theme)/connect/saved-connection-storage";
import { get_database } from "@/db";

export const runtime = "edge";
Expand Down
2 changes: 1 addition & 1 deletion src/app/api/database/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { database, database_role, database_user_role } from "@/db/schema";
import { NextResponse } from "next/server";
import { env } from "@/env";
import { encrypt } from "@/lib/encryption-edge";
import { SavedConnectionItem } from "@/app/connect/saved-connection-storage";
import { SavedConnectionItem } from "@/app/(theme)/connect/saved-connection-storage";
import { get_database } from "@/db";

export const runtime = "edge";
Expand Down
2 changes: 1 addition & 1 deletion src/app/api/databases/route.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { SavedConnectionItem } from "@/app/connect/saved-connection-storage";
import { SavedConnectionItem } from "@/app/(theme)/connect/saved-connection-storage";
import { get_database } from "@/db";
import { database, database_user_role, user as userTable } from "@/db/schema";
import withUser from "@/lib/with-user";
Expand Down
2 changes: 1 addition & 1 deletion src/app/api/temp_ops/[session_id]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ 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";
import { SavedConnectionItemConfigConfig } from "@/app/(theme)/connect/saved-connection-storage";

export const runtime = "edge";

Expand Down
Loading

0 comments on commit d54044d

Please sign in to comment.