This commit is contained in:
Şahan Hasret
2025-11-21 17:46:30 +03:00
parent c0b7fb463e
commit 76c31274d5
46 changed files with 3675 additions and 1043 deletions

59
lib/auth.ts Normal file
View File

@@ -0,0 +1,59 @@
import { jwtVerify, SignJWT } from 'jose';
import { cookies } from 'next/headers';
import { NextRequest, NextResponse } from 'next/server';
const secret = new TextEncoder().encode(
process.env.JWT_SECRET || 'default-secret-key'
);
export interface SessionPayload {
userId: string;
username: string;
iat?: number;
exp?: number;
}
export async function encrypt(payload: SessionPayload) {
return await new SignJWT(payload as unknown as Record<string, unknown>)
.setProtectedHeader({ alg: 'HS256' })
.setIssuedAt()
.setExpirationTime('24h')
.sign(secret);
}
export async function decrypt(token: string): Promise<SessionPayload | null> {
try {
const { payload } = await jwtVerify(token, secret, {
algorithms: ['HS256'],
});
return payload as unknown as SessionPayload;
} catch {
return null;
}
}
export async function getSession() {
const cookieStore = await cookies();
const token = cookieStore.get('session')?.value;
if (!token) return null;
return await decrypt(token);
}
export async function withAuth(
request: NextRequest,
handler: (req: NextRequest, session: SessionPayload) => Promise<NextResponse>
) {
const token = request.cookies.get('session')?.value;
if (!token) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
const session = await decrypt(token);
if (!session) {
return NextResponse.json({ error: 'Invalid token' }, { status: 401 });
}
return handler(request, session);
}

View File

@@ -1,4 +1,4 @@
// Veri yönetimi için localStorage tabanlı store
// Veri yönetimi için API tabanlı store
import { newsData, type NewsItem } from '@/data/news';
import { mediaData, type MediaItem } from '@/data/media';
import { documentsData, type Document } from '@/data/documents';
@@ -20,7 +20,7 @@ export interface LiveStreamConfig {
}
export interface Message {
id: string;
id: string | number;
name: string;
email: string;
phone: string;
@@ -69,6 +69,10 @@ export interface SiteSettings {
};
}
// API helper
const API_BASE = '/api';
const isBrowser = typeof window !== 'undefined';
// Default slider data
export const defaultSliderData: SliderItem[] = [
{
@@ -197,147 +201,301 @@ export const defaultCameraData: Camera[] = [
}
];
// LocalStorage keys
const KEYS = {
SLIDER: 'a2metro_slider',
NEWS: 'a2metro_news',
MEDIA: 'a2metro_media',
DOCUMENTS: 'a2metro_documents',
METRO_STATIONS: 'a2metro_stations',
LIVE_STREAM: 'a2metro_live_stream',
SITE_SETTINGS: 'a2metro_site_settings',
MESSAGES: 'a2metro_messages',
FAQS: 'a2metro_faqs',
CAMERAS: 'a2metro_cameras'
};
// Helper functions
const isBrowser = typeof window !== 'undefined';
export const dataStore = {
// Slider
getSlider: (): SliderItem[] => {
getSlider: async (): Promise<SliderItem[]> => {
if (!isBrowser) return defaultSliderData;
const stored = localStorage.getItem(KEYS.SLIDER);
return stored ? JSON.parse(stored) : defaultSliderData;
try {
const res = await fetch(`${API_BASE}/slider`);
if (!res.ok) throw new Error('Failed to fetch');
const data = await res.json();
return data.length > 0 ? data : defaultSliderData;
} catch (error) {
console.error('Slider fetch error:', error);
return defaultSliderData;
}
},
setSlider: (data: SliderItem[]) => {
if (isBrowser) {
localStorage.setItem(KEYS.SLIDER, JSON.stringify(data));
setSlider: async () => {
// Not used - individual items updated via API
console.warn('setSlider deprecated - use addSlider/updateSlider/deleteSlider');
},
addSlider: async (item: Omit<SliderItem, 'id'>) => {
try {
const res = await fetch(`${API_BASE}/slider`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(item),
});
if (!res.ok) throw new Error('Failed to add');
return await res.json();
} catch (error) {
console.error('Slider add error:', error);
throw error;
}
},
updateSlider: async (id: number, item: Partial<SliderItem>) => {
try {
const res = await fetch(`${API_BASE}/slider`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ id, ...item }),
});
if (!res.ok) throw new Error('Failed to update');
return await res.json();
} catch (error) {
console.error('Slider update error:', error);
throw error;
}
},
deleteSlider: async (id: number) => {
try {
const res = await fetch(`${API_BASE}/slider?id=${id}`, {
method: 'DELETE',
credentials: 'include',
});
if (!res.ok) throw new Error('Failed to delete');
return await res.json();
} catch (error) {
console.error('Slider delete error:', error);
throw error;
}
},
// News
getNews: (): NewsItem[] => {
getNews: async (): Promise<NewsItem[]> => {
if (!isBrowser) return newsData;
const stored = localStorage.getItem(KEYS.NEWS);
return stored ? JSON.parse(stored) : newsData;
},
setNews: (data: NewsItem[]) => {
if (isBrowser) {
localStorage.setItem(KEYS.NEWS, JSON.stringify(data));
try {
const res = await fetch(`${API_BASE}/news`);
if (!res.ok) throw new Error('Failed to fetch');
const data = await res.json();
return data.length > 0 ? data : newsData;
} catch (error) {
console.error('News fetch error:', error);
return newsData;
}
},
addNews: (newsItem: Omit<NewsItem, 'id'>) => {
const current = dataStore.getNews();
const newItem = { ...newsItem, id: Date.now() };
dataStore.setNews([newItem, ...current]);
return newItem;
setNews: async () => {
console.warn('setNews deprecated - use addNews/updateNews/deleteNews');
},
updateNews: (id: number, newsItem: Partial<NewsItem>) => {
const current = dataStore.getNews();
const updated = current.map((item) =>
item.id === id ? { ...item, ...newsItem } : item
);
dataStore.setNews(updated);
addNews: async (newsItem: Omit<NewsItem, 'id'>) => {
try {
const res = await fetch(`${API_BASE}/news`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(newsItem),
});
if (!res.ok) throw new Error('Failed to add');
return await res.json();
} catch (error) {
console.error('News add error:', error);
throw error;
}
},
deleteNews: (id: number) => {
const current = dataStore.getNews();
const filtered = current.filter((item) => item.id !== id);
dataStore.setNews(filtered);
updateNews: async (id: number, newsItem: Partial<NewsItem>) => {
try {
const res = await fetch(`${API_BASE}/news`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ id, ...newsItem }),
});
if (!res.ok) throw new Error('Failed to update');
return await res.json();
} catch (error) {
console.error('News update error:', error);
throw error;
}
},
deleteNews: async (id: number) => {
try {
const res = await fetch(`${API_BASE}/news?id=${id}`, {
method: 'DELETE',
credentials: 'include',
});
if (!res.ok) throw new Error('Failed to delete');
return await res.json();
} catch (error) {
console.error('News delete error:', error);
throw error;
}
},
// Media
getMedia: (): MediaItem[] => {
getMedia: async (): Promise<MediaItem[]> => {
if (!isBrowser) return mediaData;
const stored = localStorage.getItem(KEYS.MEDIA);
return stored ? JSON.parse(stored) : mediaData;
},
setMedia: (data: MediaItem[]) => {
if (isBrowser) {
localStorage.setItem(KEYS.MEDIA, JSON.stringify(data));
try {
const res = await fetch(`${API_BASE}/media`);
if (!res.ok) throw new Error('Failed to fetch');
const data = await res.json();
return data.length > 0 ? data : mediaData;
} catch (error) {
console.error('Media fetch error:', error);
return mediaData;
}
},
addMedia: (mediaItem: Omit<MediaItem, 'id'>) => {
const current = dataStore.getMedia();
const newItem = { ...mediaItem, id: Date.now() };
dataStore.setMedia([newItem, ...current]);
return newItem;
setMedia: async () => {
console.warn('setMedia deprecated - use addMedia/updateMedia/deleteMedia');
},
updateMedia: (id: number, mediaItem: Partial<MediaItem>) => {
const current = dataStore.getMedia();
const updated = current.map((item) =>
item.id === id ? { ...item, ...mediaItem } : item
);
dataStore.setMedia(updated);
addMedia: async (mediaItem: Omit<MediaItem, 'id'>) => {
try {
const res = await fetch(`${API_BASE}/media`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(mediaItem),
});
if (!res.ok) throw new Error('Failed to add');
return await res.json();
} catch (error) {
console.error('Media add error:', error);
throw error;
}
},
deleteMedia: (id: number) => {
const current = dataStore.getMedia();
const filtered = current.filter((item) => item.id !== id);
dataStore.setMedia(filtered);
updateMedia: async (id: number, mediaItem: Partial<MediaItem>) => {
try {
const res = await fetch(`${API_BASE}/media`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ id, ...mediaItem }),
});
if (!res.ok) throw new Error('Failed to update');
return await res.json();
} catch (error) {
console.error('Media update error:', error);
throw error;
}
},
deleteMedia: async (id: number) => {
try {
const res = await fetch(`${API_BASE}/media?id=${id}`, {
method: 'DELETE',
credentials: 'include',
});
if (!res.ok) throw new Error('Failed to delete');
return await res.json();
} catch (error) {
console.error('Media delete error:', error);
throw error;
}
},
// Documents
getDocuments: (): Document[] => {
getDocuments: async (): Promise<Document[]> => {
if (!isBrowser) return documentsData;
const stored = localStorage.getItem(KEYS.DOCUMENTS);
return stored ? JSON.parse(stored) : documentsData;
},
setDocuments: (data: Document[]) => {
if (isBrowser) {
localStorage.setItem(KEYS.DOCUMENTS, JSON.stringify(data));
try {
const res = await fetch(`${API_BASE}/documents`);
if (!res.ok) throw new Error('Failed to fetch');
const data = await res.json();
return data.length > 0 ? data : documentsData;
} catch (error) {
console.error('Documents fetch error:', error);
return documentsData;
}
},
addDocument: (document: Omit<Document, 'id'>) => {
const current = dataStore.getDocuments();
const newItem = { ...document, id: Date.now() };
dataStore.setDocuments([newItem, ...current]);
return newItem;
setDocuments: async () => {
console.warn('setDocuments deprecated - use addDocument/updateDocument/deleteDocument');
},
updateDocument: (id: number, document: Partial<Document>) => {
const current = dataStore.getDocuments();
const updated = current.map((item) =>
item.id === id ? { ...item, ...document } : item
);
dataStore.setDocuments(updated);
addDocument: async (document: Omit<Document, 'id'>) => {
try {
const res = await fetch(`${API_BASE}/documents`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(document),
});
if (!res.ok) throw new Error('Failed to add');
return await res.json();
} catch (error) {
console.error('Document add error:', error);
throw error;
}
},
deleteDocument: (id: number) => {
const current = dataStore.getDocuments();
const filtered = current.filter((item) => item.id !== id);
dataStore.setDocuments(filtered);
updateDocument: async (id: number, document: Partial<Document>) => {
try {
const res = await fetch(`${API_BASE}/documents`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ id, ...document }),
});
if (!res.ok) throw new Error('Failed to update');
return await res.json();
} catch (error) {
console.error('Document update error:', error);
throw error;
}
},
deleteDocument: async (id: number) => {
try {
const res = await fetch(`${API_BASE}/documents?id=${id}`, {
method: 'DELETE',
credentials: 'include',
});
if (!res.ok) {
console.error('Delete document failed - Status:', res.status, res.statusText);
const text = await res.text();
console.error('Response body:', text);
try {
const errorData = JSON.parse(text);
throw new Error(errorData.error || 'Failed to delete');
} catch {
throw new Error(`HTTP ${res.status}: ${res.statusText}`);
}
}
return await res.json();
} catch (error) {
console.error('Document delete error:', error);
throw error;
}
},
// Metro Stations
getMetroStations: (): MetroStation[] => {
getMetroStations: async (): Promise<MetroStation[]> => {
if (!isBrowser) return metroStations;
const stored = localStorage.getItem(KEYS.METRO_STATIONS);
return stored ? JSON.parse(stored) : metroStations;
},
setMetroStations: (data: MetroStation[]) => {
if (isBrowser) {
localStorage.setItem(KEYS.METRO_STATIONS, JSON.stringify(data));
try {
const res = await fetch(`${API_BASE}/stations`);
if (!res.ok) throw new Error('Failed to fetch');
const data = await res.json();
return data.length > 0 ? data : metroStations;
} catch (error) {
console.error('Stations fetch error:', error);
return metroStations;
}
},
updateStation: (id: number, station: Partial<MetroStation>) => {
const current = dataStore.getMetroStations();
const updated = current.map((item) =>
item.id === id ? { ...item, ...station } : item
);
dataStore.setMetroStations(updated);
setMetroStations: async () => {
console.warn('setMetroStations deprecated - use updateStation');
},
updateStation: async (id: number, station: Partial<MetroStation>) => {
try {
const res = await fetch(`${API_BASE}/stations`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ id, ...station }),
});
if (!res.ok) throw new Error('Failed to update');
return await res.json();
} catch (error) {
console.error('Station update error:', error);
throw error;
}
},
// Live Stream
getLiveStream: (): LiveStreamConfig => {
getLiveStream: async (): Promise<LiveStreamConfig> => {
if (!isBrowser) {
return {
url: 'https://www.youtube.com/embed/jfKfPfyJRdk?si=example',
@@ -345,62 +503,96 @@ export const dataStore = {
title: 'Canlı Yayın'
};
}
const stored = localStorage.getItem(KEYS.LIVE_STREAM);
return stored ? JSON.parse(stored) : {
url: 'https://www.youtube.com/embed/jfKfPfyJRdk?si=example',
active: true,
title: 'Canlı Yayın'
};
try {
const res = await fetch(`${API_BASE}/live-stream`);
if (!res.ok) throw new Error('Failed to fetch');
return await res.json();
} catch (error) {
console.error('Live stream fetch error:', error);
return {
url: 'https://www.youtube.com/embed/jfKfPfyJRdk?si=example',
active: true,
title: 'Canlı Yayın'
};
}
},
setLiveStream: (config: LiveStreamConfig) => {
if (isBrowser) {
localStorage.setItem(KEYS.LIVE_STREAM, JSON.stringify(config));
setLiveStream: async (config: LiveStreamConfig) => {
try {
const res = await fetch(`${API_BASE}/live-stream`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify(config),
});
if (!res.ok) throw new Error('Failed to update');
return await res.json();
} catch (error) {
console.error('Live stream update error:', error);
throw error;
}
},
// Messages
getMessages: (): Message[] => {
getMessages: async (): Promise<Message[]> => {
if (!isBrowser) return [];
const stored = localStorage.getItem(KEYS.MESSAGES);
return stored ? JSON.parse(stored) : [];
},
addMessage: (message: Omit<Message, 'id' | 'date' | 'read'>) => {
const messages = dataStore.getMessages();
const newMessage: Message = {
...message,
id: Date.now().toString(),
date: new Date().toISOString(),
read: false
};
messages.unshift(newMessage);
if (isBrowser) {
localStorage.setItem(KEYS.MESSAGES, JSON.stringify(messages));
}
return newMessage;
},
markMessageAsRead: (id: string) => {
const messages = dataStore.getMessages();
const updated = messages.map(msg =>
msg.id === id ? { ...msg, read: true } : msg
);
if (isBrowser) {
localStorage.setItem(KEYS.MESSAGES, JSON.stringify(updated));
try {
const res = await fetch(`${API_BASE}/messages`);
if (!res.ok) throw new Error('Failed to fetch');
return await res.json();
} catch (error) {
console.error('Messages fetch error:', error);
return [];
}
},
deleteMessage: (id: string) => {
const messages = dataStore.getMessages();
const filtered = messages.filter(msg => msg.id !== id);
if (isBrowser) {
localStorage.setItem(KEYS.MESSAGES, JSON.stringify(filtered));
addMessage: async (message: Omit<Message, 'id' | 'createdAt' | 'updatedAt'>) => {
try {
const res = await fetch(`${API_BASE}/messages`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify(message),
});
if (!res.ok) throw new Error('Failed to add');
return await res.json();
} catch (error) {
console.error('Message add error:', error);
throw error;
}
},
markMessageAsRead: async (id: string | number) => {
try {
const res = await fetch(`${API_BASE}/messages`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ id: typeof id === 'string' ? parseInt(id) : id, read: true }),
});
if (!res.ok) throw new Error('Failed to update');
return await res.json();
} catch (error) {
console.error('Message update error:', error);
throw error;
}
},
deleteMessage: async (id: string | number) => {
try {
const res = await fetch(`${API_BASE}/messages?id=${id}`, {
method: 'DELETE',
credentials: 'include',
});
if (!res.ok) throw new Error('Failed to delete');
return await res.json();
} catch (error) {
console.error('Message delete error:', error);
throw error;
}
},
// Site Settings
getSiteSettings: (): SiteSettings => {
getSiteSettings: async (): Promise<SiteSettings> => {
if (!isBrowser) {
return {
contact: {
@@ -423,133 +615,185 @@ export const dataStore = {
}
};
}
const stored = localStorage.getItem(KEYS.SITE_SETTINGS);
if (stored) return JSON.parse(stored);
// Default settings
const defaultSettings: SiteSettings = {
contact: {
phone: '+90 (312) 123 45 67',
email: 'info@a2metro.com.tr',
address: 'Ankara Teknokent, Cyberpark C Blok Kat:3 No:301 Çankaya/ANKARA',
kep: 'a2metro@hs03.kep.tr'
},
social: {
facebook: 'https://facebook.com/a2metro',
twitter: 'https://twitter.com/a2metro',
instagram: 'https://instagram.com/a2metro',
youtube: 'https://youtube.com/@a2metro',
linkedin: 'https://linkedin.com/company/a2metro'
},
companyInfo: {
name: 'A2 Metro',
fullName: 'A2 Metro Yapı ve İnşaat A.Ş.',
foundedYear: '2010'
}
};
localStorage.setItem(KEYS.SITE_SETTINGS, JSON.stringify(defaultSettings));
return defaultSettings;
try {
const res = await fetch(`${API_BASE}/settings`);
if (!res.ok) throw new Error('Failed to fetch');
return await res.json();
} catch (error) {
console.error('Settings fetch error:', error);
return {
contact: {
phone: '+90 (312) 123 45 67',
email: 'info@a2metro.com.tr',
address: 'Ankara Teknokent, Cyberpark C Blok Kat:3 No:301 Çankaya/ANKARA',
kep: 'a2metro@hs03.kep.tr'
},
social: {
facebook: 'https://facebook.com/a2metro',
twitter: 'https://twitter.com/a2metro',
instagram: 'https://instagram.com/a2metro',
youtube: 'https://youtube.com/@a2metro',
linkedin: 'https://linkedin.com/company/a2metro'
},
companyInfo: {
name: 'A2 Metro',
fullName: 'A2 Metro Yapı ve İnşaat A.Ş.',
foundedYear: '2010'
}
};
}
},
updateSiteSettings: (settings: Partial<SiteSettings>) => {
const current = dataStore.getSiteSettings();
const updated = {
...current,
...settings,
contact: { ...current.contact, ...(settings.contact || {}) },
social: { ...current.social, ...(settings.social || {}) },
companyInfo: { ...current.companyInfo, ...(settings.companyInfo || {}) }
};
if (isBrowser) {
localStorage.setItem(KEYS.SITE_SETTINGS, JSON.stringify(updated));
updateSiteSettings: async (settings: Partial<SiteSettings>) => {
try {
const current = await dataStore.getSiteSettings();
const updated = {
...current,
...settings,
contact: { ...current.contact, ...(settings.contact || {}) },
social: { ...current.social, ...(settings.social || {}) },
companyInfo: { ...current.companyInfo, ...(settings.companyInfo || {}) }
};
const res = await fetch(`${API_BASE}/settings`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(updated),
});
if (!res.ok) throw new Error('Failed to update');
return await res.json();
} catch (error) {
console.error('Settings update error:', error);
throw error;
}
},
// FAQs
getFAQs: (): FAQ[] => {
getFAQs: async (): Promise<FAQ[]> => {
if (!isBrowser) return defaultFAQData;
const stored = localStorage.getItem(KEYS.FAQS);
return stored ? JSON.parse(stored) : defaultFAQData;
},
setFAQs: (faqs: FAQ[]) => {
if (isBrowser) {
localStorage.setItem(KEYS.FAQS, JSON.stringify(faqs));
try {
const res = await fetch(`${API_BASE}/faqs`);
if (!res.ok) throw new Error('Failed to fetch');
const data = await res.json();
return data.length > 0 ? data : defaultFAQData;
} catch (error) {
console.error('FAQs fetch error:', error);
return defaultFAQData;
}
},
addFAQ: (faq: Omit<FAQ, 'id'>) => {
const faqs = dataStore.getFAQs();
const newFAQ: FAQ = {
...faq,
id: faqs.length > 0 ? Math.max(...faqs.map(f => f.id)) + 1 : 1
};
dataStore.setFAQs([...faqs, newFAQ]);
return newFAQ;
setFAQs: async () => {
console.warn('setFAQs deprecated - use addFAQ/updateFAQ/deleteFAQ');
},
updateFAQ: (id: number, updates: Partial<FAQ>) => {
const faqs = dataStore.getFAQs();
const updated = faqs.map(faq =>
faq.id === id ? { ...faq, ...updates } : faq
);
dataStore.setFAQs(updated);
addFAQ: async (faq: Omit<FAQ, 'id'>) => {
try {
const res = await fetch(`${API_BASE}/faqs`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(faq),
});
if (!res.ok) throw new Error('Failed to add');
return await res.json();
} catch (error) {
console.error('FAQ add error:', error);
throw error;
}
},
deleteFAQ: (id: number) => {
const faqs = dataStore.getFAQs();
dataStore.setFAQs(faqs.filter(faq => faq.id !== id));
updateFAQ: async (id: number, updates: Partial<FAQ>) => {
try {
const res = await fetch(`${API_BASE}/faqs`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ id, ...updates }),
});
if (!res.ok) throw new Error('Failed to update');
return await res.json();
} catch (error) {
console.error('FAQ update error:', error);
throw error;
}
},
deleteFAQ: async (id: number) => {
try {
const res = await fetch(`${API_BASE}/faqs?id=${id}`, {
method: 'DELETE',
credentials: 'include',
});
if (!res.ok) throw new Error('Failed to delete');
return await res.json();
} catch (error) {
console.error('FAQ delete error:', error);
throw error;
}
},
// Cameras
getCameras: (): Camera[] => {
getCameras: async (): Promise<Camera[]> => {
if (!isBrowser) return defaultCameraData;
const stored = localStorage.getItem(KEYS.CAMERAS);
return stored ? JSON.parse(stored) : defaultCameraData;
},
setCameras: (cameras: Camera[]) => {
if (isBrowser) {
localStorage.setItem(KEYS.CAMERAS, JSON.stringify(cameras));
try {
const res = await fetch(`${API_BASE}/cameras`);
if (!res.ok) throw new Error('Failed to fetch');
const data = await res.json();
return data.length > 0 ? data : defaultCameraData;
} catch (error) {
console.error('Cameras fetch error:', error);
return defaultCameraData;
}
},
addCamera: (camera: Omit<Camera, 'id'>) => {
const cameras = dataStore.getCameras();
const newCamera: Camera = {
...camera,
id: cameras.length > 0 ? Math.max(...cameras.map(c => c.id)) + 1 : 1
};
dataStore.setCameras([...cameras, newCamera]);
return newCamera;
setCameras: async () => {
console.warn('setCameras deprecated - use addCamera/updateCamera/deleteCamera');
},
updateCamera: (id: number, updates: Partial<Camera>) => {
const cameras = dataStore.getCameras();
const updated = cameras.map(camera =>
camera.id === id ? { ...camera, ...updates } : camera
);
dataStore.setCameras(updated);
addCamera: async (camera: Omit<Camera, 'id' | 'createdAt' | 'updatedAt'>) => {
try {
const res = await fetch(`${API_BASE}/cameras`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify(camera),
});
if (!res.ok) throw new Error('Failed to add');
return await res.json();
} catch (error) {
console.error('Camera add error:', error);
throw error;
}
},
deleteCamera: (id: number) => {
const cameras = dataStore.getCameras();
dataStore.setCameras(cameras.filter(camera => camera.id !== id));
updateCamera: async (id: number, updates: Partial<Camera>) => {
try {
const res = await fetch(`${API_BASE}/cameras`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ id, ...updates }),
});
if (!res.ok) throw new Error('Failed to update');
return await res.json();
} catch (error) {
console.error('Camera update error:', error);
throw error;
}
},
// Reset all data
deleteCamera: async (id: number) => {
try {
const res = await fetch(`${API_BASE}/cameras?id=${id}`, {
method: 'DELETE',
credentials: 'include',
});
if (!res.ok) throw new Error('Failed to delete');
return await res.json();
} catch (error) {
console.error('Camera delete error:', error);
throw error;
}
},
// Reset all data (Not applicable for API-based storage)
resetAll: () => {
if (isBrowser) {
localStorage.removeItem(KEYS.SLIDER);
localStorage.removeItem(KEYS.NEWS);
localStorage.removeItem(KEYS.MEDIA);
localStorage.removeItem(KEYS.DOCUMENTS);
localStorage.removeItem(KEYS.METRO_STATIONS);
localStorage.removeItem(KEYS.LIVE_STREAM);
localStorage.removeItem(KEYS.SITE_SETTINGS);
localStorage.removeItem(KEYS.MESSAGES);
localStorage.removeItem(KEYS.FAQS);
localStorage.removeItem(KEYS.CAMERAS);
}
console.warn('resetAll not applicable for API-based storage');
}
};

9
lib/prisma.ts Normal file
View File

@@ -0,0 +1,9 @@
import { PrismaClient } from '@prisma/client';
const globalForPrisma = globalThis as unknown as {
prisma: PrismaClient | undefined;
};
export const prisma = globalForPrisma.prisma ?? new PrismaClient();
if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma;