İlk sürüm: hDiyanetProxy v1.0.0

- Backend: Node.js + Express + MySQL + JWT auth
- 8 MySQL tablosu (users, countries, states, cities, prayer_times, ramadan_times, eid_times, fetch_logs)
- Diyanet API entegrasyonu (auth + token yönetimi)
- Tüm API endpointleri (places, prayer-times, ramadan, eid, admin)
- Rate limiting, CORS, input validation
- Cron job (gece 02:00 otomatik veri çekme)
- Frontend: Login, Dashboard, Fetch Panel, Namaz Vakitleri, Ramazan, Admin, Profil
- Admin kullanıcı: admin/admin123
This commit is contained in:
hOLOlu
2026-02-27 07:53:41 +03:00
commit a798066049
44 changed files with 6092 additions and 0 deletions

1905
backend/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

24
backend/package.json Normal file
View File

@@ -0,0 +1,24 @@
{
"name": "hdiyanetproxy-backend",
"version": "1.0.0",
"description": "hDiyanetProxy Backend API",
"main": "src/app.js",
"scripts": {
"start": "node src/app.js",
"dev": "node --watch src/app.js",
"migrate": "node src/utils/migrate.js",
"seed": "node src/utils/seed.js"
},
"dependencies": {
"mysql2": "^3.18.2",
"express": "^4.21.0",
"jsonwebtoken": "^9.0.2",
"bcrypt": "^5.1.1",
"cors": "^2.8.5",
"express-rate-limit": "^7.4.0",
"express-validator": "^7.2.0",
"node-cron": "^3.0.3",
"axios": "^1.7.7",
"dotenv": "^16.4.5"
}
}

98
backend/src/app.js Normal file
View File

@@ -0,0 +1,98 @@
// hDiyanetProxy - Ana Uygulama
const express = require('express');
const cors = require('cors');
const rateLimit = require('express-rate-limit');
const path = require('path');
require('dotenv').config();
const app = express();
const PORT = process.env.PORT || 3000;
// --- Middleware ---
// CORS ayarları
app.use(cors({
origin: '*',
methods: ['GET', 'POST', 'PATCH', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization']
}));
// Body parser
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// Rate limiting - dakikada 120 istek
const limiter = rateLimit({
windowMs: 1 * 60 * 1000, // 1 dakika
max: 120,
message: { error: 'Çok fazla istek gönderdiniz, lütfen bir süre bekleyin' },
standardHeaders: true,
legacyHeaders: false
});
app.use('/api/', limiter);
// Auth endpointleri için daha sıkı rate limiting
const authLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 dakika
max: 20,
message: { error: 'Çok fazla giriş denemesi, 15 dakika bekleyin' }
});
app.use('/api/v1/auth/login', authLimiter);
// --- Frontend Statik Dosyalar ---
app.use(express.static(path.join(__dirname, '../../frontend/src')));
// --- API Routes ---
app.use('/api/v1/auth', require('./routes/auth'));
app.use('/api/v1/places', require('./routes/places'));
app.use('/api/v1/prayer-times', require('./routes/prayerTimes'));
app.use('/api/v1/ramadan', require('./routes/ramadan'));
app.use('/api/v1/eid', require('./routes/eid'));
app.use('/api/v1/admin', require('./routes/admin'));
// --- API Durum Endpoint'i ---
app.get('/api/v1/status', (req, res) => {
res.json({
name: 'hDiyanetProxy',
version: '1.0.0',
status: 'running',
timestamp: new Date().toISOString()
});
});
// --- Frontend SPA Route'ları ---
app.get('/', (req, res) => res.sendFile(path.join(__dirname, '../../frontend/src/pages/login.html')));
app.get('/login', (req, res) => res.sendFile(path.join(__dirname, '../../frontend/src/pages/login.html')));
app.get('/dashboard', (req, res) => res.sendFile(path.join(__dirname, '../../frontend/src/pages/dashboard.html')));
app.get('/fetch', (req, res) => res.sendFile(path.join(__dirname, '../../frontend/src/pages/fetch.html')));
app.get('/prayer-times', (req, res) => res.sendFile(path.join(__dirname, '../../frontend/src/pages/prayer-times.html')));
app.get('/ramadan', (req, res) => res.sendFile(path.join(__dirname, '../../frontend/src/pages/ramadan.html')));
app.get('/admin/users', (req, res) => res.sendFile(path.join(__dirname, '../../frontend/src/pages/admin-users.html')));
app.get('/profile', (req, res) => res.sendFile(path.join(__dirname, '../../frontend/src/pages/profile.html')));
// --- 404 Handler ---
app.use((req, res) => {
if (req.path.startsWith('/api/')) {
return res.status(404).json({ error: 'Endpoint bulunamadı' });
}
res.status(404).send('Sayfa bulunamadı');
});
// --- Error Handler ---
app.use((err, req, res, next) => {
console.error('Sunucu hatası:', err);
res.status(500).json({ error: 'Sunucu hatası' });
});
// --- Sunucuyu Başlat ---
app.listen(PORT, () => {
console.log(`\n🚀 hDiyanetProxy sunucusu çalışıyor: http://localhost:${PORT}`);
console.log(`📡 API: http://localhost:${PORT}/api/v1`);
console.log(`🖥️ Frontend: http://localhost:${PORT}/\n`);
// Cron job'ları başlat
const { startCronJobs } = require('./jobs/cronJobs');
startCronJobs();
});
module.exports = app;

View File

@@ -0,0 +1,178 @@
// hDiyanetProxy - Admin Controller
const FetchService = require('../services/fetchService');
const FetchLogModel = require('../models/FetchLog');
const UserModel = require('../models/User');
const PlaceModel = require('../models/Place');
const DiyanetService = require('../services/diyanetService');
const { validationResult } = require('express-validator');
const AdminController = {
// Dashboard istatistikleri
async getDashboard(req, res) {
try {
const stats = await PlaceModel.getStats();
const recentLogs = await FetchLogModel.getRecent(10);
res.json({ stats, recentLogs });
} catch (err) {
console.error('Dashboard hatası:', err);
res.status(500).json({ error: 'Sunucu hatası' });
}
},
// Yer bilgilerini çek
async fetchPlaces(req, res) {
try {
const result = await FetchService.fetchAndSavePlaces(req.user.id);
res.json(result);
} catch (err) {
console.error('Fetch places hatası:', err);
res.status(500).json({ error: `Yer bilgileri çekilirken hata: ${err.message}` });
}
},
// Yıllık namaz vakitlerini çek
async fetchYearly(req, res) {
try {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const { cityId, startDate, endDate } = req.body;
const result = await FetchService.fetchAndSaveYearly(cityId, startDate, endDate, req.user.id);
res.json(result);
} catch (err) {
console.error('Fetch yearly hatası:', err);
res.status(500).json({ error: `Namaz vakitleri çekilirken hata: ${err.message}` });
}
},
// Ramazan vakitlerini çek
async fetchRamadan(req, res) {
try {
const { cityId } = req.body;
if (!cityId) {
return res.status(400).json({ error: 'cityId gerekli' });
}
const result = await FetchService.fetchAndSaveRamadan(cityId, req.user.id);
res.json(result);
} catch (err) {
console.error('Fetch ramadan hatası:', err);
res.status(500).json({ error: `Ramazan vakitleri çekilirken hata: ${err.message}` });
}
},
// Bayram vakitlerini çek
async fetchEid(req, res) {
try {
const { cityId } = req.body;
if (!cityId) {
return res.status(400).json({ error: 'cityId gerekli' });
}
const result = await FetchService.fetchAndSaveEid(cityId, req.user.id);
res.json(result);
} catch (err) {
console.error('Fetch eid hatası:', err);
res.status(500).json({ error: `Bayram vakitleri çekilirken hata: ${err.message}` });
}
},
// Cache temizle
async clearCache(req, res) {
try {
const result = await DiyanetService.clearCache();
await FetchLogModel.create(req.user.id, 'clear_cache', null, null, 'Diyanet API cache temizlendi', 'success', 0);
res.json({ success: true, message: 'Cache temizlendi', data: result });
} catch (err) {
console.error('Clear cache hatası:', err);
res.status(500).json({ error: `Cache temizlenirken hata: ${err.message}` });
}
},
// Logları getir
async getLogs(req, res) {
try {
const page = parseInt(req.query.page) || 1;
const limit = parseInt(req.query.limit) || 50;
const result = await FetchLogModel.getAll(page, limit);
res.json(result);
} catch (err) {
console.error('Logs hatası:', err);
res.status(500).json({ error: 'Sunucu hatası' });
}
},
// --- Kullanıcı Yönetimi ---
// Tüm kullanıcıları listele
async getUsers(req, res) {
try {
const users = await UserModel.findAll();
res.json(users);
} catch (err) {
console.error('Users hatası:', err);
res.status(500).json({ error: 'Sunucu hatası' });
}
},
// Kullanıcı oluştur
async createUser(req, res) {
try {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const { username, password, role } = req.body;
const existing = await UserModel.findByUsername(username);
if (existing) {
return res.status(409).json({ error: 'Bu kullanıcı adı zaten kullanılıyor' });
}
const user = await UserModel.create(username, password, role || 'user');
res.status(201).json({ message: 'Kullanıcı oluşturuldu', user });
} catch (err) {
console.error('Create user hatası:', err);
res.status(500).json({ error: 'Sunucu hatası' });
}
},
// Kullanıcı aktif/pasif yap
async toggleUserActive(req, res) {
try {
const { id } = req.params;
const { isActive } = req.body;
// Kendini pasif yapamasın
if (parseInt(id) === req.user.id) {
return res.status(400).json({ error: 'Kendinizi pasif yapamazsınız' });
}
await UserModel.toggleActive(parseInt(id), isActive);
res.json({ message: isActive ? 'Kullanıcı aktif edildi' : 'Kullanıcı pasif edildi' });
} catch (err) {
console.error('Toggle user hatası:', err);
res.status(500).json({ error: 'Sunucu hatası' });
}
},
// Kullanıcı sil
async deleteUser(req, res) {
try {
const { id } = req.params;
if (parseInt(id) === req.user.id) {
return res.status(400).json({ error: 'Kendinizi silemezsiniz' });
}
await UserModel.delete(parseInt(id));
res.json({ message: 'Kullanıcı silindi' });
} catch (err) {
console.error('Delete user hatası:', err);
res.status(500).json({ error: 'Sunucu hatası' });
}
}
};
module.exports = AdminController;

View File

@@ -0,0 +1,136 @@
// hDiyanetProxy - Auth Controller
const jwt = require('jsonwebtoken');
const UserModel = require('../models/User');
const { validationResult } = require('express-validator');
const AuthController = {
// Kullanıcı girişi
async login(req, res) {
try {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const { username, password } = req.body;
// Kullanıcıyı bul
const user = await UserModel.findByUsername(username);
if (!user) {
return res.status(401).json({ error: 'Kullanıcı adı veya şifre hatalı' });
}
// Aktif mi kontrol et
if (!user.is_active) {
return res.status(403).json({ error: 'Hesabınız pasif durumda' });
}
// Şifreyi doğrula
const isValid = await UserModel.verifyPassword(password, user.password);
if (!isValid) {
return res.status(401).json({ error: 'Kullanıcı adı veya şifre hatalı' });
}
// JWT token oluştur
const token = jwt.sign(
{ id: user.id, username: user.username, role: user.role },
process.env.JWT_SECRET,
{ expiresIn: process.env.JWT_EXPIRES_IN || '24h' }
);
res.json({
message: 'Giriş başarılı',
token,
user: {
id: user.id,
username: user.username,
role: user.role
}
});
} catch (err) {
console.error('Login hatası:', err);
res.status(500).json({ error: 'Sunucu hatası' });
}
},
// Kullanıcı kaydı
async register(req, res) {
try {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const { username, password } = req.body;
// Kullanıcı adı zaten var mı kontrol et
const existing = await UserModel.findByUsername(username);
if (existing) {
return res.status(409).json({ error: 'Bu kullanıcı adı zaten kullanılıyor' });
}
// Yeni kullanıcı oluştur
const user = await UserModel.create(username, password, 'user');
res.status(201).json({
message: 'Kullanıcı başarıyla oluşturuldu',
user: { id: user.id, username: user.username, role: user.role }
});
} catch (err) {
console.error('Register hatası:', err);
res.status(500).json({ error: 'Sunucu hatası' });
}
},
// Profil bilgisi
async profile(req, res) {
try {
const user = await UserModel.findById(req.user.id);
if (!user) {
return res.status(404).json({ error: 'Kullanıcı bulunamadı' });
}
res.json({ user });
} catch (err) {
res.status(500).json({ error: 'Sunucu hatası' });
}
},
// Yeni token oluştur
async refreshToken(req, res) {
try {
const token = jwt.sign(
{ id: req.user.id, username: req.user.username, role: req.user.role },
process.env.JWT_SECRET,
{ expiresIn: process.env.JWT_EXPIRES_IN || '24h' }
);
res.json({ token });
} catch (err) {
res.status(500).json({ error: 'Sunucu hatası' });
}
},
// Şifre değiştir
async changePassword(req, res) {
try {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const { currentPassword, newPassword } = req.body;
const user = await UserModel.findByUsername(req.user.username);
const isValid = await UserModel.verifyPassword(currentPassword, user.password);
if (!isValid) {
return res.status(401).json({ error: 'Mevcut şifre hatalı' });
}
await UserModel.updatePassword(req.user.id, newPassword);
res.json({ message: 'Şifre başarıyla güncellendi' });
} catch (err) {
res.status(500).json({ error: 'Sunucu hatası' });
}
}
};
module.exports = AuthController;

View File

@@ -0,0 +1,21 @@
// hDiyanetProxy - Eid Controller
const EidModel = require('../models/EidTime');
const EidController = {
// Şehre göre bayram vakitlerini getir
async getByCityId(req, res) {
try {
const { cityId } = req.query;
if (!cityId) {
return res.status(400).json({ error: 'cityId parametresi gerekli' });
}
const times = await EidModel.getByCityId(parseInt(cityId));
res.json(times);
} catch (err) {
console.error('Eid hatası:', err);
res.status(500).json({ error: 'Sunucu hatası' });
}
}
};
module.exports = EidController;

View File

@@ -0,0 +1,62 @@
// hDiyanetProxy - Places Controller
const PlaceModel = require('../models/Place');
const PlacesController = {
// Tüm ülkeleri getir
async getCountries(req, res) {
try {
const countries = await PlaceModel.getAllCountries();
res.json(countries);
} catch (err) {
console.error('Countries hatası:', err);
res.status(500).json({ error: 'Sunucu hatası' });
}
},
// Ülkeye göre eyaletleri getir
async getStates(req, res) {
try {
const { countryId } = req.query;
if (!countryId) {
return res.status(400).json({ error: 'countryId parametresi gerekli' });
}
const states = await PlaceModel.getStatesByCountry(parseInt(countryId));
res.json(states);
} catch (err) {
console.error('States hatası:', err);
res.status(500).json({ error: 'Sunucu hatası' });
}
},
// Eyalete göre şehirleri getir
async getCities(req, res) {
try {
const { stateId } = req.query;
if (!stateId) {
return res.status(400).json({ error: 'stateId parametresi gerekli' });
}
const cities = await PlaceModel.getCitiesByState(parseInt(stateId));
res.json(cities);
} catch (err) {
console.error('Cities hatası:', err);
res.status(500).json({ error: 'Sunucu hatası' });
}
},
// Şehir detayı
async getCityDetail(req, res) {
try {
const { cityId } = req.params;
const city = await PlaceModel.getCityDetail(parseInt(cityId));
if (!city) {
return res.status(404).json({ error: 'Şehir bulunamadı' });
}
res.json(city);
} catch (err) {
console.error('City detail hatası:', err);
res.status(500).json({ error: 'Sunucu hatası' });
}
}
};
module.exports = PlacesController;

View File

@@ -0,0 +1,46 @@
// hDiyanetProxy - Prayer Times Controller
const PrayerTimeModel = require('../models/PrayerTime');
const { validationResult } = require('express-validator');
const PrayerTimesController = {
// Tarih aralığına göre namaz vakitlerini getir
async getByDateRange(req, res) {
try {
const { cityId, startDate, endDate } = req.query;
if (!cityId) {
return res.status(400).json({ error: 'cityId parametresi gerekli' });
}
const start = startDate || new Date().toISOString().split('T')[0];
const end = endDate || start;
const times = await PrayerTimeModel.getByDateRange(parseInt(cityId), start, end);
res.json(times);
} catch (err) {
console.error('Prayer times hatası:', err);
res.status(500).json({ error: 'Sunucu hatası' });
}
},
// Bugünkü namaz vaktini getir
async getToday(req, res) {
try {
const { cityId } = req.query;
if (!cityId) {
return res.status(400).json({ error: 'cityId parametresi gerekli' });
}
const today = await PrayerTimeModel.getToday(parseInt(cityId));
if (!today) {
return res.status(404).json({ error: 'Bugün için namaz vakti bulunamadı' });
}
res.json(today);
} catch (err) {
console.error('Prayer times today hatası:', err);
res.status(500).json({ error: 'Sunucu hatası' });
}
}
};
module.exports = PrayerTimesController;

View File

@@ -0,0 +1,39 @@
// hDiyanetProxy - Ramadan Controller
const RamadanModel = require('../models/RamadanTime');
const RamadanController = {
// Şehre göre ramazan vakitlerini getir
async getByCityId(req, res) {
try {
const { cityId } = req.query;
if (!cityId) {
return res.status(400).json({ error: 'cityId parametresi gerekli' });
}
const times = await RamadanModel.getByCityId(parseInt(cityId));
res.json(times);
} catch (err) {
console.error('Ramadan hatası:', err);
res.status(500).json({ error: 'Sunucu hatası' });
}
},
// Bugünkü ramazan vaktini getir
async getToday(req, res) {
try {
const { cityId } = req.query;
if (!cityId) {
return res.status(400).json({ error: 'cityId parametresi gerekli' });
}
const today = await RamadanModel.getToday(parseInt(cityId));
if (!today) {
return res.status(404).json({ error: 'Bugün için ramazan vakti bulunamadı' });
}
res.json(today);
} catch (err) {
console.error('Ramadan today hatası:', err);
res.status(500).json({ error: 'Sunucu hatası' });
}
}
};
module.exports = RamadanController;

View File

@@ -0,0 +1,47 @@
// hDiyanetProxy - Cron Jobs
// Her gece 02:00'de otomatik veri çekme
const cron = require('node-cron');
const FetchService = require('../services/fetchService');
const pool = require('../utils/db');
function startCronJobs() {
// Her gece saat 02:00'de çalışır (Türkiye saati)
cron.schedule('0 2 * * *', async () => {
console.log('\n⏰ [CRON] Otomatik veri çekme başladı:', new Date().toISOString());
try {
// Veritabanındaki tüm şehirler için günlük namaz vakitlerini güncelle
const [cities] = await pool.execute('SELECT DISTINCT city_id FROM prayer_times LIMIT 50');
if (cities.length === 0) {
console.log(' Güncellenecek şehir bulunamadı');
return;
}
const currentYear = new Date().getFullYear();
const startDate = `${currentYear}-01-01T00:00:00.0Z`;
const endDate = `${currentYear}-12-31T23:59:59.0Z`;
for (const city of cities) {
try {
await FetchService.fetchAndSaveYearly(city.city_id, startDate, endDate, null);
console.log(` ✅ Şehir ${city.city_id} güncellendi`);
} catch (err) {
console.error(` ❌ Şehir ${city.city_id} güncellenemedi: ${err.message}`);
}
// API'yi yormamak için bekleme
await new Promise(r => setTimeout(r, 500));
}
console.log('⏰ [CRON] Otomatik veri çekme tamamlandı\n');
} catch (err) {
console.error('⏰ [CRON] Hata:', err.message);
}
}, {
timezone: 'Europe/Istanbul'
});
console.log('⏰ Cron job ayarlandı: Her gece 02:00 (Europe/Istanbul)');
}
module.exports = { startCronJobs };

View File

@@ -0,0 +1,31 @@
// hDiyanetProxy - JWT Auth Middleware
const jwt = require('jsonwebtoken');
// Token doğrulama middleware'i
function authMiddleware(req, res, next) {
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return res.status(401).json({ error: 'Yetkilendirme token\'ı gerekli' });
}
const token = authHeader.split(' ')[1];
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = decoded;
next();
} catch (err) {
return res.status(401).json({ error: 'Geçersiz veya süresi dolmuş token' });
}
}
// Admin rolü kontrolü middleware'i
function adminMiddleware(req, res, next) {
if (req.user.role !== 'admin') {
return res.status(403).json({ error: 'Bu işlem için admin yetkisi gerekli' });
}
next();
}
module.exports = { authMiddleware, adminMiddleware };

View File

@@ -0,0 +1,51 @@
// hDiyanetProxy - Bayram Vakitleri Modeli
const pool = require('../utils/db');
const EidModel = {
// Bayram vakti ekle veya güncelle
async upsert(cityId, eidName, date, times) {
await pool.execute(
`INSERT INTO eid_times (city_id, eid_name, date, prayer_time, fajr, sunrise, dhuhr, asr, maghrib, isha)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
ON DUPLICATE KEY UPDATE
prayer_time = VALUES(prayer_time), fajr = VALUES(fajr), sunrise = VALUES(sunrise),
dhuhr = VALUES(dhuhr), asr = VALUES(asr), maghrib = VALUES(maghrib), isha = VALUES(isha)`,
[cityId, eidName, date, times.prayerTime, times.fajr, times.sunrise, times.dhuhr, times.asr, times.maghrib, times.isha]
);
},
// Şehre göre bayram vakitlerini getir
async getByCityId(cityId) {
const [rows] = await pool.execute(
'SELECT * FROM eid_times WHERE city_id = ? ORDER BY date',
[cityId]
);
return rows;
},
// Toplu ekleme
async bulkUpsert(cityId, items) {
const conn = await pool.getConnection();
try {
await conn.beginTransaction();
for (const item of items) {
await conn.execute(
`INSERT INTO eid_times (city_id, eid_name, date, prayer_time, fajr, sunrise, dhuhr, asr, maghrib, isha)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
ON DUPLICATE KEY UPDATE
prayer_time = VALUES(prayer_time), fajr = VALUES(fajr), sunrise = VALUES(sunrise),
dhuhr = VALUES(dhuhr), asr = VALUES(asr), maghrib = VALUES(maghrib), isha = VALUES(isha)`,
[cityId, item.eidName, item.date, item.prayerTime, item.fajr, item.sunrise, item.dhuhr, item.asr, item.maghrib, item.isha]
);
}
await conn.commit();
} catch (err) {
await conn.rollback();
throw err;
} finally {
conn.release();
}
}
};
module.exports = EidModel;

View File

@@ -0,0 +1,43 @@
// hDiyanetProxy - Fetch Log Modeli
const pool = require('../utils/db');
const FetchLogModel = {
// Log kaydı oluştur
async create(userId, action, cityId, cityName, details, status = 'success', recordsCount = 0) {
const [result] = await pool.execute(
'INSERT INTO fetch_logs (user_id, action, city_id, city_name, details, status, records_count) VALUES (?, ?, ?, ?, ?, ?, ?)',
[userId, action, cityId, cityName, details, status, recordsCount]
);
return result.insertId;
},
// Son logları getir
async getRecent(limit = 50) {
const [rows] = await pool.execute(
`SELECT fl.*, u.username
FROM fetch_logs fl
LEFT JOIN users u ON fl.user_id = u.id
ORDER BY fl.created_at DESC
LIMIT ?`,
[limit]
);
return rows;
},
// Tüm logları getir (sayfalı)
async getAll(page = 1, limit = 50) {
const offset = (page - 1) * limit;
const [rows] = await pool.execute(
`SELECT fl.*, u.username
FROM fetch_logs fl
LEFT JOIN users u ON fl.user_id = u.id
ORDER BY fl.created_at DESC
LIMIT ? OFFSET ?`,
[limit, offset]
);
const [[{ total }]] = await pool.execute('SELECT COUNT(*) as total FROM fetch_logs');
return { logs: rows, total, page, limit };
}
};
module.exports = FetchLogModel;

View File

@@ -0,0 +1,80 @@
// hDiyanetProxy - Yer Bilgisi Modeli
const pool = require('../utils/db');
const PlaceModel = {
// --- Ülkeler ---
async upsertCountry(id, name) {
await pool.execute(
'INSERT INTO countries (id, name) VALUES (?, ?) ON DUPLICATE KEY UPDATE name = VALUES(name)',
[id, name]
);
},
async getAllCountries() {
const [rows] = await pool.execute('SELECT * FROM countries ORDER BY name');
return rows;
},
// --- Eyaletler / İller ---
async upsertState(id, name, countryId) {
await pool.execute(
'INSERT INTO states (id, name, country_id) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE name = VALUES(name)',
[id, name, countryId]
);
},
async getStatesByCountry(countryId) {
const [rows] = await pool.execute('SELECT * FROM states WHERE country_id = ? ORDER BY name', [countryId]);
return rows;
},
async getAllStates() {
const [rows] = await pool.execute('SELECT * FROM states ORDER BY name');
return rows;
},
// --- Şehirler / İlçeler ---
async upsertCity(id, name, stateId, lat = null, lng = null) {
await pool.execute(
'INSERT INTO cities (id, name, state_id, latitude, longitude) VALUES (?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE name = VALUES(name), latitude = VALUES(latitude), longitude = VALUES(longitude)',
[id, name, stateId, lat, lng]
);
},
async getCitiesByState(stateId) {
const [rows] = await pool.execute('SELECT * FROM cities WHERE state_id = ? ORDER BY name', [stateId]);
return rows;
},
async getCityDetail(cityId) {
const [rows] = await pool.execute(
`SELECT c.*, s.name as state_name, co.name as country_name
FROM cities c
JOIN states s ON c.state_id = s.id
JOIN countries co ON s.country_id = co.id
WHERE c.id = ?`,
[cityId]
);
return rows[0] || null;
},
// İstatistikler
async getStats() {
const [[countries]] = await pool.execute('SELECT COUNT(*) as count FROM countries');
const [[states]] = await pool.execute('SELECT COUNT(*) as count FROM states');
const [[cities]] = await pool.execute('SELECT COUNT(*) as count FROM cities');
const [[prayerTimes]] = await pool.execute('SELECT COUNT(*) as count FROM prayer_times');
const [[ramadanTimes]] = await pool.execute('SELECT COUNT(*) as count FROM ramadan_times');
const [[eidTimes]] = await pool.execute('SELECT COUNT(*) as count FROM eid_times');
return {
countries: countries.count,
states: states.count,
cities: cities.count,
prayerTimes: prayerTimes.count,
ramadanTimes: ramadanTimes.count,
eidTimes: eidTimes.count
};
}
};
module.exports = PlaceModel;

View File

@@ -0,0 +1,61 @@
// hDiyanetProxy - Namaz Vakitleri Modeli
const pool = require('../utils/db');
const PrayerTimeModel = {
// Namaz vakti ekle veya güncelle (upsert)
async upsert(cityId, date, times) {
await pool.execute(
`INSERT INTO prayer_times (city_id, date, fajr, sunrise, dhuhr, asr, maghrib, isha, qibla)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
ON DUPLICATE KEY UPDATE
fajr = VALUES(fajr), sunrise = VALUES(sunrise), dhuhr = VALUES(dhuhr),
asr = VALUES(asr), maghrib = VALUES(maghrib), isha = VALUES(isha), qibla = VALUES(qibla)`,
[cityId, date, times.fajr, times.sunrise, times.dhuhr, times.asr, times.maghrib, times.isha, times.qibla || null]
);
},
// Tarih aralığına göre namaz vakitlerini getir
async getByDateRange(cityId, startDate, endDate) {
const [rows] = await pool.execute(
`SELECT * FROM prayer_times WHERE city_id = ? AND date BETWEEN ? AND ? ORDER BY date`,
[cityId, startDate, endDate]
);
return rows;
},
// Bugünkü namaz vaktini getir
async getToday(cityId) {
const today = new Date().toISOString().split('T')[0];
const [rows] = await pool.execute(
'SELECT * FROM prayer_times WHERE city_id = ? AND date = ?',
[cityId, today]
);
return rows[0] || null;
},
// Toplu ekleme (batch upsert)
async bulkUpsert(cityId, timesArray) {
const conn = await pool.getConnection();
try {
await conn.beginTransaction();
for (const item of timesArray) {
await conn.execute(
`INSERT INTO prayer_times (city_id, date, fajr, sunrise, dhuhr, asr, maghrib, isha, qibla)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
ON DUPLICATE KEY UPDATE
fajr = VALUES(fajr), sunrise = VALUES(sunrise), dhuhr = VALUES(dhuhr),
asr = VALUES(asr), maghrib = VALUES(maghrib), isha = VALUES(isha), qibla = VALUES(qibla)`,
[cityId, item.date, item.fajr, item.sunrise, item.dhuhr, item.asr, item.maghrib, item.isha, item.qibla || null]
);
}
await conn.commit();
} catch (err) {
await conn.rollback();
throw err;
} finally {
conn.release();
}
}
};
module.exports = PrayerTimeModel;

View File

@@ -0,0 +1,61 @@
// hDiyanetProxy - Ramazan Vakitleri Modeli
const pool = require('../utils/db');
const RamadanModel = {
// Ramazan vakti ekle veya güncelle
async upsert(cityId, date, times) {
await pool.execute(
`INSERT INTO ramadan_times (city_id, date, fajr, sunrise, dhuhr, asr, maghrib, isha)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
ON DUPLICATE KEY UPDATE
fajr = VALUES(fajr), sunrise = VALUES(sunrise), dhuhr = VALUES(dhuhr),
asr = VALUES(asr), maghrib = VALUES(maghrib), isha = VALUES(isha)`,
[cityId, date, times.fajr, times.sunrise, times.dhuhr, times.asr, times.maghrib, times.isha]
);
},
// Şehre göre ramazan vakitlerini getir
async getByCityId(cityId) {
const [rows] = await pool.execute(
'SELECT * FROM ramadan_times WHERE city_id = ? ORDER BY date',
[cityId]
);
return rows;
},
// Bugünkü ramazan vaktini getir
async getToday(cityId) {
const today = new Date().toISOString().split('T')[0];
const [rows] = await pool.execute(
'SELECT * FROM ramadan_times WHERE city_id = ? AND date = ?',
[cityId, today]
);
return rows[0] || null;
},
// Toplu ekleme
async bulkUpsert(cityId, timesArray) {
const conn = await pool.getConnection();
try {
await conn.beginTransaction();
for (const item of timesArray) {
await conn.execute(
`INSERT INTO ramadan_times (city_id, date, fajr, sunrise, dhuhr, asr, maghrib, isha)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
ON DUPLICATE KEY UPDATE
fajr = VALUES(fajr), sunrise = VALUES(sunrise), dhuhr = VALUES(dhuhr),
asr = VALUES(asr), maghrib = VALUES(maghrib), isha = VALUES(isha)`,
[cityId, item.date, item.fajr, item.sunrise, item.dhuhr, item.asr, item.maghrib, item.isha]
);
}
await conn.commit();
} catch (err) {
await conn.rollback();
throw err;
} finally {
conn.release();
}
}
};
module.exports = RamadanModel;

View File

@@ -0,0 +1,56 @@
// hDiyanetProxy - Kullanıcı Modeli
const pool = require('../utils/db');
const bcrypt = require('bcrypt');
const UserModel = {
// Kullanıcı adına göre bul
async findByUsername(username) {
const [rows] = await pool.execute('SELECT * FROM users WHERE username = ?', [username]);
return rows[0] || null;
},
// ID'ye göre bul
async findById(id) {
const [rows] = await pool.execute('SELECT id, username, role, is_active, created_at FROM users WHERE id = ?', [id]);
return rows[0] || null;
},
// Yeni kullanıcı oluştur
async create(username, password, role = 'user') {
const hashedPassword = await bcrypt.hash(password, 10);
const [result] = await pool.execute(
'INSERT INTO users (username, password, role) VALUES (?, ?, ?)',
[username, hashedPassword, role]
);
return { id: result.insertId, username, role };
},
// Şifre doğrula
async verifyPassword(plainPassword, hashedPassword) {
return bcrypt.compare(plainPassword, hashedPassword);
},
// Tüm kullanıcıları listele
async findAll() {
const [rows] = await pool.execute('SELECT id, username, role, is_active, created_at, updated_at FROM users ORDER BY id');
return rows;
},
// Kullanıcıyı aktif/pasif yap
async toggleActive(id, isActive) {
await pool.execute('UPDATE users SET is_active = ? WHERE id = ?', [isActive ? 1 : 0, id]);
},
// Kullanıcı sil
async delete(id) {
await pool.execute('DELETE FROM users WHERE id = ?', [id]);
},
// Şifre güncelle
async updatePassword(id, newPassword) {
const hashedPassword = await bcrypt.hash(newPassword, 10);
await pool.execute('UPDATE users SET password = ? WHERE id = ?', [hashedPassword, id]);
}
};
module.exports = UserModel;

View File

@@ -0,0 +1,55 @@
// hDiyanetProxy - Admin Routes
const express = require('express');
const router = express.Router();
const { body } = require('express-validator');
const AdminController = require('../controllers/adminController');
const { authMiddleware, adminMiddleware } = require('../middleware/auth');
// Tüm admin endpointleri auth + admin yetkisi gerektirir
router.use(authMiddleware);
router.use(adminMiddleware);
// GET /api/v1/admin/dashboard
router.get('/dashboard', AdminController.getDashboard);
// POST /api/v1/admin/fetch/places
router.post('/fetch/places', AdminController.fetchPlaces);
// POST /api/v1/admin/fetch/yearly
router.post('/fetch/yearly', [
body('cityId').isInt().withMessage('cityId sayı olmalı'),
body('startDate').notEmpty().withMessage('startDate gerekli'),
body('endDate').notEmpty().withMessage('endDate gerekli')
], AdminController.fetchYearly);
// POST /api/v1/admin/fetch/ramadan
router.post('/fetch/ramadan', AdminController.fetchRamadan);
// POST /api/v1/admin/fetch/eid
router.post('/fetch/eid', AdminController.fetchEid);
// POST /api/v1/admin/cache/clear
router.post('/cache/clear', AdminController.clearCache);
// GET /api/v1/admin/logs
router.get('/logs', AdminController.getLogs);
// --- Kullanıcı Yönetimi ---
// GET /api/v1/admin/users
router.get('/users', AdminController.getUsers);
// POST /api/v1/admin/users
router.post('/users', [
body('username').trim().isLength({ min: 3, max: 50 }).withMessage('Kullanıcı adı 3-50 karakter olmalı'),
body('password').isLength({ min: 6 }).withMessage('Şifre en az 6 karakter olmalı'),
body('role').optional().isIn(['admin', 'user']).withMessage('Rol admin veya user olmalı')
], AdminController.createUser);
// PATCH /api/v1/admin/users/:id/toggle
router.patch('/users/:id/toggle', AdminController.toggleUserActive);
// DELETE /api/v1/admin/users/:id
router.delete('/users/:id', AdminController.deleteUser);
module.exports = router;

View File

@@ -0,0 +1,32 @@
// hDiyanetProxy - Auth Routes
const express = require('express');
const router = express.Router();
const { body } = require('express-validator');
const AuthController = require('../controllers/authController');
const { authMiddleware } = require('../middleware/auth');
// POST /api/v1/auth/login
router.post('/login', [
body('username').trim().notEmpty().withMessage('Kullanıcı adı gerekli'),
body('password').notEmpty().withMessage('Şifre gerekli')
], AuthController.login);
// POST /api/v1/auth/register
router.post('/register', [
body('username').trim().isLength({ min: 3, max: 50 }).withMessage('Kullanıcı adı 3-50 karakter olmalı'),
body('password').isLength({ min: 6 }).withMessage('Şifre en az 6 karakter olmalı')
], AuthController.register);
// GET /api/v1/auth/profile
router.get('/profile', authMiddleware, AuthController.profile);
// POST /api/v1/auth/refresh
router.post('/refresh', authMiddleware, AuthController.refreshToken);
// POST /api/v1/auth/change-password
router.post('/change-password', authMiddleware, [
body('currentPassword').notEmpty().withMessage('Mevcut şifre gerekli'),
body('newPassword').isLength({ min: 6 }).withMessage('Yeni şifre en az 6 karakter olmalı')
], AuthController.changePassword);
module.exports = router;

12
backend/src/routes/eid.js Normal file
View File

@@ -0,0 +1,12 @@
// hDiyanetProxy - Eid Routes
const express = require('express');
const router = express.Router();
const EidController = require('../controllers/eidController');
const { authMiddleware } = require('../middleware/auth');
router.use(authMiddleware);
// GET /api/v1/eid?cityId={id}
router.get('/', EidController.getByCityId);
module.exports = router;

View File

@@ -0,0 +1,22 @@
// hDiyanetProxy - Places Routes
const express = require('express');
const router = express.Router();
const PlacesController = require('../controllers/placesController');
const { authMiddleware } = require('../middleware/auth');
// Tüm endpointler auth gerektirir
router.use(authMiddleware);
// GET /api/v1/places/countries
router.get('/countries', PlacesController.getCountries);
// GET /api/v1/places/states?countryId={id}
router.get('/states', PlacesController.getStates);
// GET /api/v1/places/cities?stateId={id}
router.get('/cities', PlacesController.getCities);
// GET /api/v1/places/city/:cityId
router.get('/city/:cityId', PlacesController.getCityDetail);
module.exports = router;

View File

@@ -0,0 +1,15 @@
// hDiyanetProxy - Prayer Times Routes
const express = require('express');
const router = express.Router();
const PrayerTimesController = require('../controllers/prayerTimesController');
const { authMiddleware } = require('../middleware/auth');
router.use(authMiddleware);
// GET /api/v1/prayer-times?cityId={id}&startDate={date}&endDate={date}
router.get('/', PrayerTimesController.getByDateRange);
// GET /api/v1/prayer-times/today?cityId={id}
router.get('/today', PrayerTimesController.getToday);
module.exports = router;

View File

@@ -0,0 +1,15 @@
// hDiyanetProxy - Ramadan Routes
const express = require('express');
const router = express.Router();
const RamadanController = require('../controllers/ramadanController');
const { authMiddleware } = require('../middleware/auth');
router.use(authMiddleware);
// GET /api/v1/ramadan?cityId={id}
router.get('/', RamadanController.getByCityId);
// GET /api/v1/ramadan/today?cityId={id}
router.get('/today', RamadanController.getToday);
module.exports = router;

View File

@@ -0,0 +1,220 @@
// hDiyanetProxy - Diyanet API Servisi
// Diyanet İşleri Başkanlığı API'sinden veri çekme işlemleri
const axios = require('axios');
const BASE_URL = process.env.DIYANET_API_URL || 'https://awqatsalah.diyanet.gov.tr';
const DIYANET_EMAIL = process.env.DIYANET_EMAIL;
const DIYANET_PASSWORD = process.env.DIYANET_PASSWORD;
// Token bilgileri bellekte tutulur
let accessToken = null;
let refreshToken = null;
let tokenExpiry = null;
// Axios instance oluştur
const api = axios.create({
baseURL: BASE_URL,
timeout: 30000,
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
// SSL sertifika sorunları için
httpsAgent: new (require('https').Agent)({ rejectUnauthorized: false })
});
// Her istekten önce token ekle
api.interceptors.request.use(async (config) => {
// Login isteğine token ekleme
if (config.url && config.url.includes('/Auth/Login')) {
return config;
}
// Token yoksa veya süresi dolmuşsa yenile
if (!accessToken || (tokenExpiry && Date.now() >= tokenExpiry)) {
await DiyanetService.authenticate();
}
if (accessToken) {
config.headers.Authorization = `Bearer ${accessToken}`;
}
return config;
});
// 401 hatası alırsa token yenile ve tekrar dene
api.interceptors.response.use(
(response) => response,
async (error) => {
const originalRequest = error.config;
if (error.response?.status === 401 && !originalRequest._retry) {
originalRequest._retry = true;
try {
// Önce refresh token dene
if (refreshToken) {
await DiyanetService.refreshAccessToken();
} else {
await DiyanetService.authenticate();
}
originalRequest.headers.Authorization = `Bearer ${accessToken}`;
return api(originalRequest);
} catch (authError) {
// Refresh da başarısız olursa, sıfırdan login ol
await DiyanetService.authenticate();
originalRequest.headers.Authorization = `Bearer ${accessToken}`;
return api(originalRequest);
}
}
return Promise.reject(error);
}
);
const DiyanetService = {
// --- Kimlik Doğrulama ---
// Email ve şifre ile giriş yap, token al
async authenticate() {
try {
const response = await axios.post(`${BASE_URL}/Auth/Login`, {
email: DIYANET_EMAIL,
password: DIYANET_PASSWORD
}, {
headers: { 'Content-Type': 'application/json' },
httpsAgent: new (require('https').Agent)({ rejectUnauthorized: false })
});
if (response.data.success) {
accessToken = response.data.data.accessToken;
refreshToken = response.data.data.refreshToken;
// JWT'den expiry süresini çıkar (güvenli tarafta kalmak için 5 dk erken yenile)
try {
const payload = JSON.parse(Buffer.from(accessToken.split('.')[1], 'base64').toString());
tokenExpiry = (payload.exp * 1000) - (5 * 60 * 1000);
} catch {
// 55 dakika sonra yenile (varsayılan)
tokenExpiry = Date.now() + (55 * 60 * 1000);
}
console.log('✅ Diyanet API token alındı');
return true;
} else {
throw new Error(`Diyanet auth hatası: ${response.data.message}`);
}
} catch (error) {
console.error('❌ Diyanet API kimlik doğrulama hatası:', error.message);
throw error;
}
},
// Refresh token ile yeni access token al
async refreshAccessToken() {
try {
const response = await axios.get(`${BASE_URL}/Auth/RefreshToken/${refreshToken}`, {
httpsAgent: new (require('https').Agent)({ rejectUnauthorized: false })
});
if (response.data.success) {
accessToken = response.data.data.accessToken;
refreshToken = response.data.data.refreshToken;
try {
const payload = JSON.parse(Buffer.from(accessToken.split('.')[1], 'base64').toString());
tokenExpiry = (payload.exp * 1000) - (5 * 60 * 1000);
} catch {
tokenExpiry = Date.now() + (55 * 60 * 1000);
}
console.log('🔄 Diyanet API token yenilendi');
return true;
}
throw new Error('Token yenileme başarısız');
} catch (error) {
console.error('❌ Token yenileme hatası:', error.message);
// Refresh başarısızsa sıfırdan login ol
accessToken = null;
refreshToken = null;
tokenExpiry = null;
throw error;
}
},
// --- Yer Bilgileri ---
// Yardımcı: API yanıtından veriyi çıkar
// Diyanet API {data: [...], success: true} formatında dönüyor
_extractData(response) {
const body = response.data;
if (body && typeof body === 'object' && 'data' in body) {
return body.data;
}
return body;
},
// Tüm ülkeleri çek
async getCountries() {
const response = await api.get('/api/Place/Countries');
return this._extractData(response);
},
// Ülkeye göre eyaletleri çek
async getStates(countryId) {
const response = await api.get(`/api/Place/States/${countryId}`);
return this._extractData(response);
},
// Tüm eyaletleri çek
async getAllStates() {
const response = await api.get('/api/Place/States');
return this._extractData(response);
},
// Eyalete göre şehirleri çek
async getCities(stateId) {
const response = await api.get(`/api/Place/Cities/${stateId}`);
return this._extractData(response);
},
// Tüm şehirleri çek
async getAllCities() {
const response = await api.get('/api/Place/Cities');
return this._extractData(response);
},
// Şehir detayı çek
async getCityDetail(cityId) {
const response = await api.get(`/api/Place/CityDetail/${cityId}`);
return this._extractData(response);
},
// --- Namaz Vakitleri ---
// Yıllık namaz vakitlerini çek
async getYearlyPrayerTimes(cityId, startDate, endDate) {
const response = await api.post('/api/AwqatSalah/Yearly', {
cityId: parseInt(cityId),
startDate,
endDate
});
return this._extractData(response);
},
// --- Ramazan İmsakiyesi ---
async getRamadanTimes(cityId) {
const response = await api.get(`/api/AwqatSalah/Ramadan/${cityId}`);
return this._extractData(response);
},
// --- Bayram Vakitleri ---
async getEidTimes(cityId) {
const response = await api.get(`/api/AwqatSalah/Eid/${cityId}`);
return this._extractData(response);
},
// --- Cache Temizleme ---
async clearCache() {
const response = await api.get('/api/Cache/ClearCache');
return this._extractData(response);
}
};
module.exports = DiyanetService;

View File

@@ -0,0 +1,191 @@
// hDiyanetProxy - Veri Çekme ve Senkronizasyon Servisi
const DiyanetService = require('./diyanetService');
const PlaceModel = require('../models/Place');
const PrayerTimeModel = require('../models/PrayerTime');
const RamadanModel = require('../models/RamadanTime');
const EidModel = require('../models/EidTime');
const FetchLogModel = require('../models/FetchLog');
const FetchService = {
// Tüm yer bilgilerini çek ve veritabanına kaydet
async fetchAndSavePlaces(userId = null) {
let totalRecords = 0;
try {
// 1. Ülkeleri çek
console.log('📍 Ülkeler çekiliyor...');
const countries = await DiyanetService.getCountries();
if (Array.isArray(countries)) {
for (const c of countries) {
await PlaceModel.upsertCountry(c.id, c.name);
}
totalRecords += countries.length;
console.log(`${countries.length} ülke kaydedildi`);
}
// 2. Eyaletleri çek (her ülke için)
console.log('📍 Eyaletler/İller çekiliyor...');
if (Array.isArray(countries)) {
for (const c of countries) {
try {
const states = await DiyanetService.getStates(c.id);
if (Array.isArray(states)) {
for (const s of states) {
await PlaceModel.upsertState(s.id, s.name, c.id);
}
totalRecords += states.length;
}
} catch (e) {
console.log(` ⚠️ ${c.name} eyaletleri alınamadı: ${e.message}`);
}
// Rate limiting - API'yi yormamak için küçük bekleme
await new Promise(r => setTimeout(r, 100));
}
console.log(` ✅ Eyaletler kaydedildi`);
}
// 3. Şehirleri çek - tüm eyaletler için
console.log('📍 Şehirler/İlçeler çekiliyor...');
const pool = require('../utils/db');
const [allStates] = await pool.execute('SELECT id, name FROM states');
for (const s of allStates) {
try {
const cities = await DiyanetService.getCities(s.id);
if (Array.isArray(cities)) {
for (const ci of cities) {
await PlaceModel.upsertCity(ci.id, ci.name, s.id, ci.latitude || null, ci.longitude || null);
}
totalRecords += cities.length;
}
} catch (e) {
// Bazı eyaletlerin şehirleri olmayabilir
}
await new Promise(r => setTimeout(r, 50));
}
console.log(` ✅ Şehirler kaydedildi`);
// Log kaydı
await FetchLogModel.create(userId, 'fetch_places', null, null, 'Tüm yer bilgileri güncellendi', 'success', totalRecords);
return { success: true, message: 'Yer bilgileri güncellendi', totalRecords };
} catch (err) {
await FetchLogModel.create(userId, 'fetch_places', null, null, `Hata: ${err.message}`, 'error', 0);
throw err;
}
},
// Yıllık namaz vakitlerini çek ve kaydet
async fetchAndSaveYearly(cityId, startDate, endDate, userId = null) {
try {
console.log(`🕌 Namaz vakitleri çekiliyor: cityId=${cityId}, ${startDate} - ${endDate}`);
const data = await DiyanetService.getYearlyPrayerTimes(cityId, startDate, endDate);
if (!data || !Array.isArray(data)) {
throw new Error('API\'den geçersiz veri döndü');
}
// Verileri dönüştür ve kaydet
const timesArray = data.map(item => ({
date: item.gregorianDateLong ? item.gregorianDateLong.split('T')[0] : item.gregorianDate,
fajr: item.fajr || item.imsak,
sunrise: item.sunrise || item.gunes,
dhuhr: item.dhuhr || item.ogle,
asr: item.asr || item.ikindi,
maghrib: item.maghrib || item.aksam,
isha: item.isha || item.yatsi,
qibla: item.qibla || null
}));
await PrayerTimeModel.bulkUpsert(cityId, timesArray);
// Şehir adını al
const cityDetail = await PlaceModel.getCityDetail(cityId);
const cityName = cityDetail ? cityDetail.name : `City ${cityId}`;
await FetchLogModel.create(userId, 'fetch_yearly', cityId, cityName,
`${startDate} - ${endDate} arası namaz vakitleri çekildi`, 'success', timesArray.length);
console.log(`${timesArray.length} namaz vakti kaydedildi`);
return { success: true, message: `${timesArray.length} namaz vakti kaydedildi`, count: timesArray.length };
} catch (err) {
await FetchLogModel.create(userId, 'fetch_yearly', cityId, null, `Hata: ${err.message}`, 'error', 0);
throw err;
}
},
// Ramazan vakitlerini çek ve kaydet
async fetchAndSaveRamadan(cityId, userId = null) {
try {
console.log(`🌙 Ramazan vakitleri çekiliyor: cityId=${cityId}`);
const data = await DiyanetService.getRamadanTimes(cityId);
if (!data || !Array.isArray(data)) {
throw new Error('API\'den geçersiz veri döndü');
}
const timesArray = data.map(item => ({
date: item.gregorianDateLong ? item.gregorianDateLong.split('T')[0] : item.gregorianDate,
fajr: item.fajr || item.imsak,
sunrise: item.sunrise || item.gunes,
dhuhr: item.dhuhr || item.ogle,
asr: item.asr || item.ikindi,
maghrib: item.maghrib || item.aksam,
isha: item.isha || item.yatsi
}));
await RamadanModel.bulkUpsert(cityId, timesArray);
const cityDetail = await PlaceModel.getCityDetail(cityId);
const cityName = cityDetail ? cityDetail.name : `City ${cityId}`;
await FetchLogModel.create(userId, 'fetch_ramadan', cityId, cityName,
'Ramazan vakitleri çekildi', 'success', timesArray.length);
console.log(`${timesArray.length} ramazan vakti kaydedildi`);
return { success: true, message: `${timesArray.length} ramazan vakti kaydedildi`, count: timesArray.length };
} catch (err) {
await FetchLogModel.create(userId, 'fetch_ramadan', cityId, null, `Hata: ${err.message}`, 'error', 0);
throw err;
}
},
// Bayram vakitlerini çek ve kaydet
async fetchAndSaveEid(cityId, userId = null) {
try {
console.log(`🎉 Bayram vakitleri çekiliyor: cityId=${cityId}`);
const data = await DiyanetService.getEidTimes(cityId);
if (!data) {
throw new Error('API\'den geçersiz veri döndü');
}
// Bayram verisi tek obje veya dizi olabilir
const items = Array.isArray(data) ? data : [data];
const eidItems = items.map(item => ({
eidName: item.eidName || item.bayramAdi || 'Bayram',
date: item.gregorianDateLong ? item.gregorianDateLong.split('T')[0] : item.gregorianDate || item.date,
prayerTime: item.eidPrayerTime || item.bayramNamazi || null,
fajr: item.fajr || item.imsak || null,
sunrise: item.sunrise || item.gunes || null,
dhuhr: item.dhuhr || item.ogle || null,
asr: item.asr || item.ikindi || null,
maghrib: item.maghrib || item.aksam || null,
isha: item.isha || item.yatsi || null
}));
await EidModel.bulkUpsert(cityId, eidItems);
const cityDetail = await PlaceModel.getCityDetail(cityId);
const cityName = cityDetail ? cityDetail.name : `City ${cityId}`;
await FetchLogModel.create(userId, 'fetch_eid', cityId, cityName,
'Bayram vakitleri çekildi', 'success', eidItems.length);
console.log(`${eidItems.length} bayram vakti kaydedildi`);
return { success: true, message: `${eidItems.length} bayram vakti kaydedildi`, count: eidItems.length };
} catch (err) {
await FetchLogModel.create(userId, 'fetch_eid', cityId, null, `Hata: ${err.message}`, 'error', 0);
throw err;
}
}
};
module.exports = FetchService;

27
backend/src/utils/db.js Normal file
View File

@@ -0,0 +1,27 @@
// hDiyanetProxy - Veritabanı bağlantı modülü
const mysql = require('mysql2/promise');
require('dotenv').config();
const pool = mysql.createPool({
host: process.env.DB_HOST,
port: process.env.DB_PORT || 3306,
user: process.env.DB_USER,
password: process.env.DB_PASS,
database: process.env.DB_NAME,
waitForConnections: true,
connectionLimit: 10,
queueLimit: 0,
charset: 'utf8mb4'
});
// Bağlantı testi
pool.getConnection()
.then(conn => {
console.log('✅ MySQL veritabanına bağlandı');
conn.release();
})
.catch(err => {
console.error('❌ MySQL bağlantı hatası:', err.message);
});
module.exports = pool;

View File

@@ -0,0 +1,134 @@
// hDiyanetProxy - Migration Script
// Veritabanı tablolarını oluşturur
const pool = require('./db');
const migrations = [
// Kullanıcılar tablosu
`CREATE TABLE IF NOT EXISTS users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
role ENUM('admin', 'user') DEFAULT 'user',
is_active TINYINT(1) DEFAULT 1,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4`,
// Ülkeler tablosu
`CREATE TABLE IF NOT EXISTS countries (
id INT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4`,
// Eyaletler/İller tablosu
`CREATE TABLE IF NOT EXISTS states (
id INT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
country_id INT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX idx_country (country_id),
FOREIGN KEY (country_id) REFERENCES countries(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4`,
// Şehirler/İlçeler tablosu
`CREATE TABLE IF NOT EXISTS cities (
id INT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
state_id INT NOT NULL,
latitude DECIMAL(10,7) NULL,
longitude DECIMAL(10,7) NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX idx_state (state_id),
FOREIGN KEY (state_id) REFERENCES states(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4`,
// Namaz vakitleri tablosu
`CREATE TABLE IF NOT EXISTS prayer_times (
id INT AUTO_INCREMENT PRIMARY KEY,
city_id INT NOT NULL,
date DATE NOT NULL,
fajr VARCHAR(10),
sunrise VARCHAR(10),
dhuhr VARCHAR(10),
asr VARCHAR(10),
maghrib VARCHAR(10),
isha VARCHAR(10),
qibla VARCHAR(10),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_city_date (city_id, date),
INDEX idx_city (city_id),
INDEX idx_date (date)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4`,
// Ramazan vakitleri tablosu
`CREATE TABLE IF NOT EXISTS ramadan_times (
id INT AUTO_INCREMENT PRIMARY KEY,
city_id INT NOT NULL,
date DATE NOT NULL,
fajr VARCHAR(10),
sunrise VARCHAR(10),
dhuhr VARCHAR(10),
asr VARCHAR(10),
maghrib VARCHAR(10),
isha VARCHAR(10),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_city_date (city_id, date),
INDEX idx_city (city_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4`,
// Bayram vakitleri tablosu
`CREATE TABLE IF NOT EXISTS eid_times (
id INT AUTO_INCREMENT PRIMARY KEY,
city_id INT NOT NULL,
eid_name VARCHAR(100),
date DATE NOT NULL,
prayer_time VARCHAR(10),
fajr VARCHAR(10),
sunrise VARCHAR(10),
dhuhr VARCHAR(10),
asr VARCHAR(10),
maghrib VARCHAR(10),
isha VARCHAR(10),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_city_eid_date (city_id, eid_name, date),
INDEX idx_city (city_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4`,
// Veri çekme logları tablosu
`CREATE TABLE IF NOT EXISTS fetch_logs (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NULL,
action VARCHAR(50) NOT NULL,
city_id INT NULL,
city_name VARCHAR(255) NULL,
details TEXT NULL,
status ENUM('success', 'error') DEFAULT 'success',
records_count INT DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX idx_user (user_id),
INDEX idx_created (created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4`
];
async function runMigrations() {
console.log('🔄 Migration başlatılıyor...\n');
for (let i = 0; i < migrations.length; i++) {
const tableName = migrations[i].match(/CREATE TABLE IF NOT EXISTS (\w+)/)?.[1] || `migration_${i}`;
try {
await pool.execute(migrations[i]);
console.log(`${tableName} tablosu oluşturuldu`);
} catch (err) {
console.error(`${tableName} hatası: ${err.message}`);
}
}
console.log('\n✅ Migration tamamlandı!');
process.exit(0);
}
runMigrations();

31
backend/src/utils/seed.js Normal file
View File

@@ -0,0 +1,31 @@
// hDiyanetProxy - Seed Script
// İlk admin kullanıcıyı oluşturur
const pool = require('./db');
const bcrypt = require('bcrypt');
async function seed() {
console.log('🌱 Seed başlatılıyor...\n');
try {
// Admin kullanıcı kontrolü
const [existing] = await pool.execute('SELECT id FROM users WHERE username = ?', ['admin']);
if (existing.length > 0) {
console.log(' Admin kullanıcı zaten mevcut, atlanıyor');
} else {
const hashedPassword = await bcrypt.hash('admin123', 10);
await pool.execute(
'INSERT INTO users (username, password, role, is_active) VALUES (?, ?, ?, ?)',
['admin', hashedPassword, 'admin', 1]
);
console.log(' ✅ Admin kullanıcı oluşturuldu (admin / admin123)');
}
} catch (err) {
console.error(' ❌ Seed hatası:', err.message);
}
console.log('\n✅ Seed tamamlandı!');
process.exit(0);
}
seed();