feat(qr): complete QR studio, dynamic google fonts, mouse canvas drag, and template sync
This commit is contained in:
@@ -0,0 +1,142 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import fs from "fs/promises";
|
||||
import path from "path";
|
||||
import { supabase } from "@/lib/supabase";
|
||||
|
||||
const DATA_FILE = path.join(process.cwd(), "src/data/qr-templates.json");
|
||||
|
||||
function mapFromDb(row: any) {
|
||||
return {
|
||||
key: row.key,
|
||||
name: row.name,
|
||||
categoryName: row.category_name,
|
||||
aspectRatio: row.aspect_ratio || "1/1",
|
||||
bgType: row.bg_type || "gradient",
|
||||
bgImageUrl: row.bg_image_url || undefined,
|
||||
bgGradient: row.bg_gradient || undefined,
|
||||
plaqueColor: row.plaque_color || undefined,
|
||||
borderAccent: row.border_accent || undefined,
|
||||
accentText: row.accent_text || undefined,
|
||||
headerText: row.header_text || undefined,
|
||||
subText: row.sub_text || undefined,
|
||||
dividerColor: row.divider_color || undefined,
|
||||
qrConfig: row.qr_config,
|
||||
layers: row.layers,
|
||||
isActive: row.is_active ?? true,
|
||||
createdAt: row.created_at,
|
||||
updatedAt: row.updated_at,
|
||||
};
|
||||
}
|
||||
|
||||
function mapToDb(item: any) {
|
||||
return {
|
||||
key: item.key,
|
||||
name: item.name,
|
||||
category_name: item.categoryName || null,
|
||||
aspect_ratio: item.aspectRatio || "1/1",
|
||||
bg_type: item.bgType || "gradient",
|
||||
bg_image_url: item.bgImageUrl || null,
|
||||
bg_gradient: item.bgGradient || null,
|
||||
plaque_color: item.plaqueColor || null,
|
||||
border_accent: item.borderAccent || null,
|
||||
accent_text: item.accentText || null,
|
||||
header_text: item.headerText || null,
|
||||
sub_text: item.subText || null,
|
||||
divider_color: item.dividerColor || null,
|
||||
qr_config: item.qrConfig || item.defaultQrConfig || null,
|
||||
layers: item.layers || item.defaultLayers || null,
|
||||
is_active: item.isActive !== undefined ? item.isActive : true,
|
||||
updated_at: new Date().toISOString(),
|
||||
};
|
||||
}
|
||||
|
||||
async function readFromDisk(): Promise<any[]> {
|
||||
try {
|
||||
const raw = await fs.readFile(DATA_FILE, "utf-8");
|
||||
return JSON.parse(raw);
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
async function writeToDisk(list: any[]): Promise<void> {
|
||||
try {
|
||||
await fs.writeFile(DATA_FILE, JSON.stringify(list, null, 2), "utf-8");
|
||||
} catch {}
|
||||
}
|
||||
|
||||
export async function GET() {
|
||||
try {
|
||||
const { data, error } = await supabase
|
||||
.from("qr_stand_templates")
|
||||
.select("*")
|
||||
.order("created_at", { ascending: false });
|
||||
|
||||
if (!error && data && data.length > 0) {
|
||||
const mapped = data.map(mapFromDb);
|
||||
await writeToDisk(mapped);
|
||||
return NextResponse.json({ templates: mapped, source: "supabase_db" });
|
||||
}
|
||||
} catch {}
|
||||
|
||||
const diskTemplates = await readFromDisk();
|
||||
return NextResponse.json({ templates: diskTemplates, source: "disk_cache" });
|
||||
}
|
||||
|
||||
export async function POST(req: Request) {
|
||||
try {
|
||||
const body = await req.json();
|
||||
if (!body || !body.key) {
|
||||
return NextResponse.json({ error: "Şablon key zorunludur" }, { status: 400 });
|
||||
}
|
||||
|
||||
// 1. Supabase'e Upsert et
|
||||
try {
|
||||
const dbRow = mapToDb(body);
|
||||
await supabase.from("qr_stand_templates").upsert(dbRow, { onConflict: "key" });
|
||||
} catch (dbErr) {
|
||||
console.warn("Supabase upsert warning:", dbErr);
|
||||
}
|
||||
|
||||
// 2. Diske de yaz (offline / backup)
|
||||
const diskList = await readFromDisk();
|
||||
const existingIdx = diskList.findIndex((t: any) => t.key === body.key);
|
||||
if (existingIdx >= 0) {
|
||||
diskList[existingIdx] = { ...diskList[existingIdx], ...body };
|
||||
} else {
|
||||
diskList.unshift(body);
|
||||
}
|
||||
await writeToDisk(diskList);
|
||||
|
||||
return NextResponse.json({ success: true, template: body });
|
||||
} catch (err: any) {
|
||||
return NextResponse.json({ error: err.message }, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
export async function DELETE(req: Request) {
|
||||
try {
|
||||
const { searchParams } = new URL(req.url);
|
||||
const key = searchParams.get("key");
|
||||
if (!key) {
|
||||
return NextResponse.json({ error: "Şablon key zorunludur" }, { status: 400 });
|
||||
}
|
||||
|
||||
// 1. Supabase'den Sil
|
||||
try {
|
||||
await supabase.from("qr_stand_templates").delete().eq("key", key);
|
||||
} catch (dbErr) {
|
||||
console.warn("Supabase delete warning:", dbErr);
|
||||
}
|
||||
|
||||
// 2. Diskten de Sil
|
||||
const diskList = await readFromDisk();
|
||||
const filtered = diskList.filter((t: any) => t.key !== key);
|
||||
await writeToDisk(filtered);
|
||||
|
||||
return NextResponse.json({ success: true, deletedKey: key });
|
||||
} catch (err: any) {
|
||||
return NextResponse.json({ error: err.message }, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user