diff --git a/.env.node.example b/.env.node.example index fb986f1..6e0ebc5 100644 --- a/.env.node.example +++ b/.env.node.example @@ -4,10 +4,10 @@ DATABASE_URL="file:./db.sqlite" # File Storage -FILE_STORAGE="base64" # Options: "base64"(default), "local", "r2" +FILE_STORAGE="base64" # Options: "base64"(default), "disk", "r2" # Local File Storage Configuration -FILE_STORAGE_LOCAL_PATH=".files" # Path to store files locally, default is .files directory +# FILE_STORAGE_DISK_PATH=".files" # Path to store files locally, default is .files directory # For more Environment Variables, see wrangler.toml [vars] # ... \ No newline at end of file diff --git a/src/app/components/dev/DatabaseStudio.tsx b/src/app/components/dev/DatabaseStudio.tsx index d1f097c..91dee12 100644 --- a/src/app/components/dev/DatabaseStudio.tsx +++ b/src/app/components/dev/DatabaseStudio.tsx @@ -1,9 +1,6 @@ import { Badge } from "@/app/components/ui/badge"; import { Button } from "@/app/components/ui/button"; -import { Card, CardContent, CardHeader, CardTitle } from "@/app/components/ui/card"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "@/app/components/ui/dialog"; -import { ScrollArea } from "@/app/components/ui/scroll-area"; -import { Separator } from "@/app/components/ui/separator"; import { Textarea } from "@/app/components/ui/textarea"; import { getDb } from "@/app/lib/db-client"; import { Database } from "lucide-react"; @@ -106,85 +103,6 @@ export function DatabaseStudio() { setIsOpen(false); }; - const renderTable = () => { - if (!result?.columns || !result?.rows) return null; - - return ( -
- - - - - {result.columns.map((column) => ( - - ))} - - - - {result.rows.map((row, rowIndex) => ( - - {result.columns!.map((column) => ( - - ))} - - ))} - -
- {column} -
- {row[column] !== null && row[column] !== undefined ? ( - String(row[column]) - ) : ( - NULL - )} -
-
-
- ); - }; - - const renderResult = () => { - if (!result) return null; - - return ( - - - Query Results -
- {result.executionTime && {result.executionTime.toFixed(2)}ms} - -
-
- - {result.error ? ( -
-

Error:

-

{result.error}

-
- ) : result.columns && result.rows ? ( -
-
- {result.rows.length} rows - {result.columns.length} columns -
- {renderTable()} -
- ) : ( -
-
- {typeof result.changes === "number" && Affected rows: {result.changes}} - {result.lastInsertRowid && Insert ID: {String(result.lastInsertRowid)}} -
-

Query execution completed

-
- )} -
-
- ); - }; - return ( <> {/* Floating Button */} @@ -207,7 +125,7 @@ export function DatabaseStudio() { -
+
{/* SQL Input Area */}

@@ -254,7 +172,7 @@ export function DatabaseStudio() { {/* Results Display Area - Fill remaining space */} {result ? ( -

+
{/* Results Header */}
@@ -269,7 +187,7 @@ export function DatabaseStudio() {
{/* Results Content - Fill remaining space */} -
+
{result.error ? (
@@ -289,7 +207,7 @@ export function DatabaseStudio() { {/* Table - Fill remaining space */}
- +
@@ -335,8 +253,7 @@ export function DatabaseStudio() { ))}
-
- +
) : ( diff --git a/src/server/ai/provider/cloudflare.ts b/src/server/ai/provider/cloudflare.ts index 47fd073..99a1c48 100644 --- a/src/server/ai/provider/cloudflare.ts +++ b/src/server/ai/provider/cloudflare.ts @@ -27,7 +27,7 @@ const createFormData = (params: any, model: CloudflareAiModel, request: TypixGen // Append images with numbered parameter names for (let i = 0; i < Math.min(images.length, maxInputImages); i++) { - const imageBlob = base64ToBlob(images[i]!); + const imageBlob = base64ToBlob(dataURItoBase64(images[i]!)); form.append(`input_image_${i}`, imageBlob); } } @@ -76,7 +76,7 @@ const generateSingle = async (request: TypixGenerateRequest, settings: ApiProvid params.height = size?.height; } if (genType === "i2i") { - params.image_b64 = request.images![0]!; + params.image_b64 = dataURItoBase64(request.images![0]!); } // Built-in Cloudflare Worker AI diff --git a/src/server/lib/util.ts b/src/server/lib/util.ts index 7a67f9f..bfffa81 100644 --- a/src/server/lib/util.ts +++ b/src/server/lib/util.ts @@ -13,7 +13,7 @@ export function base64ToBlob(base64: string, mimeType = "image/png") { } export function dataURItoBase64(dataURI: string) { - return dataURI.split(",")[1]; + return dataURI.split(",")[1]!; } export async function readableStreamToDataURI(stream: ReadableStream, fmt = "png") { diff --git a/src/server/service/file/storage.ts b/src/server/service/file/storage.ts index c4e2452..434da6f 100644 --- a/src/server/service/file/storage.ts +++ b/src/server/service/file/storage.ts @@ -1,6 +1,6 @@ import { type Storage, files } from "@/server/db/schemas"; import { inBrowser } from "@/server/lib/env"; -import { fetchUrlToDataURI } from "@/server/lib/util"; +import { base64ToDataURI, fetchUrlToDataURI } from "@/server/lib/util"; import { and, eq } from "drizzle-orm"; import { getContext } from "../context"; @@ -120,7 +120,7 @@ export const getFileMetadata = async (fileId: string, userId: string) => { }; /** - * Get file base64-encoded string + * Get file base64 data URL * @param fileId File ID to get data for * @param userId User ID to check access * @param redirect @@ -138,10 +138,11 @@ export const getFileData = async (fileId: string, userId: string) => { switch (metadata.protocol) { case "data:": - return metadata.accessUrl.split(",")[1]!; // Return base64 part only + return metadata.accessUrl; case "file:": { const fs = await import("node:fs/promises"); - return await fs.readFile(metadata.accessUrl, "base64"); + const fileSuffix = metadata.accessUrl.split(".").pop(); + return base64ToDataURI(await fs.readFile(metadata.accessUrl, "base64"), fileSuffix); } default: return await fetchUrlToDataURI(metadata.accessUrl); diff --git a/wrangler.toml b/wrangler.toml index a5ec769..f73860f 100644 --- a/wrangler.toml +++ b/wrangler.toml @@ -30,7 +30,7 @@ binding = "AI" # Authenticated users benefit from cross-device data sync and server-side data persistence. # Provides the best of both worlds: instant local access with optional cloud features. MODE = "client" -GOOGLE_ANALYTICS_ID = "G-0T65G1J5DT" +# GOOGLE_ANALYTICS_ID = "G-0T65G1J5DT" # Authentication configuration, only for mixed mode #