application [wireframe-renderer-web] view page [pages/canvas] development
This commit is contained in:
546
apps/wireframe-renderer-web/src/pages/canvas.tsx
Normal file
546
apps/wireframe-renderer-web/src/pages/canvas.tsx
Normal file
@@ -0,0 +1,546 @@
|
|||||||
|
import { useState, useRef, useEffect } from "react";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { Label } from "@/components/ui/label";
|
||||||
|
import { Separator } from "@/components/ui/separator";
|
||||||
|
import { Upload, Download, Paintbrush, Square, Circle, Minus, Type, Trash2, Play, Save, Folder } from "lucide-react";
|
||||||
|
import { skill, entities, auth } from "@/lib/nvwa";
|
||||||
|
|
||||||
|
type Tool = "pen" | "rectangle" | "circle" | "line" | "text" | "eraser";
|
||||||
|
|
||||||
|
interface DrawingElement {
|
||||||
|
type: Tool;
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
width?: number;
|
||||||
|
height?: number;
|
||||||
|
x2?: number;
|
||||||
|
y2?: number;
|
||||||
|
text?: string;
|
||||||
|
color?: string;
|
||||||
|
strokeWidth?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function CanvasPage() {
|
||||||
|
const canvasRef = useRef<HTMLCanvasElement>(null);
|
||||||
|
const [tool, setTool] = useState<Tool>("pen");
|
||||||
|
const [isDrawing, setIsDrawing] = useState(false);
|
||||||
|
const [startPos, setStartPos] = useState({ x: 0, y: 0 });
|
||||||
|
const [description, setDescription] = useState("");
|
||||||
|
const [generatedImageUrl, setGeneratedImageUrl] = useState<string | null>(null);
|
||||||
|
const [isGenerating, setIsGenerating] = useState(false);
|
||||||
|
const [elements, setElements] = useState<DrawingElement[]>([]);
|
||||||
|
const [currentProject, setCurrentProject] = useState<any>(null);
|
||||||
|
const [currentWireframe, setCurrentWireframe] = useState<any>(null);
|
||||||
|
const [projects, setProjects] = useState<any[]>([]);
|
||||||
|
const [wireframeName, setWireframeName] = useState("Untitled Wireframe");
|
||||||
|
const [isSaving, setIsSaving] = useState(false);
|
||||||
|
|
||||||
|
// Canvas drawing functions
|
||||||
|
const startDrawing = (e: React.MouseEvent<HTMLCanvasElement>) => {
|
||||||
|
const canvas = canvasRef.current;
|
||||||
|
if (!canvas) return;
|
||||||
|
|
||||||
|
const rect = canvas.getBoundingClientRect();
|
||||||
|
const x = e.clientX - rect.left;
|
||||||
|
const y = e.clientY - rect.top;
|
||||||
|
|
||||||
|
setStartPos({ x, y });
|
||||||
|
setIsDrawing(true);
|
||||||
|
|
||||||
|
if (tool === "pen") {
|
||||||
|
// For pen, we add points continuously
|
||||||
|
const newElement: DrawingElement = {
|
||||||
|
type: "pen",
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
color: "#000000",
|
||||||
|
strokeWidth: 2
|
||||||
|
};
|
||||||
|
setElements(prev => [...prev, newElement]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const draw = (e: React.MouseEvent<HTMLCanvasElement>) => {
|
||||||
|
if (!isDrawing || !canvasRef.current) return;
|
||||||
|
|
||||||
|
const canvas = canvasRef.current;
|
||||||
|
const ctx = canvas.getContext("2d");
|
||||||
|
if (!ctx) return;
|
||||||
|
|
||||||
|
const rect = canvas.getBoundingClientRect();
|
||||||
|
const x = e.clientX - rect.left;
|
||||||
|
const y = e.clientY - rect.top;
|
||||||
|
|
||||||
|
if (tool === "pen") {
|
||||||
|
// Draw freehand line
|
||||||
|
ctx.strokeStyle = "#000000";
|
||||||
|
ctx.lineWidth = 2;
|
||||||
|
ctx.lineCap = "round";
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(startPos.x, startPos.y);
|
||||||
|
ctx.lineTo(x, y);
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
// Update last element with current position
|
||||||
|
setElements(prev => {
|
||||||
|
const newElements = [...prev];
|
||||||
|
const lastElement = newElements[newElements.length - 1];
|
||||||
|
if (lastElement && lastElement.type === "pen") {
|
||||||
|
lastElement.x2 = x;
|
||||||
|
lastElement.y2 = y;
|
||||||
|
}
|
||||||
|
return newElements;
|
||||||
|
});
|
||||||
|
setStartPos({ x, y });
|
||||||
|
} else {
|
||||||
|
// Preview shape while drawing
|
||||||
|
redrawCanvas();
|
||||||
|
ctx.strokeStyle = "#000000";
|
||||||
|
ctx.lineWidth = 2;
|
||||||
|
|
||||||
|
switch (tool) {
|
||||||
|
case "rectangle":
|
||||||
|
ctx.strokeRect(startPos.x, startPos.y, x - startPos.x, y - startPos.y);
|
||||||
|
break;
|
||||||
|
case "circle":
|
||||||
|
const radius = Math.sqrt(Math.pow(x - startPos.x, 2) + Math.pow(y - startPos.y, 2));
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(startPos.x, startPos.y, radius, 0, 2 * Math.PI);
|
||||||
|
ctx.stroke();
|
||||||
|
break;
|
||||||
|
case "line":
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(startPos.x, startPos.y);
|
||||||
|
ctx.lineTo(x, y);
|
||||||
|
ctx.stroke();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const stopDrawing = (e: React.MouseEvent<HTMLCanvasElement>) => {
|
||||||
|
if (!isDrawing || !canvasRef.current) return;
|
||||||
|
|
||||||
|
const canvas = canvasRef.current;
|
||||||
|
const rect = canvas.getBoundingClientRect();
|
||||||
|
const x = e.clientX - rect.left;
|
||||||
|
const y = e.clientY - rect.top;
|
||||||
|
|
||||||
|
if (tool !== "pen") {
|
||||||
|
const newElement: DrawingElement = {
|
||||||
|
type: tool,
|
||||||
|
x: startPos.x,
|
||||||
|
y: startPos.y,
|
||||||
|
x2: x,
|
||||||
|
y2: y,
|
||||||
|
color: "#000000",
|
||||||
|
strokeWidth: 2
|
||||||
|
};
|
||||||
|
setElements(prev => [...prev, newElement]);
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsDrawing(false);
|
||||||
|
redrawCanvas();
|
||||||
|
};
|
||||||
|
|
||||||
|
const redrawCanvas = () => {
|
||||||
|
const canvas = canvasRef.current;
|
||||||
|
const ctx = canvas?.getContext("2d");
|
||||||
|
if (!canvas || !ctx) return;
|
||||||
|
|
||||||
|
// Clear canvas
|
||||||
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||||
|
|
||||||
|
// Redraw all elements
|
||||||
|
elements.forEach(element => {
|
||||||
|
ctx.strokeStyle = element.color || "#000000";
|
||||||
|
ctx.lineWidth = element.strokeWidth || 2;
|
||||||
|
|
||||||
|
switch (element.type) {
|
||||||
|
case "rectangle":
|
||||||
|
if (element.x2 && element.y2) {
|
||||||
|
ctx.strokeRect(element.x, element.y, element.x2 - element.x, element.y2 - element.y);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "circle":
|
||||||
|
if (element.x2 && element.y2) {
|
||||||
|
const radius = Math.sqrt(Math.pow(element.x2 - element.x, 2) + Math.pow(element.y2 - element.y, 2));
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(element.x, element.y, radius, 0, 2 * Math.PI);
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "line":
|
||||||
|
if (element.x2 && element.y2) {
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(element.x, element.y);
|
||||||
|
ctx.lineTo(element.x2, element.y2);
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
redrawCanvas();
|
||||||
|
}, [elements]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
loadProjectsAndWireframe();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const loadProjectsAndWireframe = async () => {
|
||||||
|
try {
|
||||||
|
const user = await auth.currentUser();
|
||||||
|
if (!user) return;
|
||||||
|
|
||||||
|
// Load projects
|
||||||
|
const { data: userProjects } = await entities
|
||||||
|
.from("nvwa_project")
|
||||||
|
.select("*")
|
||||||
|
.eq("user_id", user.id);
|
||||||
|
|
||||||
|
if (userProjects && userProjects.length > 0) {
|
||||||
|
setProjects(userProjects);
|
||||||
|
// Use first project or create new one
|
||||||
|
const project = userProjects[0];
|
||||||
|
setCurrentProject(project);
|
||||||
|
|
||||||
|
// Load latest wireframe for this project
|
||||||
|
const { data: wireframes } = await entities
|
||||||
|
.from("nvwa_wireframe")
|
||||||
|
.select("*")
|
||||||
|
.eq("project_id", project.id)
|
||||||
|
.order("updated_at", { ascending: false })
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
|
if (wireframes && wireframes.length > 0) {
|
||||||
|
const wireframe = wireframes[0];
|
||||||
|
setCurrentWireframe(wireframe);
|
||||||
|
setWireframeName(wireframe.name);
|
||||||
|
setDescription(wireframe.description || "");
|
||||||
|
|
||||||
|
// Load canvas data
|
||||||
|
if (wireframe.canvas_data) {
|
||||||
|
setElements(wireframe.canvas_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Create default project
|
||||||
|
const { data: newProject } = await entities
|
||||||
|
.from("nvwa_project")
|
||||||
|
.insert({
|
||||||
|
name: "Default Project",
|
||||||
|
description: "Default wireframe project",
|
||||||
|
user_id: user.id
|
||||||
|
})
|
||||||
|
.select()
|
||||||
|
.single();
|
||||||
|
|
||||||
|
if (newProject) {
|
||||||
|
setCurrentProject(newProject);
|
||||||
|
setProjects([newProject]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to load projects:", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleFileUpload = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
const file = e.target.files?.[0];
|
||||||
|
if (!file) return;
|
||||||
|
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onload = (event) => {
|
||||||
|
const img = new Image();
|
||||||
|
img.onload = () => {
|
||||||
|
const canvas = canvasRef.current;
|
||||||
|
const ctx = canvas?.getContext("2d");
|
||||||
|
if (canvas && ctx) {
|
||||||
|
// Resize canvas to fit image
|
||||||
|
canvas.width = img.width;
|
||||||
|
canvas.height = img.height;
|
||||||
|
ctx.drawImage(img, 0, 0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
img.src = event.target?.result as string;
|
||||||
|
};
|
||||||
|
reader.readAsDataURL(file);
|
||||||
|
};
|
||||||
|
|
||||||
|
const generateImage = async () => {
|
||||||
|
if (!canvasRef.current || !description.trim()) return;
|
||||||
|
|
||||||
|
// Ensure wireframe is saved before generation
|
||||||
|
if (!currentWireframe) {
|
||||||
|
await saveWireframe();
|
||||||
|
if (!currentWireframe) return; // Still no wireframe, maybe save failed
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsGenerating(true);
|
||||||
|
try {
|
||||||
|
// Convert canvas to base64
|
||||||
|
const canvas = canvasRef.current;
|
||||||
|
const dataUrl = canvas.toDataURL("image/png");
|
||||||
|
|
||||||
|
// Use DoodlePainting skill
|
||||||
|
const result = await skill.execute("DoodlePainting", {
|
||||||
|
sketch: dataUrl,
|
||||||
|
prompt: description,
|
||||||
|
style: "modern",
|
||||||
|
structureStrength: 0.8
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result && result.imageUrls && result.imageUrls.length > 0) {
|
||||||
|
const generatedUrl = result.imageUrls[0];
|
||||||
|
setGeneratedImageUrl(generatedUrl);
|
||||||
|
|
||||||
|
// Save generation to database
|
||||||
|
await entities
|
||||||
|
.from("nvwa_generation")
|
||||||
|
.insert({
|
||||||
|
wireframe_id: currentWireframe.id,
|
||||||
|
prompt: description,
|
||||||
|
status: "completed",
|
||||||
|
generated_image_url: generatedUrl
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Generation failed:", error);
|
||||||
|
alert("生成失败,请重试");
|
||||||
|
|
||||||
|
// Save failed generation
|
||||||
|
if (currentWireframe) {
|
||||||
|
await entities
|
||||||
|
.from("nvwa_generation")
|
||||||
|
.insert({
|
||||||
|
wireframe_id: currentWireframe.id,
|
||||||
|
prompt: description,
|
||||||
|
status: "failed",
|
||||||
|
error_message: error instanceof Error ? error.message : "Unknown error"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
setIsGenerating(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const downloadImage = () => {
|
||||||
|
if (!generatedImageUrl) return;
|
||||||
|
|
||||||
|
const link = document.createElement("a");
|
||||||
|
link.href = generatedImageUrl;
|
||||||
|
link.download = "wireframe-render.png";
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
|
document.body.removeChild(link);
|
||||||
|
};
|
||||||
|
|
||||||
|
const saveWireframe = async () => {
|
||||||
|
if (!currentProject) return;
|
||||||
|
|
||||||
|
setIsSaving(true);
|
||||||
|
try {
|
||||||
|
const canvasData = elements;
|
||||||
|
|
||||||
|
if (currentWireframe) {
|
||||||
|
// Update existing wireframe
|
||||||
|
await entities
|
||||||
|
.from("nvwa_wireframe")
|
||||||
|
.update({
|
||||||
|
name: wireframeName,
|
||||||
|
canvas_data: canvasData,
|
||||||
|
description: description
|
||||||
|
})
|
||||||
|
.eq("id", currentWireframe.id);
|
||||||
|
} else {
|
||||||
|
// Create new wireframe
|
||||||
|
const { data: newWireframe } = await entities
|
||||||
|
.from("nvwa_wireframe")
|
||||||
|
.insert({
|
||||||
|
project_id: currentProject.id,
|
||||||
|
name: wireframeName,
|
||||||
|
canvas_data: canvasData,
|
||||||
|
description: description
|
||||||
|
})
|
||||||
|
.select()
|
||||||
|
.single();
|
||||||
|
|
||||||
|
if (newWireframe) {
|
||||||
|
setCurrentWireframe(newWireframe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to save wireframe:", error);
|
||||||
|
alert("保存失败,请重试");
|
||||||
|
} finally {
|
||||||
|
setIsSaving(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const clearCanvas = () => {
|
||||||
|
setElements([]);
|
||||||
|
const canvas = canvasRef.current;
|
||||||
|
const ctx = canvas?.getContext("2d");
|
||||||
|
if (canvas && ctx) {
|
||||||
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex h-screen bg-background">
|
||||||
|
{/* Toolbar */}
|
||||||
|
<div className="w-16 bg-muted p-2 flex flex-col gap-2">
|
||||||
|
<Button
|
||||||
|
variant={tool === "pen" ? "default" : "ghost"}
|
||||||
|
size="sm"
|
||||||
|
onClick={() => setTool("pen")}
|
||||||
|
title="Pen"
|
||||||
|
>
|
||||||
|
<Paintbrush className="w-4 h-4" />
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant={tool === "rectangle" ? "default" : "ghost"}
|
||||||
|
size="sm"
|
||||||
|
onClick={() => setTool("rectangle")}
|
||||||
|
title="Rectangle"
|
||||||
|
>
|
||||||
|
<Square className="w-4 h-4" />
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant={tool === "circle" ? "default" : "ghost"}
|
||||||
|
size="sm"
|
||||||
|
onClick={() => setTool("circle")}
|
||||||
|
title="Circle"
|
||||||
|
>
|
||||||
|
<Circle className="w-4 h-4" />
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant={tool === "line" ? "default" : "ghost"}
|
||||||
|
size="sm"
|
||||||
|
onClick={() => setTool("line")}
|
||||||
|
title="Line"
|
||||||
|
>
|
||||||
|
<Minus className="w-4 h-4" />
|
||||||
|
</Button>
|
||||||
|
<Separator />
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
onClick={clearCanvas}
|
||||||
|
title="Clear"
|
||||||
|
>
|
||||||
|
<Trash2 className="w-4 h-4" />
|
||||||
|
</Button>
|
||||||
|
<Label htmlFor="file-upload" className="cursor-pointer">
|
||||||
|
<Button variant="ghost" size="sm" asChild>
|
||||||
|
<span title="Upload">
|
||||||
|
<Upload className="w-4 h-4" />
|
||||||
|
</span>
|
||||||
|
</Button>
|
||||||
|
</Label>
|
||||||
|
<Input
|
||||||
|
id="file-upload"
|
||||||
|
type="file"
|
||||||
|
accept="image/*"
|
||||||
|
onChange={handleFileUpload}
|
||||||
|
className="hidden"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Canvas */}
|
||||||
|
<div className="flex-1 p-4">
|
||||||
|
<canvas
|
||||||
|
ref={canvasRef}
|
||||||
|
width={800}
|
||||||
|
height={600}
|
||||||
|
className="border border-border bg-white cursor-crosshair"
|
||||||
|
onMouseDown={startDrawing}
|
||||||
|
onMouseMove={draw}
|
||||||
|
onMouseUp={stopDrawing}
|
||||||
|
onMouseLeave={stopDrawing}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Sidebar */}
|
||||||
|
<div className="w-80 bg-muted p-4 flex flex-col gap-4">
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>Project & Wireframe</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="space-y-2">
|
||||||
|
<div>
|
||||||
|
<Label htmlFor="project-name">Project</Label>
|
||||||
|
<Input
|
||||||
|
id="project-name"
|
||||||
|
value={currentProject?.name || ""}
|
||||||
|
disabled
|
||||||
|
placeholder="No project selected"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Label htmlFor="wireframe-name">Wireframe Name</Label>
|
||||||
|
<Input
|
||||||
|
id="wireframe-name"
|
||||||
|
value={wireframeName}
|
||||||
|
onChange={(e) => setWireframeName(e.target.value)}
|
||||||
|
placeholder="Wireframe name"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<Button onClick={saveWireframe} disabled={isSaving} size="sm">
|
||||||
|
<Save className="w-4 h-4 mr-2" />
|
||||||
|
{isSaving ? "Saving..." : "Save"}
|
||||||
|
</Button>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>Description</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<Textarea
|
||||||
|
placeholder="Describe your wireframe design..."
|
||||||
|
value={description}
|
||||||
|
onChange={(e) => setDescription(e.target.value)}
|
||||||
|
rows={4}
|
||||||
|
/>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
onClick={generateImage}
|
||||||
|
disabled={isGenerating || !description.trim()}
|
||||||
|
className="w-full"
|
||||||
|
>
|
||||||
|
<Play className="w-4 h-4 mr-2" />
|
||||||
|
{isGenerating ? "Generating..." : "Generate Image"}
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
{generatedImageUrl && (
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>Generated Image</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="flex flex-col gap-2">
|
||||||
|
<img
|
||||||
|
src={generatedImageUrl}
|
||||||
|
alt="Generated"
|
||||||
|
className="w-full rounded border"
|
||||||
|
/>
|
||||||
|
<Button onClick={downloadImage} variant="outline" size="sm">
|
||||||
|
<Download className="w-4 h-4 mr-2" />
|
||||||
|
Download
|
||||||
|
</Button>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
111
database/migrations/0000_melted_blade.sql
Normal file
111
database/migrations/0000_melted_blade.sql
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
CREATE TYPE "public"."generation_status" AS ENUM('pending', 'processing', 'completed', 'failed');--> statement-breakpoint
|
||||||
|
CREATE TABLE "account" (
|
||||||
|
"id" text PRIMARY KEY NOT NULL,
|
||||||
|
"account_id" text NOT NULL,
|
||||||
|
"provider_id" text NOT NULL,
|
||||||
|
"user_id" text NOT NULL,
|
||||||
|
"access_token" text,
|
||||||
|
"refresh_token" text,
|
||||||
|
"id_token" text,
|
||||||
|
"access_token_expires_at" timestamp,
|
||||||
|
"refresh_token_expires_at" timestamp,
|
||||||
|
"scope" text,
|
||||||
|
"password" text,
|
||||||
|
"created_at" timestamp DEFAULT now() NOT NULL,
|
||||||
|
"updated_at" timestamp NOT NULL
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
CREATE TABLE "jwks" (
|
||||||
|
"id" text PRIMARY KEY NOT NULL,
|
||||||
|
"public_key" text NOT NULL,
|
||||||
|
"private_key" text NOT NULL,
|
||||||
|
"created_at" timestamp NOT NULL
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
CREATE TABLE "session" (
|
||||||
|
"id" text PRIMARY KEY NOT NULL,
|
||||||
|
"expires_at" timestamp NOT NULL,
|
||||||
|
"token" text NOT NULL,
|
||||||
|
"created_at" timestamp DEFAULT now() NOT NULL,
|
||||||
|
"updated_at" timestamp NOT NULL,
|
||||||
|
"ip_address" text,
|
||||||
|
"user_agent" text,
|
||||||
|
"user_id" text NOT NULL,
|
||||||
|
CONSTRAINT "session_token_unique" UNIQUE("token")
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
CREATE TABLE "user" (
|
||||||
|
"id" text PRIMARY KEY NOT NULL,
|
||||||
|
"name" text NOT NULL,
|
||||||
|
"email" text NOT NULL,
|
||||||
|
"email_verified" boolean DEFAULT false NOT NULL,
|
||||||
|
"image" text,
|
||||||
|
"created_at" timestamp DEFAULT now() NOT NULL,
|
||||||
|
"updated_at" timestamp DEFAULT now() NOT NULL,
|
||||||
|
"username" text,
|
||||||
|
"display_username" text,
|
||||||
|
"phone_number" text,
|
||||||
|
"phone_number_verified" boolean,
|
||||||
|
"role" text DEFAULT 'user',
|
||||||
|
"lang" text DEFAULT 'en',
|
||||||
|
CONSTRAINT "user_email_unique" UNIQUE("email"),
|
||||||
|
CONSTRAINT "user_username_unique" UNIQUE("username"),
|
||||||
|
CONSTRAINT "user_phone_number_unique" UNIQUE("phone_number")
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
CREATE TABLE "verification" (
|
||||||
|
"id" text PRIMARY KEY NOT NULL,
|
||||||
|
"identifier" text NOT NULL,
|
||||||
|
"value" text NOT NULL,
|
||||||
|
"expires_at" timestamp NOT NULL,
|
||||||
|
"created_at" timestamp DEFAULT now() NOT NULL,
|
||||||
|
"updated_at" timestamp DEFAULT now() NOT NULL
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
CREATE TABLE "nvwa_attribute_file" (
|
||||||
|
"id" serial PRIMARY KEY NOT NULL,
|
||||||
|
"name" varchar(255) NOT NULL,
|
||||||
|
"type" varchar(255) NOT NULL,
|
||||||
|
"description" text,
|
||||||
|
"url" varchar(255),
|
||||||
|
"created_at" timestamp DEFAULT now() NOT NULL,
|
||||||
|
"updated_at" timestamp DEFAULT now() NOT NULL
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
CREATE TABLE "nvwa_generation" (
|
||||||
|
"id" serial PRIMARY KEY NOT NULL,
|
||||||
|
"wireframe_id" integer NOT NULL,
|
||||||
|
"prompt" text NOT NULL,
|
||||||
|
"status" "generation_status" DEFAULT 'pending' NOT NULL,
|
||||||
|
"generated_image_url" varchar(500),
|
||||||
|
"error_message" text,
|
||||||
|
"created_at" timestamp DEFAULT now() NOT NULL,
|
||||||
|
"updated_at" timestamp DEFAULT now() NOT NULL
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
CREATE TABLE "nvwa_project" (
|
||||||
|
"id" serial PRIMARY KEY NOT NULL,
|
||||||
|
"name" varchar(255) NOT NULL,
|
||||||
|
"description" text,
|
||||||
|
"user_id" text NOT NULL,
|
||||||
|
"created_at" timestamp DEFAULT now() NOT NULL,
|
||||||
|
"updated_at" timestamp DEFAULT now() NOT NULL
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
CREATE TABLE "nvwa_wireframe" (
|
||||||
|
"id" serial PRIMARY KEY NOT NULL,
|
||||||
|
"project_id" integer NOT NULL,
|
||||||
|
"name" varchar(255) NOT NULL,
|
||||||
|
"canvas_data" jsonb,
|
||||||
|
"description" text,
|
||||||
|
"uploaded_file_id" integer,
|
||||||
|
"created_at" timestamp DEFAULT now() NOT NULL,
|
||||||
|
"updated_at" timestamp DEFAULT now() NOT NULL
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
ALTER TABLE "account" ADD CONSTRAINT "account_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||||
|
ALTER TABLE "session" ADD CONSTRAINT "session_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||||
|
ALTER TABLE "nvwa_generation" ADD CONSTRAINT "nvwa_generation_wireframe_id_nvwa_wireframe_id_fk" FOREIGN KEY ("wireframe_id") REFERENCES "public"."nvwa_wireframe"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||||
|
ALTER TABLE "nvwa_project" ADD CONSTRAINT "nvwa_project_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||||
|
ALTER TABLE "nvwa_wireframe" ADD CONSTRAINT "nvwa_wireframe_project_id_nvwa_project_id_fk" FOREIGN KEY ("project_id") REFERENCES "public"."nvwa_project"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||||
|
ALTER TABLE "nvwa_wireframe" ADD CONSTRAINT "nvwa_wireframe_uploaded_file_id_nvwa_attribute_file_id_fk" FOREIGN KEY ("uploaded_file_id") REFERENCES "public"."nvwa_attribute_file"("id") ON DELETE set null ON UPDATE no action;
|
||||||
717
database/migrations/meta/0000_snapshot.json
Normal file
717
database/migrations/meta/0000_snapshot.json
Normal file
@@ -0,0 +1,717 @@
|
|||||||
|
{
|
||||||
|
"id": "c9f3d206-784b-40af-b29e-6ac087da8be5",
|
||||||
|
"prevId": "00000000-0000-0000-0000-000000000000",
|
||||||
|
"version": "7",
|
||||||
|
"dialect": "postgresql",
|
||||||
|
"tables": {
|
||||||
|
"public.account": {
|
||||||
|
"name": "account",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"account_id": {
|
||||||
|
"name": "account_id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"provider_id": {
|
||||||
|
"name": "provider_id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"user_id": {
|
||||||
|
"name": "user_id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"access_token": {
|
||||||
|
"name": "access_token",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"refresh_token": {
|
||||||
|
"name": "refresh_token",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"id_token": {
|
||||||
|
"name": "id_token",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"access_token_expires_at": {
|
||||||
|
"name": "access_token_expires_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"refresh_token_expires_at": {
|
||||||
|
"name": "refresh_token_expires_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"scope": {
|
||||||
|
"name": "scope",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"password": {
|
||||||
|
"name": "password",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "now()"
|
||||||
|
},
|
||||||
|
"updated_at": {
|
||||||
|
"name": "updated_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"account_user_id_user_id_fk": {
|
||||||
|
"name": "account_user_id_user_id_fk",
|
||||||
|
"tableFrom": "account",
|
||||||
|
"tableTo": "user",
|
||||||
|
"columnsFrom": [
|
||||||
|
"user_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "cascade",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"policies": {},
|
||||||
|
"checkConstraints": {},
|
||||||
|
"isRLSEnabled": false
|
||||||
|
},
|
||||||
|
"public.jwks": {
|
||||||
|
"name": "jwks",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"public_key": {
|
||||||
|
"name": "public_key",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"private_key": {
|
||||||
|
"name": "private_key",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"policies": {},
|
||||||
|
"checkConstraints": {},
|
||||||
|
"isRLSEnabled": false
|
||||||
|
},
|
||||||
|
"public.session": {
|
||||||
|
"name": "session",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"expires_at": {
|
||||||
|
"name": "expires_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"token": {
|
||||||
|
"name": "token",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "now()"
|
||||||
|
},
|
||||||
|
"updated_at": {
|
||||||
|
"name": "updated_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"ip_address": {
|
||||||
|
"name": "ip_address",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"user_agent": {
|
||||||
|
"name": "user_agent",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"user_id": {
|
||||||
|
"name": "user_id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"session_user_id_user_id_fk": {
|
||||||
|
"name": "session_user_id_user_id_fk",
|
||||||
|
"tableFrom": "session",
|
||||||
|
"tableTo": "user",
|
||||||
|
"columnsFrom": [
|
||||||
|
"user_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "cascade",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {
|
||||||
|
"session_token_unique": {
|
||||||
|
"name": "session_token_unique",
|
||||||
|
"nullsNotDistinct": false,
|
||||||
|
"columns": [
|
||||||
|
"token"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"policies": {},
|
||||||
|
"checkConstraints": {},
|
||||||
|
"isRLSEnabled": false
|
||||||
|
},
|
||||||
|
"public.user": {
|
||||||
|
"name": "user",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"name": "name",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"email": {
|
||||||
|
"name": "email",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"email_verified": {
|
||||||
|
"name": "email_verified",
|
||||||
|
"type": "boolean",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"image": {
|
||||||
|
"name": "image",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "now()"
|
||||||
|
},
|
||||||
|
"updated_at": {
|
||||||
|
"name": "updated_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "now()"
|
||||||
|
},
|
||||||
|
"username": {
|
||||||
|
"name": "username",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"display_username": {
|
||||||
|
"name": "display_username",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"phone_number": {
|
||||||
|
"name": "phone_number",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"phone_number_verified": {
|
||||||
|
"name": "phone_number_verified",
|
||||||
|
"type": "boolean",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"role": {
|
||||||
|
"name": "role",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"default": "'user'"
|
||||||
|
},
|
||||||
|
"lang": {
|
||||||
|
"name": "lang",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"default": "'en'"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {
|
||||||
|
"user_email_unique": {
|
||||||
|
"name": "user_email_unique",
|
||||||
|
"nullsNotDistinct": false,
|
||||||
|
"columns": [
|
||||||
|
"email"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"user_username_unique": {
|
||||||
|
"name": "user_username_unique",
|
||||||
|
"nullsNotDistinct": false,
|
||||||
|
"columns": [
|
||||||
|
"username"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"user_phone_number_unique": {
|
||||||
|
"name": "user_phone_number_unique",
|
||||||
|
"nullsNotDistinct": false,
|
||||||
|
"columns": [
|
||||||
|
"phone_number"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"policies": {},
|
||||||
|
"checkConstraints": {},
|
||||||
|
"isRLSEnabled": false
|
||||||
|
},
|
||||||
|
"public.verification": {
|
||||||
|
"name": "verification",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"identifier": {
|
||||||
|
"name": "identifier",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"value": {
|
||||||
|
"name": "value",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"expires_at": {
|
||||||
|
"name": "expires_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "now()"
|
||||||
|
},
|
||||||
|
"updated_at": {
|
||||||
|
"name": "updated_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "now()"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"policies": {},
|
||||||
|
"checkConstraints": {},
|
||||||
|
"isRLSEnabled": false
|
||||||
|
},
|
||||||
|
"public.nvwa_attribute_file": {
|
||||||
|
"name": "nvwa_attribute_file",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "serial",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"name": "name",
|
||||||
|
"type": "varchar(255)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"name": "type",
|
||||||
|
"type": "varchar(255)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"name": "description",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"name": "url",
|
||||||
|
"type": "varchar(255)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "now()"
|
||||||
|
},
|
||||||
|
"updated_at": {
|
||||||
|
"name": "updated_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "now()"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"policies": {},
|
||||||
|
"checkConstraints": {},
|
||||||
|
"isRLSEnabled": false
|
||||||
|
},
|
||||||
|
"public.nvwa_generation": {
|
||||||
|
"name": "nvwa_generation",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "serial",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"wireframe_id": {
|
||||||
|
"name": "wireframe_id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"prompt": {
|
||||||
|
"name": "prompt",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"name": "status",
|
||||||
|
"type": "generation_status",
|
||||||
|
"typeSchema": "public",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "'pending'"
|
||||||
|
},
|
||||||
|
"generated_image_url": {
|
||||||
|
"name": "generated_image_url",
|
||||||
|
"type": "varchar(500)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"error_message": {
|
||||||
|
"name": "error_message",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "now()"
|
||||||
|
},
|
||||||
|
"updated_at": {
|
||||||
|
"name": "updated_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "now()"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"nvwa_generation_wireframe_id_nvwa_wireframe_id_fk": {
|
||||||
|
"name": "nvwa_generation_wireframe_id_nvwa_wireframe_id_fk",
|
||||||
|
"tableFrom": "nvwa_generation",
|
||||||
|
"tableTo": "nvwa_wireframe",
|
||||||
|
"columnsFrom": [
|
||||||
|
"wireframe_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "cascade",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"policies": {},
|
||||||
|
"checkConstraints": {},
|
||||||
|
"isRLSEnabled": false
|
||||||
|
},
|
||||||
|
"public.nvwa_project": {
|
||||||
|
"name": "nvwa_project",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "serial",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"name": "name",
|
||||||
|
"type": "varchar(255)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"name": "description",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"user_id": {
|
||||||
|
"name": "user_id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "now()"
|
||||||
|
},
|
||||||
|
"updated_at": {
|
||||||
|
"name": "updated_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "now()"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"nvwa_project_user_id_user_id_fk": {
|
||||||
|
"name": "nvwa_project_user_id_user_id_fk",
|
||||||
|
"tableFrom": "nvwa_project",
|
||||||
|
"tableTo": "user",
|
||||||
|
"columnsFrom": [
|
||||||
|
"user_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "cascade",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"policies": {},
|
||||||
|
"checkConstraints": {},
|
||||||
|
"isRLSEnabled": false
|
||||||
|
},
|
||||||
|
"public.nvwa_wireframe": {
|
||||||
|
"name": "nvwa_wireframe",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "serial",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"project_id": {
|
||||||
|
"name": "project_id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"name": "name",
|
||||||
|
"type": "varchar(255)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"canvas_data": {
|
||||||
|
"name": "canvas_data",
|
||||||
|
"type": "jsonb",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"name": "description",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"uploaded_file_id": {
|
||||||
|
"name": "uploaded_file_id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "now()"
|
||||||
|
},
|
||||||
|
"updated_at": {
|
||||||
|
"name": "updated_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "now()"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"nvwa_wireframe_project_id_nvwa_project_id_fk": {
|
||||||
|
"name": "nvwa_wireframe_project_id_nvwa_project_id_fk",
|
||||||
|
"tableFrom": "nvwa_wireframe",
|
||||||
|
"tableTo": "nvwa_project",
|
||||||
|
"columnsFrom": [
|
||||||
|
"project_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "cascade",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
},
|
||||||
|
"nvwa_wireframe_uploaded_file_id_nvwa_attribute_file_id_fk": {
|
||||||
|
"name": "nvwa_wireframe_uploaded_file_id_nvwa_attribute_file_id_fk",
|
||||||
|
"tableFrom": "nvwa_wireframe",
|
||||||
|
"tableTo": "nvwa_attribute_file",
|
||||||
|
"columnsFrom": [
|
||||||
|
"uploaded_file_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "set null",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"policies": {},
|
||||||
|
"checkConstraints": {},
|
||||||
|
"isRLSEnabled": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"enums": {
|
||||||
|
"public.generation_status": {
|
||||||
|
"name": "generation_status",
|
||||||
|
"schema": "public",
|
||||||
|
"values": [
|
||||||
|
"pending",
|
||||||
|
"processing",
|
||||||
|
"completed",
|
||||||
|
"failed"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"schemas": {},
|
||||||
|
"sequences": {},
|
||||||
|
"roles": {},
|
||||||
|
"policies": {},
|
||||||
|
"views": {},
|
||||||
|
"_meta": {
|
||||||
|
"columns": {},
|
||||||
|
"schemas": {},
|
||||||
|
"tables": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
13
database/migrations/meta/_journal.json
Normal file
13
database/migrations/meta/_journal.json
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"version": "7",
|
||||||
|
"dialect": "postgresql",
|
||||||
|
"entries": [
|
||||||
|
{
|
||||||
|
"idx": 0,
|
||||||
|
"version": "7",
|
||||||
|
"when": 1765959832572,
|
||||||
|
"tag": "0000_melted_blade",
|
||||||
|
"breakpoints": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user