"use server"; import { revalidatePath } from "next/cache"; import { DATABASE_ID, ID, MEDIA_BUCKET_ID, storage, TABLES, tablesDB, } from "@/lib/appwrite-rest"; import { requireSessionSecret } from "@/lib/auth"; function slugify(s: string) { return s .toLowerCase() .replace(/[ğ]/g, "g") .replace(/[ü]/g, "u") .replace(/[ş]/g, "s") .replace(/[ı]/g, "i") .replace(/[ö]/g, "o") .replace(/[ç]/g, "c") .replace(/[^a-z0-9]+/g, "-") .replace(/^-+|-+$/g, "") .slice(0, 80); } function num(v: FormDataEntryValue | null) { if (v === null || v === "") return null; const n = Number(v); return Number.isFinite(n) ? n : null; } function bool(v: FormDataEntryValue | null) { return v === "on" || v === "true" || v === "1"; } function str(v: FormDataEntryValue | null) { if (v === null) return null; const s = String(v).trim(); return s === "" ? null : s; } function strArr(v: FormDataEntryValue | null) { if (v === null) return null; return String(v) .split(",") .map((x) => x.trim()) .filter(Boolean); } // ─── Media ─────────────────────────────────────────────────────── export async function uploadMedia(formData: FormData): Promise { const secret = await requireSessionSecret(); const file = formData.get("file"); if (!(file instanceof File) || file.size === 0) { throw new Error("Dosya seçilmedi"); } await storage.createFile(MEDIA_BUCKET_ID, ID.unique(), file, secret); revalidatePath("/admin/medya"); } export async function deleteMediaFile(fileId: string) { const secret = await requireSessionSecret(); await storage.deleteFile(MEDIA_BUCKET_ID, fileId, secret); revalidatePath("/admin/medya"); } // ─── Blog ──────────────────────────────────────────────────────── export async function saveBlogPost(formData: FormData) { const secret = await requireSessionSecret(); const id = str(formData.get("id")); const title = str(formData.get("title")); if (!title) throw new Error("Başlık zorunlu"); const slug = str(formData.get("slug")) || slugify(title); const status = (str(formData.get("status")) ?? "draft") as "draft" | "published"; const data = { slug, title, excerpt: str(formData.get("excerpt")), content: str(formData.get("content")), cover_image: str(formData.get("cover_image")), cover_file_id: str(formData.get("cover_file_id")), author: str(formData.get("author")), status, published_at: status === "published" ? str(formData.get("published_at")) || new Date().toISOString() : null, tags: strArr(formData.get("tags")), seo_title: str(formData.get("seo_title")), seo_description: str(formData.get("seo_description")), seo_image: str(formData.get("seo_image")), }; if (id) { await tablesDB.updateRow(DATABASE_ID, TABLES.blogPosts, id, data, secret); } else { await tablesDB.createRow( DATABASE_ID, TABLES.blogPosts, ID.unique(), data, secret, ); } revalidatePath("/admin/blog"); revalidatePath("/blog"); if (slug) revalidatePath(`/blog/${slug}`); } export async function deleteBlogPost(formData: FormData) { const secret = await requireSessionSecret(); const id = String(formData.get("id")); await tablesDB.deleteRow(DATABASE_ID, TABLES.blogPosts, id, secret); revalidatePath("/admin/blog"); revalidatePath("/blog"); } // ─── Services ──────────────────────────────────────────────────── export async function saveService(formData: FormData) { const secret = await requireSessionSecret(); const id = str(formData.get("id")); const title = str(formData.get("title")); if (!title) throw new Error("Başlık zorunlu"); const description = str(formData.get("description")); if (!description) throw new Error("Açıklama zorunlu"); const slug = str(formData.get("slug")) || slugify(title); // FAQ as JSON-encoded array. Each item: {"q":"...","a":"..."} const faqRaw = String(formData.get("faq") ?? ""); const faq = faqRaw .split("\n---\n") .map((block) => { const lines = block.trim().split("\n"); const q = lines[0]?.trim(); const a = lines.slice(1).join("\n").trim(); if (!q || !a) return null; return JSON.stringify({ q, a }); }) .filter((x): x is string => x !== null); const data = { slug, title, description, icon: str(formData.get("icon")), order: num(formData.get("order")) ?? 0, featured: bool(formData.get("featured")), content: str(formData.get("content")), features: strArr(formData.get("features"))?.filter(Boolean) ?? null, faq: faq.length > 0 ? faq : null, hero_image: str(formData.get("hero_image")), }; if (id) { await tablesDB.updateRow(DATABASE_ID, TABLES.services, id, data, secret); } else { await tablesDB.createRow( DATABASE_ID, TABLES.services, slug, data, secret, ); } revalidatePath("/admin/hizmetler"); revalidatePath("/hizmetler"); revalidatePath("/"); } export async function deleteService(formData: FormData) { const secret = await requireSessionSecret(); const id = String(formData.get("id")); await tablesDB.deleteRow(DATABASE_ID, TABLES.services, id, secret); revalidatePath("/admin/hizmetler"); revalidatePath("/hizmetler"); } // ─── Projects ──────────────────────────────────────────────────── export async function saveProject(formData: FormData) { const secret = await requireSessionSecret(); const id = str(formData.get("id")); const title = str(formData.get("title")); if (!title) throw new Error("Başlık zorunlu"); const slug = str(formData.get("slug")) || slugify(title); const description = str(formData.get("description")); if (!description) throw new Error("Açıklama zorunlu"); // Gallery: one URL per line const galleryRaw = String(formData.get("gallery") ?? ""); const gallery = galleryRaw .split("\n") .map((s) => s.trim()) .filter(Boolean); const data = { slug, title, description, image_url: str(formData.get("image_url")), live_url: str(formData.get("live_url")), category: str(formData.get("category")), technologies: strArr(formData.get("technologies")), year: num(formData.get("year")), featured: bool(formData.get("featured")), gallery: gallery.length > 0 ? gallery : null, content: str(formData.get("content")), client_name: str(formData.get("client_name")), industry: str(formData.get("industry")), duration: str(formData.get("duration")), service_slug: str(formData.get("service_slug")), }; if (id) { await tablesDB.updateRow(DATABASE_ID, TABLES.projects, id, data, secret); } else { await tablesDB.createRow( DATABASE_ID, TABLES.projects, ID.unique(), data, secret, ); } revalidatePath("/admin/projeler"); revalidatePath("/projeler"); revalidatePath("/"); } export async function deleteProject(formData: FormData) { const secret = await requireSessionSecret(); const id = String(formData.get("id")); await tablesDB.deleteRow(DATABASE_ID, TABLES.projects, id, secret); revalidatePath("/admin/projeler"); revalidatePath("/projeler"); } // ─── Testimonials ──────────────────────────────────────────────── export async function saveTestimonial(formData: FormData) { const secret = await requireSessionSecret(); const id = str(formData.get("id")); const name = str(formData.get("name")); if (!name) throw new Error("Ad zorunlu"); const message = str(formData.get("message")); if (!message) throw new Error("Mesaj zorunlu"); const data = { name, role: str(formData.get("role")), company: str(formData.get("company")), message, rating: num(formData.get("rating")) ?? 5, image_url: str(formData.get("image_url")), order: num(formData.get("order")) ?? 0, featured: bool(formData.get("featured")), }; if (id) { await tablesDB.updateRow(DATABASE_ID, TABLES.testimonials, id, data, secret); } else { await tablesDB.createRow( DATABASE_ID, TABLES.testimonials, ID.unique(), data, secret, ); } revalidatePath("/admin/referanslar"); revalidatePath("/"); } export async function deleteTestimonial(formData: FormData) { const secret = await requireSessionSecret(); const id = String(formData.get("id")); await tablesDB.deleteRow(DATABASE_ID, TABLES.testimonials, id, secret); revalidatePath("/admin/referanslar"); } // ─── SEO Settings ──────────────────────────────────────────────── export async function saveSeoSettings(formData: FormData) { const secret = await requireSessionSecret(); const data = { site_name: str(formData.get("site_name")), site_description: str(formData.get("site_description")), default_og_image: str(formData.get("default_og_image")), twitter_handle: str(formData.get("twitter_handle")), facebook_url: str(formData.get("facebook_url")), linkedin_url: str(formData.get("linkedin_url")), instagram_url: str(formData.get("instagram_url")), google_site_verification: str(formData.get("google_site_verification")), gtm_id: str(formData.get("gtm_id")), }; try { await tablesDB.updateRow( DATABASE_ID, TABLES.seoSettings, "global", data, secret, ); } catch { await tablesDB.createRow( DATABASE_ID, TABLES.seoSettings, "global", data, secret, ); } revalidatePath("/", "layout"); revalidatePath("/admin/seo"); } // ─── SEO Page ──────────────────────────────────────────────────── export async function saveSeoPage(formData: FormData) { const secret = await requireSessionSecret(); const id = str(formData.get("id")); const path = str(formData.get("path")); if (!path) throw new Error("Path zorunlu"); const data = { path, title: str(formData.get("title")), description: str(formData.get("description")), og_image: str(formData.get("og_image")), canonical: str(formData.get("canonical")), noindex: bool(formData.get("noindex")), }; if (id) { await tablesDB.updateRow(DATABASE_ID, TABLES.seoPages, id, data, secret); } else { await tablesDB.createRow( DATABASE_ID, TABLES.seoPages, ID.unique(), data, secret, ); } revalidatePath(path); revalidatePath("/admin/seo"); } export async function deleteSeoPage(formData: FormData) { const secret = await requireSessionSecret(); const id = String(formData.get("id")); await tablesDB.deleteRow(DATABASE_ID, TABLES.seoPages, id, secret); revalidatePath("/admin/seo"); } // ─── Contact ───────────────────────────────────────────────────── export async function updateMessageStatus(formData: FormData) { const secret = await requireSessionSecret(); const id = String(formData.get("id")); const status = String(formData.get("status")) as | "new" | "read" | "replied" | "archived"; await tablesDB.updateRow( DATABASE_ID, TABLES.contactMessages, id, { status }, secret, ); revalidatePath("/admin/iletisim"); } export async function deleteMessage(formData: FormData) { const secret = await requireSessionSecret(); const id = String(formData.get("id")); await tablesDB.deleteRow( DATABASE_ID, TABLES.contactMessages, id, secret, ); revalidatePath("/admin/iletisim"); }