diff --git a/.gitignore b/.gitignore
index 5ef6a52..62fcee8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -39,3 +39,5 @@ yarn-error.log*
# typescript
*.tsbuildinfo
next-env.d.ts
+
+/lib/generated/prisma
diff --git a/ADMIN_CREDENTIALS.md b/ADMIN_CREDENTIALS.md
new file mode 100644
index 0000000..2af9bdd
--- /dev/null
+++ b/ADMIN_CREDENTIALS.md
@@ -0,0 +1,36 @@
+# Admin Panel Giriş Bilgileri
+
+## Yönetim Paneli URL
+`http://localhost:3000/yonetim-paneli-a2m-secure`
+
+## Giriş Bilgileri
+
+**Kullanıcı Adı:** `admin`
+
+**Şifre:** `AnkaraBel!2025.A2`
+
+---
+
+## Önemli Notlar
+
+- Bu şifre güçlü bir kombinasyondur (büyük/küçük harf, rakam, özel karakterler)
+- Production ortamına geçerken mutlaka .env dosyasındaki `ADMIN_DEFAULT_PASSWORD` değişkenini değiştirin
+- Şifre bcrypt ile hashlenmiş olarak veritabanında saklanır
+
+## Veritabanı Bilgileri
+
+- **Database:** SQLite (`prisma/dev.db`)
+- **ORM:** Prisma 7.0.0
+- **Auth:** JWT + bcrypt + httpOnly cookies
+
+## Seed Komutu
+
+Veritabanını sıfırlamak için:
+```bash
+npm run db:reset
+```
+
+Veritabanı içeriğini kontrol için:
+```bash
+sqlite3 prisma/dev.db "SELECT * FROM User;"
+```
diff --git a/app/api/auth/login/route.ts b/app/api/auth/login/route.ts
new file mode 100644
index 0000000..ec69753
--- /dev/null
+++ b/app/api/auth/login/route.ts
@@ -0,0 +1,73 @@
+import { NextRequest, NextResponse } from 'next/server';
+import { compare } from 'bcryptjs';
+import { prisma } from '@/lib/prisma';
+import { encrypt } from '@/lib/auth';
+
+export async function POST(request: NextRequest) {
+ try {
+ const { username, password } = await request.json();
+
+ if (!username || !password) {
+ return NextResponse.json(
+ { error: 'Kullanıcı adı ve şifre gerekli' },
+ { status: 400 }
+ );
+ }
+
+ // Kullanıcıyı bul
+ const user = await prisma.user.findUnique({
+ where: { username },
+ });
+
+ if (!user) {
+ return NextResponse.json(
+ { error: 'Kullanıcı adı veya şifre hatalı' },
+ { status: 401 }
+ );
+ }
+
+ // Şifreyi kontrol et
+ const isPasswordValid = await compare(password, user.password);
+
+ if (!isPasswordValid) {
+ return NextResponse.json(
+ { error: 'Kullanıcı adı veya şifre hatalı' },
+ { status: 401 }
+ );
+ }
+
+ // JWT token oluştur
+ const token = await encrypt({
+ userId: user.id.toString(),
+ username: user.username,
+ });
+
+ // Response oluştur
+ const response = NextResponse.json(
+ {
+ user: {
+ id: user.id,
+ username: user.username,
+ },
+ },
+ { status: 200 }
+ );
+
+ // Cookie'ye token ekle
+ response.cookies.set('session', token, {
+ httpOnly: true,
+ secure: process.env.NODE_ENV === 'production',
+ sameSite: 'lax',
+ maxAge: 60 * 60 * 24, // 24 saat
+ path: '/',
+ });
+
+ return response;
+ } catch (error) {
+ console.error('Login error:', error);
+ return NextResponse.json(
+ { error: 'Giriş yapılırken bir hata oluştu' },
+ { status: 500 }
+ );
+ }
+}
diff --git a/app/api/auth/logout/route.ts b/app/api/auth/logout/route.ts
new file mode 100644
index 0000000..4c5f277
--- /dev/null
+++ b/app/api/auth/logout/route.ts
@@ -0,0 +1,13 @@
+import { NextResponse } from 'next/server';
+
+export async function POST() {
+ const response = NextResponse.json(
+ { message: 'Çıkış başarılı' },
+ { status: 200 }
+ );
+
+ // Cookie'yi sil
+ response.cookies.delete('session');
+
+ return response;
+}
diff --git a/app/api/auth/session/route.ts b/app/api/auth/session/route.ts
new file mode 100644
index 0000000..116e204
--- /dev/null
+++ b/app/api/auth/session/route.ts
@@ -0,0 +1,18 @@
+import { NextResponse } from 'next/server';
+import { getSession } from '@/lib/auth';
+
+export async function GET() {
+ const session = await getSession();
+
+ if (!session) {
+ return NextResponse.json({ authenticated: false }, { status: 401 });
+ }
+
+ return NextResponse.json({
+ authenticated: true,
+ user: {
+ id: session.userId,
+ username: session.username,
+ },
+ });
+}
diff --git a/app/api/cameras/route.ts b/app/api/cameras/route.ts
new file mode 100644
index 0000000..edde3e0
--- /dev/null
+++ b/app/api/cameras/route.ts
@@ -0,0 +1,103 @@
+import { NextRequest, NextResponse } from 'next/server';
+import { prisma } from '@/lib/prisma';
+import { withAuth } from '@/lib/auth';
+
+// GET - Tüm kameraları getir
+export async function GET() {
+ try {
+ const cameras = await prisma.camera.findMany({
+ orderBy: { order: 'asc' },
+ });
+
+ return NextResponse.json(cameras);
+ } catch (error) {
+ console.error('Cameras fetch error:', error);
+ return NextResponse.json(
+ { error: 'Kameralar alınırken hata oluştu' },
+ { status: 500 }
+ );
+ }
+}
+
+// POST - Yeni kamera ekle (Auth gerekli)
+export async function POST(request: NextRequest) {
+ return withAuth(request, async () => {
+ try {
+ const data = await request.json();
+
+ const camera = await prisma.camera.create({
+ data: {
+ name: data.name,
+ location: data.location,
+ videoUrl: data.videoUrl,
+ status: data.status,
+ viewers: data.viewers || 0,
+ order: data.order,
+ },
+ });
+
+ return NextResponse.json(camera, { status: 201 });
+ } catch (error) {
+ console.error('Camera create error:', error);
+ return NextResponse.json(
+ { error: 'Kamera oluşturulurken hata oluştu' },
+ { status: 500 }
+ );
+ }
+ });
+}
+
+// PUT - Kamera güncelle (Auth gerekli)
+export async function PUT(request: NextRequest) {
+ return withAuth(request, async () => {
+ try {
+ const data = await request.json();
+
+ const camera = await prisma.camera.update({
+ where: { id: data.id },
+ data: {
+ name: data.name,
+ location: data.location,
+ videoUrl: data.videoUrl,
+ status: data.status,
+ viewers: data.viewers,
+ order: data.order,
+ },
+ });
+
+ return NextResponse.json(camera);
+ } catch (error) {
+ console.error('Camera update error:', error);
+ return NextResponse.json(
+ { error: 'Kamera güncellenirken hata oluştu' },
+ { status: 500 }
+ );
+ }
+ });
+}
+
+// DELETE - Kamera sil (Auth gerekli)
+export async function DELETE(request: NextRequest) {
+ return withAuth(request, async () => {
+ try {
+ const { searchParams } = new URL(request.url);
+ const id = searchParams.get('id');
+
+ if (!id) {
+ return NextResponse.json({ error: 'ID gerekli' }, { status: 400 });
+ }
+
+ await prisma.camera.delete({
+ where: { id: parseInt(id) },
+ });
+
+ return NextResponse.json({ message: 'Kamera silindi' });
+ } catch (error) {
+ console.error('Camera delete error:', error);
+ return NextResponse.json(
+ { error: 'Kamera silinirken hata oluştu' },
+ { status: 500 }
+ );
+ }
+ });
+}
diff --git a/app/api/documents/route.ts b/app/api/documents/route.ts
new file mode 100644
index 0000000..295a780
--- /dev/null
+++ b/app/api/documents/route.ts
@@ -0,0 +1,116 @@
+import { NextRequest, NextResponse } from 'next/server';
+import { prisma } from '@/lib/prisma';
+import { withAuth } from '@/lib/auth';
+
+// GET - Tüm dokümanları getir
+export async function GET() {
+ try {
+ const documents = await prisma.document.findMany({
+ orderBy: { createdAt: 'desc' },
+ });
+
+ return NextResponse.json(documents);
+ } catch (error) {
+ console.error('Documents fetch error:', error);
+ return NextResponse.json(
+ { error: 'Dökümanlar alınırken hata oluştu' },
+ { status: 500 }
+ );
+ }
+}
+
+// POST - Yeni döküman ekle (Auth gerekli)
+export async function POST(request: NextRequest) {
+ return withAuth(request, async () => {
+ try {
+ const data = await request.json();
+
+ const document = await prisma.document.create({
+ data: {
+ title: data.title,
+ type: data.type,
+ category: data.category,
+ downloadUrl: data.fileUrl || data.downloadUrl,
+ size: data.fileSize || data.size || '0 MB',
+ description: data.description,
+ date: new Date().toISOString().split('T')[0],
+ },
+ });
+
+ return NextResponse.json(document, { status: 201 });
+ } catch (error) {
+ console.error('Document create error:', error);
+ return NextResponse.json(
+ { error: 'Döküman oluşturulurken hata oluştu' },
+ { status: 500 }
+ );
+ }
+ });
+}
+
+// PUT - Döküman güncelle (Auth gerekli)
+export async function PUT(request: NextRequest) {
+ return withAuth(request, async () => {
+ try {
+ const data = await request.json();
+
+ const document = await prisma.document.update({
+ where: { id: data.id },
+ data: {
+ title: data.title,
+ type: data.type,
+ category: data.category,
+ downloadUrl: data.fileUrl || data.downloadUrl,
+ size: data.fileSize || data.size,
+ description: data.description,
+ },
+ });
+
+ return NextResponse.json(document);
+ } catch (error) {
+ console.error('Document update error:', error);
+ return NextResponse.json(
+ { error: 'Döküman güncellenirken hata oluştu' },
+ { status: 500 }
+ );
+ }
+ });
+}
+
+// DELETE - Döküman sil (Auth gerekli)
+export async function DELETE(request: NextRequest) {
+ return withAuth(request, async () => {
+ try {
+ const { searchParams } = new URL(request.url);
+ const id = searchParams.get('id');
+
+ console.log('DELETE document - ID:', id);
+
+ if (!id) {
+ return NextResponse.json({ error: 'ID gerekli' }, { status: 400 });
+ }
+
+ await prisma.document.delete({
+ where: { id: parseInt(id) },
+ });
+
+ return NextResponse.json({ message: 'Döküman silindi' });
+ } catch (error: unknown) {
+ console.error('Document delete error:', error);
+
+ // Prisma P2025: Record not found
+ if (error && typeof error === 'object' && 'code' in error && error.code === 'P2025') {
+ return NextResponse.json(
+ { error: 'Döküman bulunamadı (zaten silinmiş olabilir)' },
+ { status: 404 }
+ );
+ }
+
+ return NextResponse.json(
+ { error: 'Döküman silinirken hata oluştu' },
+ { status: 500 }
+ );
+ }
+ });
+}
+
diff --git a/app/api/faqs/route.ts b/app/api/faqs/route.ts
new file mode 100644
index 0000000..cf348da
--- /dev/null
+++ b/app/api/faqs/route.ts
@@ -0,0 +1,97 @@
+import { NextRequest, NextResponse } from 'next/server';
+import { prisma } from '@/lib/prisma';
+import { withAuth } from '@/lib/auth';
+
+// GET - Tüm SSS'leri getir
+export async function GET() {
+ try {
+ const faqs = await prisma.fAQ.findMany({
+ orderBy: { order: 'asc' },
+ });
+
+ return NextResponse.json(faqs);
+ } catch (error) {
+ console.error('FAQs fetch error:', error);
+ return NextResponse.json(
+ { error: 'SSS alınırken hata oluştu' },
+ { status: 500 }
+ );
+ }
+}
+
+// POST - Yeni SSS ekle (Auth gerekli)
+export async function POST(request: NextRequest) {
+ return withAuth(request, async () => {
+ try {
+ const data = await request.json();
+
+ const faq = await prisma.fAQ.create({
+ data: {
+ question: data.question,
+ answer: data.answer,
+ order: data.order,
+ },
+ });
+
+ return NextResponse.json(faq, { status: 201 });
+ } catch (error) {
+ console.error('FAQ create error:', error);
+ return NextResponse.json(
+ { error: 'SSS oluşturulurken hata oluştu' },
+ { status: 500 }
+ );
+ }
+ });
+}
+
+// PUT - SSS güncelle (Auth gerekli)
+export async function PUT(request: NextRequest) {
+ return withAuth(request, async () => {
+ try {
+ const data = await request.json();
+
+ const faq = await prisma.fAQ.update({
+ where: { id: data.id },
+ data: {
+ question: data.question,
+ answer: data.answer,
+ order: data.order,
+ },
+ });
+
+ return NextResponse.json(faq);
+ } catch (error) {
+ console.error('FAQ update error:', error);
+ return NextResponse.json(
+ { error: 'SSS güncellenirken hata oluştu' },
+ { status: 500 }
+ );
+ }
+ });
+}
+
+// DELETE - SSS sil (Auth gerekli)
+export async function DELETE(request: NextRequest) {
+ return withAuth(request, async () => {
+ try {
+ const { searchParams } = new URL(request.url);
+ const id = searchParams.get('id');
+
+ if (!id) {
+ return NextResponse.json({ error: 'ID gerekli' }, { status: 400 });
+ }
+
+ await prisma.fAQ.delete({
+ where: { id: parseInt(id) },
+ });
+
+ return NextResponse.json({ message: 'SSS silindi' });
+ } catch (error) {
+ console.error('FAQ delete error:', error);
+ return NextResponse.json(
+ { error: 'SSS silinirken hata oluştu' },
+ { status: 500 }
+ );
+ }
+ });
+}
diff --git a/app/api/live-stream/route.ts b/app/api/live-stream/route.ts
new file mode 100644
index 0000000..16f306a
--- /dev/null
+++ b/app/api/live-stream/route.ts
@@ -0,0 +1,57 @@
+import { NextRequest, NextResponse } from 'next/server';
+import { prisma } from '@/lib/prisma';
+import { withAuth } from '@/lib/auth';
+
+// GET - Aktif canlı yayın bilgisini getir
+export async function GET() {
+ try {
+ const liveStream = await prisma.liveStream.findFirst({
+ where: { active: true },
+ });
+
+ return NextResponse.json(liveStream || { active: false, url: '', title: '' });
+ } catch (error) {
+ console.error('Live stream fetch error:', error);
+ return NextResponse.json(
+ { error: 'Canlı yayın bilgisi alınırken hata oluştu' },
+ { status: 500 }
+ );
+ }
+}
+
+// POST - Canlı yayın ayarlarını güncelle (Auth gerekli)
+export async function POST(request: NextRequest) {
+ return withAuth(request, async () => {
+ try {
+ const data = await request.json();
+
+ // Önce tüm canlı yayınları pasif yap
+ await prisma.liveStream.updateMany({
+ data: { active: false },
+ });
+
+ // Yeni ayarları oluştur veya güncelle
+ const liveStream = await prisma.liveStream.upsert({
+ where: { id: data.id || 0 },
+ update: {
+ url: data.url,
+ active: data.active,
+ title: data.title,
+ },
+ create: {
+ url: data.url,
+ active: data.active,
+ title: data.title,
+ },
+ });
+
+ return NextResponse.json(liveStream);
+ } catch (error) {
+ console.error('Live stream update error:', error);
+ return NextResponse.json(
+ { error: 'Canlı yayın güncellenirken hata oluştu' },
+ { status: 500 }
+ );
+ }
+ });
+}
diff --git a/app/api/media/route.ts b/app/api/media/route.ts
new file mode 100644
index 0000000..3f0d0cd
--- /dev/null
+++ b/app/api/media/route.ts
@@ -0,0 +1,102 @@
+import { NextRequest, NextResponse } from 'next/server';
+import { prisma } from '@/lib/prisma';
+import { withAuth } from '@/lib/auth';
+
+// GET - Tüm medya öğelerini getir
+export async function GET() {
+ try {
+ const media = await prisma.media.findMany({
+ orderBy: { createdAt: 'desc' },
+ });
+
+ return NextResponse.json(media);
+ } catch (error) {
+ console.error('Media fetch error:', error);
+ return NextResponse.json(
+ { error: 'Medya öğeleri alınırken hata oluştu' },
+ { status: 500 }
+ );
+ }
+}
+
+// POST - Yeni medya öğesi ekle (Auth gerekli)
+export async function POST(request: NextRequest) {
+ return withAuth(request, async () => {
+ try {
+ const data = await request.json();
+
+ const media = await prisma.media.create({
+ data: {
+ title: data.title,
+ type: data.type,
+ thumbnail: data.thumbnail,
+ videoUrl: data.videoUrl,
+ description: data.description,
+ date: new Date().toISOString().split('T')[0],
+ },
+ });
+
+ return NextResponse.json(media, { status: 201 });
+ } catch (error) {
+ console.error('Media create error:', error);
+ return NextResponse.json(
+ { error: 'Medya öğesi oluşturulurken hata oluştu' },
+ { status: 500 }
+ );
+ }
+ });
+}
+
+// PUT - Medya öğesi güncelle (Auth gerekli)
+export async function PUT(request: NextRequest) {
+ return withAuth(request, async () => {
+ try {
+ const data = await request.json();
+
+ const media = await prisma.media.update({
+ where: { id: data.id },
+ data: {
+ title: data.title,
+ type: data.type,
+ thumbnail: data.thumbnail,
+ videoUrl: data.videoUrl,
+ description: data.description,
+ },
+ });
+
+ return NextResponse.json(media);
+ } catch (error) {
+ console.error('Media update error:', error);
+ return NextResponse.json(
+ { error: 'Medya öğesi güncellenirken hata oluştu' },
+ { status: 500 }
+ );
+ }
+ });
+}
+
+// DELETE - Medya öğesi sil (Auth gerekli)
+export async function DELETE(request: NextRequest) {
+ return withAuth(request, async () => {
+ try {
+ const { searchParams } = new URL(request.url);
+ const id = searchParams.get('id');
+
+ if (!id) {
+ return NextResponse.json({ error: 'ID gerekli' }, { status: 400 });
+ }
+
+ await prisma.media.delete({
+ where: { id: parseInt(id) },
+ });
+
+ return NextResponse.json({ message: 'Medya öğesi silindi' });
+ } catch (error) {
+ console.error('Media delete error:', error);
+ return NextResponse.json(
+ { error: 'Medya öğesi silinirken hata oluştu' },
+ { status: 500 }
+ );
+ }
+ });
+}
diff --git a/app/api/messages/route.ts b/app/api/messages/route.ts
new file mode 100644
index 0000000..57f6074
--- /dev/null
+++ b/app/api/messages/route.ts
@@ -0,0 +1,98 @@
+import { NextRequest, NextResponse } from 'next/server';
+import { prisma } from '@/lib/prisma';
+import { withAuth } from '@/lib/auth';
+
+// GET - Tüm mesajları getir (Auth gerekli)
+export async function GET(request: NextRequest) {
+ return withAuth(request, async () => {
+ try {
+ const messages = await prisma.message.findMany({
+ orderBy: { createdAt: 'desc' },
+ });
+
+ return NextResponse.json(messages);
+ } catch (error) {
+ console.error('Messages fetch error:', error);
+ return NextResponse.json(
+ { error: 'Mesajlar alınırken hata oluştu' },
+ { status: 500 }
+ );
+ }
+ });
+}
+
+// POST - Yeni mesaj gönder (Public - iletişim formu)
+export async function POST(request: NextRequest) {
+ try {
+ const data = await request.json();
+
+ const message = await prisma.message.create({
+ data: {
+ name: data.name,
+ email: data.email,
+ phone: data.phone || '',
+ subject: data.subject,
+ type: data.type || 'contact',
+ message: data.message,
+ read: false,
+ date: new Date().toISOString().split('T')[0],
+ },
+ });
+
+ return NextResponse.json(message, { status: 201 });
+ } catch (error) {
+ console.error('Message create error:', error);
+ return NextResponse.json(
+ { error: 'Mesaj gönderilirken hata oluştu' },
+ { status: 500 }
+ );
+ }
+}
+
+// PUT - Mesajı okundu olarak işaretle (Auth gerekli)
+export async function PUT(request: NextRequest) {
+ return withAuth(request, async () => {
+ try {
+ const data = await request.json();
+
+ const message = await prisma.message.update({
+ where: { id: data.id },
+ data: { read: data.read },
+ });
+
+ return NextResponse.json(message);
+ } catch (error) {
+ console.error('Message update error:', error);
+ return NextResponse.json(
+ { error: 'Mesaj güncellenirken hata oluştu' },
+ { status: 500 }
+ );
+ }
+ });
+}
+
+// DELETE - Mesaj sil (Auth gerekli)
+export async function DELETE(request: NextRequest) {
+ return withAuth(request, async () => {
+ try {
+ const { searchParams } = new URL(request.url);
+ const id = searchParams.get('id');
+
+ if (!id) {
+ return NextResponse.json({ error: 'ID gerekli' }, { status: 400 });
+ }
+
+ await prisma.message.delete({
+ where: { id: parseInt(id) },
+ });
+
+ return NextResponse.json({ message: 'Mesaj silindi' });
+ } catch (error) {
+ console.error('Message delete error:', error);
+ return NextResponse.json(
+ { error: 'Mesaj silinirken hata oluştu' },
+ { status: 500 }
+ );
+ }
+ });
+}
diff --git a/app/api/news/route.ts b/app/api/news/route.ts
new file mode 100644
index 0000000..423bab8
--- /dev/null
+++ b/app/api/news/route.ts
@@ -0,0 +1,107 @@
+import { NextRequest, NextResponse } from 'next/server';
+import { prisma } from '@/lib/prisma';
+import { withAuth } from '@/lib/auth';
+
+// GET - Tüm haberleri getir
+export async function GET() {
+ try {
+ const news = await prisma.news.findMany({
+ orderBy: { createdAt: 'desc' },
+ });
+
+ return NextResponse.json(news);
+ } catch (error) {
+ console.error('News fetch error:', error);
+ return NextResponse.json(
+ { error: 'Haberler alınırken hata oluştu' },
+ { status: 500 }
+ );
+ }
+}
+
+// POST - Yeni haber ekle (Auth gerekli)
+export async function POST(request: NextRequest) {
+ return withAuth(request, async () => {
+ try {
+ const data = await request.json();
+
+ const news = await prisma.news.create({
+ data: {
+ title: data.title,
+ summary: data.summary || data.content.substring(0, 150) + '...',
+ content: data.content,
+ category: data.category,
+ image: data.image,
+ author: data.author,
+ tags: data.tags || '',
+ date: new Date().toISOString().split('T')[0],
+ featured: data.featured || false,
+ },
+ });
+
+ return NextResponse.json(news, { status: 201 });
+ } catch (error) {
+ console.error('News create error:', error);
+ return NextResponse.json(
+ { error: 'Haber oluşturulurken hata oluştu' },
+ { status: 500 }
+ );
+ }
+ });
+}
+
+// PUT - Haber güncelle (Auth gerekli)
+export async function PUT(request: NextRequest) {
+ return withAuth(request, async () => {
+ try {
+ const data = await request.json();
+
+ const news = await prisma.news.update({
+ where: { id: data.id },
+ data: {
+ title: data.title,
+ summary: data.summary,
+ content: data.content,
+ category: data.category,
+ image: data.image,
+ author: data.author,
+ tags: data.tags,
+ },
+ });
+
+ return NextResponse.json(news);
+ } catch (error) {
+ console.error('News update error:', error);
+ return NextResponse.json(
+ { error: 'Haber güncellenirken hata oluştu' },
+ { status: 500 }
+ );
+ }
+ });
+}
+
+// DELETE - Haber sil (Auth gerekli)
+export async function DELETE(request: NextRequest) {
+ return withAuth(request, async () => {
+ try {
+ const { searchParams } = new URL(request.url);
+ const id = searchParams.get('id');
+
+ if (!id) {
+ return NextResponse.json({ error: 'ID gerekli' }, { status: 400 });
+ }
+
+ await prisma.news.delete({
+ where: { id: parseInt(id) },
+ });
+
+ return NextResponse.json({ message: 'Haber silindi' });
+ } catch (error) {
+ console.error('News delete error:', error);
+ return NextResponse.json(
+ { error: 'Haber silinirken hata oluştu' },
+ { status: 500 }
+ );
+ }
+ });
+}
diff --git a/app/api/settings/route.ts b/app/api/settings/route.ts
new file mode 100644
index 0000000..11e6313
--- /dev/null
+++ b/app/api/settings/route.ts
@@ -0,0 +1,52 @@
+import { NextRequest, NextResponse } from 'next/server';
+import { prisma } from '@/lib/prisma';
+import { withAuth } from '@/lib/auth';
+
+// GET - Site ayarlarını getir
+export async function GET() {
+ try {
+ const settings = await prisma.siteSettings.findFirst({
+ where: { key: 'main' },
+ });
+
+ if (!settings) {
+ return NextResponse.json({ error: 'Ayarlar bulunamadı' }, { status: 404 });
+ }
+
+ return NextResponse.json(JSON.parse(settings.value));
+ } catch (error) {
+ console.error('Settings fetch error:', error);
+ return NextResponse.json(
+ { error: 'Ayarlar alınırken hata oluştu' },
+ { status: 500 }
+ );
+ }
+}
+
+// POST - Site ayarlarını güncelle (Auth gerekli)
+export async function POST(request: NextRequest) {
+ return withAuth(request, async () => {
+ try {
+ const data = await request.json();
+
+ const settings = await prisma.siteSettings.upsert({
+ where: { key: 'main' },
+ update: {
+ value: JSON.stringify(data),
+ },
+ create: {
+ key: 'main',
+ value: JSON.stringify(data),
+ },
+ });
+
+ return NextResponse.json(JSON.parse(settings.value));
+ } catch (error) {
+ console.error('Settings update error:', error);
+ return NextResponse.json(
+ { error: 'Ayarlar güncellenirken hata oluştu' },
+ { status: 500 }
+ );
+ }
+ });
+}
diff --git a/app/api/slider/route.ts b/app/api/slider/route.ts
new file mode 100644
index 0000000..aff0501
--- /dev/null
+++ b/app/api/slider/route.ts
@@ -0,0 +1,101 @@
+import { NextRequest, NextResponse } from 'next/server';
+import { prisma } from '@/lib/prisma';
+import { withAuth } from '@/lib/auth';
+
+// GET - Tüm slider item'ları getir
+export async function GET() {
+ try {
+ const items = await prisma.sliderItem.findMany({
+ orderBy: { id: 'asc' },
+ });
+
+ return NextResponse.json(items);
+ } catch (error) {
+ console.error('Slider items fetch error:', error);
+ return NextResponse.json(
+ { error: 'Slider items alınırken hata oluştu' },
+ { status: 500 }
+ );
+ }
+}
+
+// POST - Yeni slider item ekle (Auth gerekli)
+export async function POST(request: NextRequest) {
+ return withAuth(request, async () => {
+ try {
+ const data = await request.json();
+
+ const item = await prisma.sliderItem.create({
+ data: {
+ title: data.title,
+ description: data.description,
+ buttonText: data.buttonText,
+ buttonLink: data.buttonLink,
+ active: data.active ?? true,
+ },
+ });
+
+ return NextResponse.json(item, { status: 201 });
+ } catch (error) {
+ console.error('Slider item create error:', error);
+ return NextResponse.json(
+ { error: 'Slider item oluşturulurken hata oluştu' },
+ { status: 500 }
+ );
+ }
+ });
+}
+
+// PUT - Slider item güncelle (Auth gerekli)
+export async function PUT(request: NextRequest) {
+ return withAuth(request, async () => {
+ try {
+ const data = await request.json();
+
+ const item = await prisma.sliderItem.update({
+ where: { id: data.id },
+ data: {
+ title: data.title,
+ description: data.description,
+ buttonText: data.buttonText,
+ buttonLink: data.buttonLink,
+ active: data.active,
+ },
+ });
+
+ return NextResponse.json(item);
+ } catch (error) {
+ console.error('Slider item update error:', error);
+ return NextResponse.json(
+ { error: 'Slider item güncellenirken hata oluştu' },
+ { status: 500 }
+ );
+ }
+ });
+}
+
+// DELETE - Slider item sil (Auth gerekli)
+export async function DELETE(request: NextRequest) {
+ return withAuth(request, async () => {
+ try {
+ const { searchParams } = new URL(request.url);
+ const id = searchParams.get('id');
+
+ if (!id) {
+ return NextResponse.json({ error: 'ID gerekli' }, { status: 400 });
+ }
+
+ await prisma.sliderItem.delete({
+ where: { id: parseInt(id) },
+ });
+
+ return NextResponse.json({ message: 'Slider item silindi' });
+ } catch (error) {
+ console.error('Slider item delete error:', error);
+ return NextResponse.json(
+ { error: 'Slider item silinirken hata oluştu' },
+ { status: 500 }
+ );
+ }
+ });
+}
diff --git a/app/api/stations/route.ts b/app/api/stations/route.ts
new file mode 100644
index 0000000..b017a67
--- /dev/null
+++ b/app/api/stations/route.ts
@@ -0,0 +1,101 @@
+import { NextRequest, NextResponse } from 'next/server';
+import { prisma } from '@/lib/prisma';
+import { withAuth } from '@/lib/auth';
+
+// GET - Tüm metro istasyonlarını getir
+export async function GET() {
+ try {
+ const stations = await prisma.metroStation.findMany({
+ orderBy: { order: 'asc' },
+ });
+
+ return NextResponse.json(stations);
+ } catch (error) {
+ console.error('Stations fetch error:', error);
+ return NextResponse.json(
+ { error: 'İstasyonlar alınırken hata oluştu' },
+ { status: 500 }
+ );
+ }
+}
+
+// POST - Yeni istasyon ekle (Auth gerekli)
+export async function POST(request: NextRequest) {
+ return withAuth(request, async () => {
+ try {
+ const data = await request.json();
+
+ const station = await prisma.metroStation.create({
+ data: {
+ name: data.name,
+ status: data.status,
+ progress: data.progress,
+ order: data.order,
+ description: data.description || data.estimatedCompletion,
+ },
+ });
+
+ return NextResponse.json(station, { status: 201 });
+ } catch (error) {
+ console.error('Station create error:', error);
+ return NextResponse.json(
+ { error: 'İstasyon oluşturulurken hata oluştu' },
+ { status: 500 }
+ );
+ }
+ });
+}
+
+// PUT - İstasyon güncelle (Auth gerekli)
+export async function PUT(request: NextRequest) {
+ return withAuth(request, async () => {
+ try {
+ const data = await request.json();
+
+ const station = await prisma.metroStation.update({
+ where: { id: data.id },
+ data: {
+ name: data.name,
+ status: data.status,
+ progress: data.progress,
+ order: data.order,
+ description: data.description || data.estimatedCompletion,
+ },
+ });
+
+ return NextResponse.json(station);
+ } catch (error) {
+ console.error('Station update error:', error);
+ return NextResponse.json(
+ { error: 'İstasyon güncellenirken hata oluştu' },
+ { status: 500 }
+ );
+ }
+ });
+}
+
+// DELETE - İstasyon sil (Auth gerekli)
+export async function DELETE(request: NextRequest) {
+ return withAuth(request, async () => {
+ try {
+ const { searchParams } = new URL(request.url);
+ const id = searchParams.get('id');
+
+ if (!id) {
+ return NextResponse.json({ error: 'ID gerekli' }, { status: 400 });
+ }
+
+ await prisma.metroStation.delete({
+ where: { id: parseInt(id) },
+ });
+
+ return NextResponse.json({ message: 'İstasyon silindi' });
+ } catch (error) {
+ console.error('Station delete error:', error);
+ return NextResponse.json(
+ { error: 'İstasyon silinirken hata oluştu' },
+ { status: 500 }
+ );
+ }
+ });
+}
diff --git a/app/belgeler/page.tsx b/app/belgeler/page.tsx
index 559836c..77b47af 100644
--- a/app/belgeler/page.tsx
+++ b/app/belgeler/page.tsx
@@ -11,7 +11,7 @@ export default function Documents() {
const [selectedCategory, setSelectedCategory] = useState('all');
useEffect(() => {
- setDocuments(dataStore.getDocuments());
+ dataStore.getDocuments().then(setDocuments);
}, []);
const categories = [
diff --git a/app/canli-yayin/page.tsx b/app/canli-yayin/page.tsx
index d256150..976b2bb 100644
--- a/app/canli-yayin/page.tsx
+++ b/app/canli-yayin/page.tsx
@@ -11,13 +11,15 @@ export default function LiveStream() {
const [cameras, setCameras] = useState