Files
ytp-glm/src/server.js

1519 lines
40 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import express from 'express';
import session from 'express-session';
import { createServer } from 'http';
import { Server } from 'socket.io';
import sqlite3 from 'sqlite3';
import bcrypt from 'bcrypt';
import { promises as fs } from 'fs';
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';
import {
getGoodsManagers,
addGoodsManager,
updateGoodsManager,
deleteGoodsManager
} from './lib/data/goodsManagers.js';
// Units veritabanı
let units = [
{
id: 1,
name: '1. Motorlu Piyade Tugayı',
address: 'Mecidiyeköy, Şişli/İstanbul',
stk: 'STK-12345',
btk: 'BTK-67890',
commander: {
full_name: 'Mehmet Yılmaz',
rank: 'Yüzbaşı',
registration_number: '123456',
tc_kimlik: '12345678901',
phone: '05321234567'
},
created_at: new Date().toISOString()
},
{
id: 2,
name: '2. Zırhlı Tabur',
address: 'Havran, Balıkesir',
stk: 'STK-54321',
btk: 'BTK-09876',
commander: {
full_name: 'Ali Kaya',
rank: 'Binbaşı',
registration_number: '654321',
tc_kimlik: '98765432109',
phone: '05337654321'
},
created_at: new Date().toISOString()
},
{
id: 3,
name: '3. Komutanlık',
address: 'Çankaya, Ankara',
stk: 'STK-11111',
btk: 'BTK-22222',
commander: {
full_name: 'Hasan Demir',
rank: 'Üsteğmen',
registration_number: '111111',
tc_kimlik: '11111111111',
phone: '05321111111'
},
created_at: new Date().toISOString()
}
];
let nextUnitId = 4;
// Vehicles veritabanı
let vehicles = [
{
id: 1,
brand: 'Mercedes-Benz',
model: 'Actros 1851',
year: 2020,
plate: '34 ABC 123',
fuel_type: 'Dizel',
fuel_capacity: 500,
current_fuel: 300,
status: 'Aktif',
driver_id: 3,
driver_name: 'Ali Veli',
created_at: new Date().toISOString()
},
{
id: 2,
brand: 'Volvo',
model: 'FH16 750',
year: 2021,
plate: '34 XYZ 789',
fuel_type: 'Dizel',
fuel_capacity: 600,
current_fuel: 450,
status: 'Aktif',
driver_id: 4,
driver_name: 'İbrahim Kara',
created_at: new Date().toISOString()
},
{
id: 3,
brand: 'Scania',
model: 'R730',
year: 2019,
plate: '34 DEF 456',
fuel_type: 'Dizel',
fuel_capacity: 550,
current_fuel: 200,
status: 'Bakımda',
driver_id: null,
driver_name: null,
created_at: new Date().toISOString()
}
];
let nextVehicleId = 4;
// Fuel Personnel veritabanı
let fuelPersonnel = [
{
id: 1,
full_name: 'Ahmet Demir',
rank: 'Üsteğmen',
registration_number: '111222',
tc_kimlik: '11111111111',
phone: '05321112233',
is_active: true,
created_at: new Date().toISOString()
},
{
id: 2,
full_name: 'Mustafa Çelik',
rank: 'Astsubay',
registration_number: '333444',
tc_kimlik: '22222222222',
phone: '05334455566',
is_active: true,
created_at: new Date().toISOString()
}
];
let nextFuelPersonnelId = 3;
const app = express();
const server = createServer(app);
const io = new Server(server, {
cors: {
origin: "http://localhost:5173",
methods: ["GET", "POST"]
}
});
// Export io for use in other modules
// export { io }; // Commented out to avoid circular dependency
const PORT = process.env.PORT || 3000;
// ES Module equivalent of __dirname
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
// Database path setup - ensure /db directory exists and use it
const projectRoot = dirname(__dirname); // Go up from src/ to project root
const dbDir = join(projectRoot, 'db');
const dbPath = join(dbDir, 'yakit_takip.db');
// Middleware
app.use(express.json());
// Note: static file serving removed - SvelteKit handles frontend in development
// Session middleware
app.use(session({
secret: 'yakit-takip-modulu-secret-key-2023',
resave: false,
saveUninitialized: false,
cookie: {
secure: false, // development için false
maxAge: 24 * 60 * 60 * 1000 // 24 saat
}
}));
// Ensure /db directory exists
async function ensureDbDirectory() {
try {
await fs.mkdir(dbDir, { recursive: true });
console.log(`📁 Database directory ensured: ${dbDir}`);
} catch (error) {
console.error('❌ Error creating database directory:', error);
throw error;
}
}
// Veritabanı değişkeni (başlangıçta null)
let db;
// Veritabanı bağlantısını oluştur
async function createDatabaseConnection() {
return new Promise((resolve, reject) => {
db = new sqlite3.Database(dbPath, (err) => {
if (err) {
console.error('❌ Database connection error:', err);
reject(err);
} else {
console.log('✅ Database connection established');
resolve();
}
});
});
}
// Veritabanı tablolarını oluştur
async function initializeDatabase() {
return new Promise((resolve, reject) => {
db.serialize(() => {
// Kullanıcılar tablosu
db.run(`CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE NOT NULL,
password TEXT NOT NULL,
role TEXT NOT NULL,
full_name TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
is_active BOOLEAN DEFAULT 1
)`, (err) => {
if (err) reject(err);
});
// Yakıt fişleri tablosu
db.run(`CREATE TABLE IF NOT EXISTS fuel_slips (
id INTEGER PRIMARY KEY AUTOINCREMENT,
date TEXT NOT NULL,
force_command TEXT NOT NULL,
unit_id INTEGER,
unit_name TEXT,
vehicle_id INTEGER,
vehicle_info TEXT,
fuel_type TEXT NOT NULL,
liters REAL NOT NULL,
km INTEGER,
personnel_id INTEGER,
personnel_info TEXT,
goods_manager_id INTEGER,
goods_manager_info TEXT,
fuel_manager_id INTEGER,
fuel_manager_info TEXT,
status TEXT DEFAULT 'pending',
notes TEXT,
approval_date TEXT,
approval_notes TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
)`, (err) => {
if (err) reject(err);
});
// Tablolar oluşturulduktan sonra kullanıcıları ekle
setTimeout(async () => {
// Örnek kullanıcıları ekle
const users = [
{ username: 'admin', password: 'admin123', role: 'admin', full_name: 'Sistem Yöneticisi' },
{ username: 'fuel', password: 'fuel123', role: 'fuel_manager', full_name: 'Yakıt Sorumlusu' },
{ username: 'goods', password: 'goods123', role: 'goods_manager', full_name: 'Mal Sorumlusu' },
{ username: 'ibrahim_kara', password: 'kara123', role: 'goods_manager', full_name: 'İbrahim Kara' }
];
// Her kullanıcıyı kontrol et ve yoksa ekle
for (const user of users) {
const hashedPassword = await bcrypt.hash(user.password, 10);
db.get('SELECT id FROM users WHERE username = ?', [user.username], (err, row) => {
if (!row) {
db.run('INSERT INTO users (username, password, role, full_name) VALUES (?, ?, ?, ?)',
[user.username, hashedPassword, user.role, user.full_name]);
}
});
}
resolve();
}, 100);
});
});
}
// API Routes
// Login endpoint
app.post('/api/login', async (req, res) => {
const { username, password } = req.body;
if (!username || !password) {
return res.status(400).json({ message: 'Kullanıcı adı ve şifre gerekli.' });
}
try {
db.get('SELECT * FROM users WHERE username = ? AND is_active = 1', [username], async (err, user) => {
if (err) {
return res.status(500).json({ message: 'Veritabanı hatası.' });
}
if (!user) {
return res.status(401).json({ message: 'Kullanıcı bulunamadı.' });
}
const passwordMatch = await bcrypt.compare(password, user.password);
if (!passwordMatch) {
return res.status(401).json({ message: 'Şifre hatalı.' });
}
// Session oluştur
let sessionUser = {
id: user.id,
username: user.username,
role: user.role,
full_name: user.full_name
};
// Eğer goods_manager ise, goods_managers API'den gerçek ID'yi al
if (user.role === 'goods_manager') {
try {
const goodsManagersRes = await fetch('http://localhost:3000/api/goods-managers');
if (goodsManagersRes.ok) {
const goodsData = await goodsManagersRes.json();
const goodsManager = goodsData.goodsManagers?.find(gm => gm.username === user.username);
if (goodsManager) {
sessionUser.id = goodsManager.id; // goods_manager ID'sini kullan
console.log(`✅ Goods manager logged in: ${user.full_name} (ID: ${goodsManager.id})`);
}
}
} catch (fetchError) {
console.warn('⚠️ Could not fetch goods manager ID:', fetchError);
}
}
req.session.user = sessionUser;
res.json({
message: 'Giriş başarılı.',
user: sessionUser
});
});
} catch (error) {
res.status(500).json({ message: 'Sunucu hatası.' });
}
});
// Logout endpoint
app.post('/api/logout', (req, res) => {
req.session.destroy((err) => {
if (err) {
return res.status(500).json({ message: ıkış yapılamadı.' });
}
res.json({ message: ıkış başarılı.' });
});
});
// Mevcut kullanıcı bilgisi
app.get('/api/user', (req, res) => {
if (!req.session.user) {
return res.status(401).json({ message: 'Oturum bulunamadı.' });
}
res.json({ user: req.session.user });
});
// Socket.IO bildirim endpoint'i
app.post('/api/socket-notify', (req, res) => {
const { event, data } = req.body;
if (!event || !data) {
return res.status(400).json({ message: 'Event ve data zorunludur.' });
}
// Socket.IO ile olay yayınla
console.log(`📢 Socket.IO event emitted: ${event}`, data);
io.emit(event, data);
res.json({ message: 'Bildirim gönderildi.' });
});
// Tüm kullanıcıları getir (sadece admin)
app.get('/api/users', (req, res) => {
if (!req.session.user || req.session.user.role !== 'admin') {
return res.status(403).json({ message: 'Yetkisiz erişim.' });
}
db.all('SELECT id, username, role, full_name, created_at, is_active FROM users', (err, users) => {
if (err) {
return res.status(500).json({ message: 'Veritabanı hatası.' });
}
res.json({ users });
});
});
// Units API endpoint'leri
// GET - Tüm birlikleri listele
app.get('/api/units', (req, res) => {
// Yetki kontrolü (temporary - will be implemented with proper session)
res.json({ units });
});
// POST - Yeni birlik ekle
app.post('/api/units', async (req, res) => {
// Yetki kontrolü (temporary - will be implemented with proper session)
try {
const {
name,
address,
stk,
btk,
commander
} = req.body;
// Validasyon
if (!name || !address || !stk || !btk || !commander) {
return res.status(400).json({ message: 'Tüm alanlar zorunludur.' });
}
// Komutan validasyonu
const { full_name, rank, registration_number, tc_kimlik, phone, username, password } = commander;
if (!full_name || !rank || !registration_number || !tc_kimlik || !phone || !username || !password) {
return res.status(400).json({ message: 'Mal sorumlusunun tüm bilgileri zorunludur.' });
}
// TC Kimlik numarası validasyonu
if (!/^[0-9]{11}$/.test(tc_kimlik)) {
return res.status(400).json({ message: 'TC Kimlik numarası 11 haneli olmalıdır.' });
}
// Yeni birlik oluştur
const newUnit = {
id: nextUnitId++,
name: name.trim(),
address: address.trim(),
stk: stk.trim().toUpperCase(),
btk: btk.trim().toUpperCase(),
commander: {
full_name: full_name.trim(),
rank: rank.trim(),
registration_number: registration_number.trim(),
tc_kimlik: tc_kimlik.trim(),
phone: phone.trim(),
username: username.trim(),
password: password.trim()
},
created_at: new Date().toISOString()
};
// Mal sorumlusu için kullanıcı oluştur
try {
const hashedPassword = await bcrypt.hash(password, 10);
// Kullanıcı adı benzersizliğini kontrol et
const existingUser = await new Promise((resolve, reject) => {
db.get('SELECT id FROM users WHERE username = ?', [username], (err, row) => {
if (err) reject(err);
else resolve(row);
});
});
if (existingUser) {
return res.status(400).json({ message: 'Bu kullanıcı adı zaten kullanımda.' });
}
// Mal sorumlusu kullanıcı olarak ekle
await new Promise((resolve, reject) => {
db.run('INSERT INTO users (username, password, role, full_name) VALUES (?, ?, ?, ?)',
[username, hashedPassword, 'goods_manager', full_name], (err) => {
if (err) reject(err);
else resolve();
});
});
console.log(`✅ Mal sorumlusu kullanıcı oluşturuldu: ${username}`);
} catch (userError) {
console.error('❌ Mal sorumlusu kullanıcı oluşturma hatası:', userError);
return res.status(500).json({ message: 'Mal sorumlusu kullanıcı oluşturulamadı.' });
}
units.push(newUnit);
res.json({
message: 'Birlik başarıyla eklendi.',
unit: newUnit
});
} catch (error) {
res.status(500).json({ message: 'Sunucu hatası.' });
}
});
// PUT - Birlik güncelle
app.put('/api/units', async (req, res) => {
// Yetki kontrolü (temporary - will be implemented with proper session)
try {
const {
id,
name,
address,
stk,
btk,
commander
} = req.body;
// Validasyon
if (!id || !name || !address || !stk || !btk || !commander) {
return res.status(400).json({ message: 'Tüm alanlar zorunludur.' });
}
// Komutan validasyonu
const { full_name, rank, registration_number, tc_kimlik, phone, username, password } = commander;
if (!full_name || !rank || !registration_number || !tc_kimlik || !phone || !username || !password) {
return res.status(400).json({ message: 'Mal sorumlusunun tüm bilgileri zorunludur.' });
}
// TC Kimlik numarası validasyonu
if (!/^[0-9]{11}$/.test(tc_kimlik)) {
return res.status(400).json({ message: 'TC Kimlik numarası 11 haneli olmalıdır.' });
}
// Birlik bul
const unitIndex = units.findIndex(u => u.id === parseInt(id));
if (unitIndex === -1) {
return res.status(404).json({ message: 'Birlik bulunamadı.' });
}
// Birlik güncelle
const updatedCommander = {
full_name: full_name.trim(),
rank: rank.trim(),
registration_number: registration_number.trim(),
tc_kimlik: tc_kimlik.trim(),
phone: phone.trim(),
username: username.trim(),
password: password.trim()
};
units[unitIndex] = {
...units[unitIndex],
name: name.trim(),
address: address.trim(),
stk: stk.trim().toUpperCase(),
btk: btk.trim().toUpperCase(),
commander: updatedCommander
};
// Eğer kullanıcı adı veya şifre değişmişse, kullanıcıyı güncelle
const oldCommander = units[unitIndex].commander;
if (oldCommander.username !== username || password !== oldCommander.password) {
try {
const hashedPassword = await bcrypt.hash(password, 10);
// Kullanıcıyı güncelle
await new Promise((resolve, reject) => {
db.run('UPDATE users SET username = ?, password = ?, full_name = ? WHERE username = ?',
[username, hashedPassword, full_name, oldCommander.username], (err) => {
if (err) reject(err);
else resolve();
});
});
console.log(`✅ Mal sorumlusu kullanıcı güncellendi: ${username}`);
} catch (userError) {
console.error('❌ Mal sorumlusu kullanıcı güncelleme hatası:', userError);
// Kullanıcı güncellenemezse, birlik güncellemesi geri alınsın
units[unitIndex] = {
...units[unitIndex],
commander: oldCommander
};
return res.status(500).json({ message: 'Mal sorumlusu kullanıcı güncellenemedi.' });
}
}
res.json({
message: 'Birlik başarıyla güncellendi.',
unit: units[unitIndex]
});
} catch (error) {
res.status(500).json({ message: 'Sunucu hatası.' });
}
});
// DELETE - Birlik sil
app.delete('/api/units', (req, res) => {
// Yetki kontrolü (temporary - will be implemented with proper session)
try {
const { id } = req.body;
if (!id) {
return res.status(400).json({ message: 'Birlik ID zorunludur.' });
}
// Birlik bul
const unitIndex = units.findIndex(u => u.id === parseInt(id));
if (unitIndex === -1) {
return res.status(404).json({ message: 'Birlik bulunamadı.' });
}
// Birlik sil
const deletedUnit = units.splice(unitIndex, 1)[0];
res.json({
message: 'Birlik başarıyla silindi.',
unit: deletedUnit
});
} catch (error) {
res.status(500).json({ message: 'Sunucu hatası.' });
}
});
// Goods Managers API endpoint'leri
// GET - Tüm mal sorumlularını listele
app.get('/api/goods-managers', (req, res) => {
// Yetki kontrolü (temporary - will be implemented with proper session)
try {
const goodsManagers = getGoodsManagers();
res.json({ goodsManagers });
} catch (error) {
res.status(500).json({ message: 'Mal sorumluları alınırken hata oluştu.' });
}
});
// POST - Yeni mal sorumlusu ekle
app.post('/api/goods-managers', async (req, res) => {
// Yetki kontrolü (temporary - will be implemented with proper session)
try {
const {
full_name,
rank,
registration_number,
tc_kimlik,
phone,
unit_id,
username,
password,
is_active = true
} = req.body;
const newManager = addGoodsManager({
full_name,
rank,
registration_number,
tc_kimlik,
phone,
unit_id,
unit_name: units.find(u => u.id === parseInt(unit_id))?.name || 'Bilinmeyen Birlik',
username,
password,
is_active
});
res.json({
message: 'Personel başarıyla eklendi.',
goodsManager: newManager
});
} catch (error) {
if (error.message.includes('zorunludur') ||
error.message.includes('zaten kayıtlı') ||
error.message.includes('Geçersiz') ||
error.message.includes('karakter')) {
return res.status(400).json({ message: error.message });
}
res.status(500).json({ message: 'Sunucu hatası.' });
}
});
// PUT - Mal sorumlusu güncelle
app.put('/api/goods-managers', async (req, res) => {
// Yetki kontrolü (temporary - will be implemented with proper session)
try {
const {
id,
full_name,
rank,
registration_number,
tc_kimlik,
phone,
unit_id,
username,
password,
is_active
} = req.body;
const updatedManager = updateGoodsManager(id, {
full_name,
rank,
registration_number,
tc_kimlik,
phone,
unit_id,
unit_name: units.find(u => u.id === parseInt(unit_id))?.name || 'Bilinmeyen Birlik',
username,
password,
is_active
});
res.json({
message: 'Personel başarıyla güncellendi.',
goodsManager: updatedManager
});
} catch (error) {
if (error.message.includes('zorunludur') ||
error.message.includes('bulunamadı') ||
error.message.includes('zaten kayıtlı') ||
error.message.includes('Geçersiz') ||
error.message.includes('karakter')) {
return res.status(error.message.includes('bulunamadı') ? 404 : 400).json({ message: error.message });
}
res.status(500).json({ message: 'Sunucu hatası.' });
}
});
// DELETE - Mal sorumlusu sil
app.delete('/api/goods-managers', async (req, res) => {
// Yetki kontrolü (temporary - will be implemented with proper session)
try {
const { id } = req.body;
if (!id) {
return res.status(400).json({ message: 'Mal sorumlusu ID zorunludur.' });
}
const deletedManager = deleteGoodsManager(id);
res.json({
message: 'Mal sorumlusu başarıyla silindi.',
goodsManager: deletedManager
});
} catch (error) {
if (error.message.includes('bulunamadı')) {
return res.status(404).json({ message: error.message });
}
res.status(500).json({ message: 'Sunucu hatası.' });
}
});
// Fuel Personnel API endpoint'leri
// GET - Tüm yakıt personelini listele
app.get('/api/fuel-personnel', (req, res) => {
// Yetki kontrolü (temporary - will be implemented with proper session)
res.json({ fuelPersonnel });
});
// POST - Yeni yakıt personeli ekle
app.post('/api/fuel-personnel', async (req, res) => {
// Yetki kontrolü (temporary - will be implemented with proper session)
try {
const {
full_name,
rank,
registration_number,
tc_kimlik,
phone,
unit_id,
is_active = true
} = req.body;
// Validasyon
if (!full_name || !rank || !registration_number || !tc_kimlik || !phone || !unit_id) {
return res.status(400).json({ message: 'Tüm alanlar zorunludur.' });
}
// TC Kimlik numarası validasyonu
if (!/^[0-9]{11}$/.test(tc_kimlik)) {
return res.status(400).json({ message: 'TC Kimlik numarası 11 haneli olmalıdır.' });
}
// Sicil numarası tekrar kontrolü
const existingPersonnel = fuelPersonnel.find(p =>
p.registration_number === registration_number
);
if (existingPersonnel) {
return res.status(400).json({ message: 'Bu sicil numarası zaten kayıtlı.' });
}
// TC Kimlik numarası tekrar kontrolü
const existingTc = fuelPersonnel.find(p =>
p.tc_kimlik === tc_kimlik
);
if (existingTc) {
return res.status(400).json({ message: 'Bu TC Kimlik numarası zaten kayıtlı.' });
}
// Yeni personel oluştur
const newPersonnel = {
id: nextFuelPersonnelId++,
full_name: full_name.trim(),
rank: rank.trim(),
registration_number: registration_number.trim(),
tc_kimlik: tc_kimlik.trim(),
phone: phone.trim(),
unit_id: parseInt(unit_id),
is_active: Boolean(is_active),
created_at: new Date().toISOString()
};
// Birlik personeli için kullanıcı oluştur
try {
// Kullanıcı adını TC kimlik numarasından oluştur
const username = `bp_${tc_kimlik}`;
const defaultPassword = tc_kimlik.substring(0, 6); // İlk 6 hane varsayılan şifre
const hashedPassword = await bcrypt.hash(defaultPassword, 10);
// Kullanıcı adı benzersizliğini kontrol et
const existingUser = await new Promise((resolve, reject) => {
db.get('SELECT id FROM users WHERE username = ?', [username], (err, row) => {
if (err) reject(err);
else resolve(row);
});
});
if (existingUser) {
return res.status(400).json({ message: 'Bu personel için zaten kullanıcı hesabı mevcut.' });
}
// Birlik personeli olarak kullanıcı ekle
await new Promise((resolve, reject) => {
db.run('INSERT INTO users (username, password, role, full_name) VALUES (?, ?, ?, ?)',
[username, hashedPassword, 'birlik_personeli', full_name], (err) => {
if (err) reject(err);
else resolve();
});
});
console.log(`✅ Birlik personeli kullanıcı oluşturuldu: ${username}`);
} catch (userError) {
console.error('❌ Birlik personeli kullanıcı oluşturma hatası:', userError);
return res.status(500).json({ message: 'Personel için kullanıcı hesabı oluşturulamadı.' });
}
fuelPersonnel.push(newPersonnel);
res.json({
message: 'Personel başarıyla eklendi.',
fuelPersonnel: newPersonnel
});
} catch (error) {
res.status(500).json({ message: 'Sunucu hatası.' });
}
});
// PUT - Yakıt personeli güncelle
app.put('/api/fuel-personnel', (req, res) => {
// Yetki kontrolü (temporary - will be implemented with proper session)
try {
const {
id,
full_name,
rank,
registration_number,
tc_kimlik,
phone,
unit_id,
is_active
} = req.body;
// Validasyon
if (!id || !full_name || !rank || !registration_number || !tc_kimlik || !phone || !unit_id) {
return res.status(400).json({ message: 'Tüm alanlar zorunludur.' });
}
// TC Kimlik numarası validasyonu
if (!/^[0-9]{11}$/.test(tc_kimlik)) {
return res.status(400).json({ message: 'TC Kimlik numarası 11 haneli olmalıdır.' });
}
// Personel bul
const personnelIndex = fuelPersonnel.findIndex(p => p.id === parseInt(id));
if (personnelIndex === -1) {
return res.status(404).json({ message: 'Personel bulunamadı.' });
}
// Sicil numarası tekrar kontrolü (başka personel için)
const existingRegNumber = fuelPersonnel.find(p =>
p.id !== parseInt(id) && p.registration_number === registration_number
);
if (existingRegNumber) {
return res.status(400).json({ message: 'Bu sicil numarası başka bir personelde kayıtlı.' });
}
// TC Kimlik numarası tekrar kontrolü (başka personel için)
const existingTc = fuelPersonnel.find(p =>
p.id !== parseInt(id) && p.tc_kimlik === tc_kimlik
);
if (existingTc) {
return res.status(400).json({ message: 'Bu TC Kimlik numarası başka bir personelde kayıtlı.' });
}
// Personeli güncelle
fuelPersonnel[personnelIndex] = {
...fuelPersonnel[personnelIndex],
full_name: full_name.trim(),
rank: rank.trim(),
registration_number: registration_number.trim(),
tc_kimlik: tc_kimlik.trim(),
phone: phone.trim(),
unit_id: parseInt(unit_id),
is_active: Boolean(is_active)
};
res.json({
message: 'Personel başarıyla güncellendi.',
fuelPersonnel: fuelPersonnel[personnelIndex]
});
} catch (error) {
res.status(500).json({ message: 'Sunucu hatası.' });
}
});
// DELETE - Yakıt personeli sil
app.delete('/api/fuel-personnel', (req, res) => {
// Yetki kontrolü (temporary - will be implemented with proper session)
try {
const { id } = req.body;
if (!id) {
return res.status(400).json({ message: 'Personel ID zorunludur.' });
}
// Personel bul
const personnelIndex = fuelPersonnel.findIndex(p => p.id === parseInt(id));
if (personnelIndex === -1) {
return res.status(404).json({ message: 'Personel bulunamadı.' });
}
// Personel sil
const deletedPersonnel = fuelPersonnel.splice(personnelIndex, 1)[0];
res.json({
message: 'Personel başarıyla silindi.',
fuelPersonnel: deletedPersonnel
});
} catch (error) {
res.status(500).json({ message: 'Sunucu hatası.' });
}
});
// Fuel Slips API endpoint'leri
// GET - Yakıt fişlerini listele
app.get('/api/fuel-slips', (req, res) => {
const searchParams = req.query;
const status = searchParams.status;
const manager_id = searchParams.manager_id;
const fuel_manager_id = searchParams.fuel_manager_id;
// Veritabanı sorgusu
let query = 'SELECT * FROM fuel_slips WHERE 1=1';
const params = [];
// Status filtreleme
if (status) {
query += ' AND status = ?';
params.push(status);
}
// Mal sorumlusu filtreleme
if (manager_id) {
query += ' AND goods_manager_id = ?';
params.push(manager_id);
}
// Yakıt sorumlusu filtreleme
if (fuel_manager_id) {
query += ' AND fuel_manager_id = ?';
params.push(fuel_manager_id);
}
// Tarihe göre ters sırala
query += ' ORDER BY created_at DESC';
db.all(query, params, (err, rows) => {
if (err) {
console.error('GET fuel slips error:', err);
return res.status(500).json({ message: 'Veritabanı hatası.' });
}
// JSON string olarak saklanan alanları parse et
const fuelSlips = rows.map(row => ({
...row,
vehicle_info: row.vehicle_info ? JSON.parse(row.vehicle_info) : null,
personnel_info: row.personnel_info ? JSON.parse(row.personnel_info) : null,
goods_manager_info: row.goods_manager_info ? JSON.parse(row.goods_manager_info) : null,
fuel_manager_info: row.fuel_manager_info ? JSON.parse(row.fuel_manager_info) : null
}));
res.json({ fuelSlips });
});
});
// POST - Yeni yakıt fişi oluştur
app.post('/api/fuel-slips', async (req, res) => {
try {
const slipData = req.body;
console.log('📝 Creating new fuel slip with data:', slipData);
// Validasyon
const requiredFields = [
'date', 'force_command', 'unit_id', 'vehicle_id',
'fuel_type', 'liters', 'km', 'personnel_id',
'goods_manager_id', 'fuel_manager_id'
];
for (const field of requiredFields) {
if (!slipData[field]) {
return res.status(400).json({ message: `${field} alanı zorunludur.` });
}
}
// Litre ve KM validasyonu
if (slipData.liters <= 0 || slipData.km < 0) {
return res.status(400).json({ message: 'Litre ve KM değerleri geçersiz.' });
}
// Araç, personel ve mal sorumlusu bilgilerini getir
const [vehicle, unit, person, goodsManager] = await Promise.all([
vehicles.find(v => v.id === parseInt(slipData.vehicle_id)),
units.find(u => u.id === parseInt(slipData.unit_id)),
Promise.resolve({ // Temporary fuel personnel lookup
id: 1,
full_name: 'Ahmet Demir',
rank: 'Üsteğmen'
}),
Promise.resolve({ // Temporary goods manager lookup
id: slipData.goods_manager_id,
full_name: 'Mal Sorumlusu',
rank: 'Yüzbaşı'
})
]);
const vehicleInfo = vehicle ? {
brand: vehicle.brand,
model: vehicle.model,
plate: vehicle.plate,
year: vehicle.year
} : {
brand: 'Bilinmeyen',
model: 'Araç',
plate: 'Bilinmiyor',
year: 0
};
const personnelInfo = person ? {
full_name: person.full_name,
rank: person.rank
} : {
full_name: 'Bilinmeyen Personel',
rank: ''
};
const goodsManagerInfo = goodsManager ? {
full_name: goodsManager.full_name,
rank: goodsManager.rank
} : {
full_name: 'Bilinmeyen Mal Sorumlusu',
rank: ''
};
const fuelManagerInfo = { full_name: 'Yakıt Sorumlusu', rank: 'Yüzbaşı' };
const query = `
INSERT INTO fuel_slips (
date, force_command, unit_id, unit_name, vehicle_id, vehicle_info,
fuel_type, liters, km, personnel_id, personnel_info, goods_manager_id,
goods_manager_info, fuel_manager_id, fuel_manager_info, status, notes
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`;
const params = [
slipData.date,
slipData.force_command,
slipData.unit_id,
unit?.name || `Birim ${slipData.unit_id}`,
slipData.vehicle_id,
JSON.stringify(vehicleInfo),
slipData.fuel_type,
parseFloat(slipData.liters),
parseInt(slipData.km),
slipData.personnel_id,
JSON.stringify(personnelInfo),
slipData.goods_manager_id,
JSON.stringify(goodsManagerInfo),
slipData.fuel_manager_id,
JSON.stringify(fuelManagerInfo),
'pending',
slipData.notes || ''
];
db.run(query, params, function(err) {
if (err) {
console.error('Create fuel slip error:', err);
return res.status(500).json({ message: 'Veritabanı hatası.' });
}
const newSlip = {
id: this.lastID,
date: slipData.date,
force_command: slipData.force_command,
unit_id: slipData.unit_id,
unit_name: unit?.name || `Birim ${slipData.unit_id}`,
vehicle_id: slipData.vehicle_id,
vehicle_info: vehicleInfo,
fuel_type: slipData.fuel_type,
liters: parseFloat(slipData.liters),
km: parseInt(slipData.km),
personnel_id: slipData.personnel_id,
personnel_info: personnelInfo,
goods_manager_id: slipData.goods_manager_id,
goods_manager_info: goodsManagerInfo,
fuel_manager_id: slipData.fuel_manager_id,
fuel_manager_info: fuelManagerInfo,
status: 'pending',
notes: slipData.notes || '',
created_at: new Date().toISOString()
};
console.log('✅ Fuel slip created:', newSlip);
// Socket.IO ile mal sorumlusuna bildirim gönder
const socketData = {
goods_manager_id: newSlip.goods_manager_id,
fuel_slip_id: newSlip.id,
message: `${newSlip.vehicle_info.plate} plakalı araç için yeni yakıt fişi`
};
console.log('📢 Sending socket notification: fuel-slip-assigned', socketData);
io.emit('fuel-slip-assigned', socketData);
res.json({
message: 'Yakıt fişi başarıyla oluşturuldu.',
fuelSlip: newSlip
});
});
} catch (error) {
console.error('Create fuel slip error:', error);
res.status(500).json({ message: 'Sunucu hatası.' });
}
});
// PUT - Fiş durumunu güncelle (onay/reddet)
app.put('/api/fuel-slips', async (req, res) => {
try {
const { id, status, approval_notes } = req.body;
if (!id || !status) {
return res.status(400).json({ message: 'ID ve durum zorunludur.' });
}
if (!['approved', 'rejected'].includes(status)) {
return res.status(400).json({ message: 'Geçersiz durum.' });
}
const query = `
UPDATE fuel_slips
SET status = ?, approval_date = ?, approval_notes = ?
WHERE id = ?
`;
const params = [
status,
new Date().toISOString(),
approval_notes || '',
parseInt(id)
];
db.run(query, params, function(err) {
if (err) {
console.error('Update fuel slip error:', err);
return res.status(500).json({ message: 'Veritabanı hatası.' });
}
if (this.changes === 0) {
return res.status(404).json({ message: 'Fiş bulunamadı.' });
}
// Güncellenmiş fişi getir
db.get('SELECT * FROM fuel_slips WHERE id = ?', [id], (err, row) => {
if (err) {
console.error('Get updated fuel slip error:', err);
return res.status(500).json({ message: 'Veritabanı hatası.' });
}
const updatedSlip = {
...row,
vehicle_info: row.vehicle_info ? JSON.parse(row.vehicle_info) : null,
personnel_info: row.personnel_info ? JSON.parse(row.personnel_info) : null,
goods_manager_info: row.goods_manager_info ? JSON.parse(row.goods_manager_info) : null,
fuel_manager_info: row.fuel_manager_info ? JSON.parse(row.fuel_manager_info) : null
};
console.log('✅ Fuel slip updated:', updatedSlip);
// Socket.IO ile yakıt sorumlusuna bildirim gönder
const socketData = {
goods_manager_id: updatedSlip.goods_manager_id,
fuel_manager_id: updatedSlip.fuel_manager_id,
fuel_slip_id: updatedSlip.id,
status: updatedSlip.status,
approval_notes: updatedSlip.approval_notes
};
console.log('📢 Sending socket notification: fuel-slip-updated', socketData);
io.emit('fuel-slip-updated', socketData);
res.json({
message: `Fiş başarıyla ${status === 'approved' ? 'onaylandı' : 'reddedildi'}.`,
fuelSlip: updatedSlip
});
});
});
} catch (error) {
console.error('Update fuel slip error:', error);
res.status(500).json({ message: 'Sunucu hatası.' });
}
});
// DELETE - Fiş sil
app.delete('/api/fuel-slips', async (req, res) => {
try {
const { id } = req.body;
if (!id) {
return res.status(400).json({ message: 'Fiş ID zorunludur.' });
}
// Önce fişin durumunu kontrol et
db.get('SELECT status FROM fuel_slips WHERE id = ?', [id], (err, row) => {
if (err) {
console.error('Check fuel slip error:', err);
return res.status(500).json({ message: 'Veritabanı hatası.' });
}
if (!row) {
return res.status(404).json({ message: 'Fiş bulunamadı.' });
}
// Sadece pending olan fişler silinebilir
if (row.status !== 'pending') {
return res.status(400).json({ message: 'Sadece bekleyen fişler silinebilir.' });
}
// Fiş sil
db.run('DELETE FROM fuel_slips WHERE id = ?', [id], function(err) {
if (err) {
console.error('Delete fuel slip error:', err);
return res.status(500).json({ message: 'Veritabanı hatası.' });
}
if (this.changes === 0) {
return res.status(404).json({ message: 'Fiş bulunamadı.' });
}
res.json({
message: 'Fiş başarıyla silindi.',
fuelSlipId: parseInt(id)
});
});
});
} catch (error) {
console.error('Delete fuel slip error:', error);
res.status(500).json({ message: 'Sunucu hatası.' });
}
});
// Vehicles API endpoint'leri
// GET - Tüm araçları listele
app.get('/api/vehicles', (req, res) => {
// Yetki kontrolü (temporary - will be implemented with proper session)
res.json({ vehicles });
});
// POST - Yeni araç ekle
app.post('/api/vehicles', (req, res) => {
// Yetki kontrolü (temporary - will be implemented with proper session)
try {
const {
brand,
model,
year,
plate,
fuel_type,
fuel_capacity,
current_fuel,
yazlik_mukannen = 0,
kislik_mukannen = 0,
status = 'Aktif',
driver_id,
driver_name
} = req.body;
// Validasyon
if (!brand || !model || !year || !plate || !fuel_type || !fuel_capacity) {
return res.status(400).json({ message: 'Tüm zorunlu alanları doldurun.' });
}
if (year < 1990 || year > new Date().getFullYear() + 1) {
return res.status(400).json({ message: 'Geçerli bir yıl girin.' });
}
if (current_fuel < 0 || current_fuel > fuel_capacity) {
return res.status(400).json({ message: 'Mevcut yakıt değeri geçersiz.' });
}
// Plaka tekrar kontrolü
const existingVehicle = vehicles.find(v =>
v.plate.toLowerCase() === plate.toLowerCase()
);
if (existingVehicle) {
return res.status(400).json({ message: 'Bu plaka zaten kayıtlı.' });
}
// Yeni araç oluştur
const newVehicle = {
id: nextVehicleId++,
brand: brand.trim(),
model: model.trim(),
year: parseInt(year),
plate: plate.trim().toUpperCase(),
fuel_type: fuel_type.trim(),
fuel_capacity: parseInt(fuel_capacity),
current_fuel: parseInt(current_fuel) || 0,
yazlik_mukannen: parseInt(yazlik_mukannen) || 0,
kislik_mukannen: parseInt(kislik_mukannen) || 0,
status: status.trim(),
driver_id: driver_id ? parseInt(driver_id) : null,
driver_name: driver_name ? driver_name.trim() : null,
created_at: new Date().toISOString()
};
vehicles.push(newVehicle);
res.json({
message: 'Araç başarıyla eklendi.',
vehicle: newVehicle
});
} catch (error) {
res.status(500).json({ message: 'Sunucu hatası.' });
}
});
// PUT - Araç güncelle
app.put('/api/vehicles', (req, res) => {
// Yetki kontrolü (temporary - will be implemented with proper session)
try {
const {
id,
brand,
model,
year,
plate,
fuel_type,
fuel_capacity,
current_fuel,
yazlik_mukannen,
kislik_mukannen,
status,
driver_id,
driver_name
} = req.body;
// Validasyon
if (!id || !brand || !model || !year || !plate || !fuel_type || !fuel_capacity) {
return res.status(400).json({ message: 'Tüm zorunlu alanları doldurun.' });
}
if (year < 1990 || year > new Date().getFullYear() + 1) {
return res.status(400).json({ message: 'Geçerli bir yıl girin.' });
}
if (current_fuel < 0 || current_fuel > fuel_capacity) {
return res.status(400).json({ message: 'Mevcut yakıt değeri geçersiz.' });
}
// Araç bul
const vehicleIndex = vehicles.findIndex(v => v.id === parseInt(id));
if (vehicleIndex === -1) {
return res.status(404).json({ message: 'Araç bulunamadı.' });
}
// Plaka tekrar kontrolü (diğer araçlar için)
const existingVehicle = vehicles.find(v =>
v.id !== parseInt(id) && v.plate.toLowerCase() === plate.toLowerCase()
);
if (existingVehicle) {
return res.status(400).json({ message: 'Bu plaka başka bir araçta kullanılıyor.' });
}
// Araç güncelle
vehicles[vehicleIndex] = {
...vehicles[vehicleIndex],
brand: brand.trim(),
model: model.trim(),
year: parseInt(year),
plate: plate.trim().toUpperCase(),
fuel_type: fuel_type.trim(),
fuel_capacity: parseInt(fuel_capacity),
current_fuel: parseInt(current_fuel) || 0,
yazlik_mukannen: parseInt(yazlik_mukannen) || 0,
kislik_mukannen: parseInt(kislik_mukannen) || 0,
status: (status || 'Aktif').trim(),
driver_id: driver_id ? parseInt(driver_id) : null,
driver_name: driver_name ? driver_name.trim() : null
};
res.json({
message: 'Araç başarıyla güncellendi.',
vehicle: vehicles[vehicleIndex]
});
} catch (error) {
console.error('❌ PUT /api/vehicles error:', error);
console.error('Error details:', {
message: error.message,
stack: error.stack,
body: req.body
});
res.status(500).json({ message: 'Sunucu hatası.' });
}
});
// DELETE - Araç sil
app.delete('/api/vehicles', (req, res) => {
// Yetki kontrolü (temporary - will be implemented with proper session)
try {
const { id } = req.body;
if (!id) {
return res.status(400).json({ message: 'Araç ID zorunludur.' });
}
// Araç bul
const vehicleIndex = vehicles.findIndex(v => v.id === parseInt(id));
if (vehicleIndex === -1) {
return res.status(404).json({ message: 'Araç bulunamadı.' });
}
// Araç sil
const deletedVehicle = vehicles.splice(vehicleIndex, 1)[0];
res.json({
message: 'Araç başarıyla silindi.',
vehicle: deletedVehicle
});
} catch (error) {
res.status(500).json({ message: 'Sunucu hatası.' });
}
});
// Socket.IO bağlantıları
io.on('connection', (socket) => {
console.log('✅ Bir kullanıcı bağlandı:', socket.id);
// API'den gelen event'ları dinle ve broadcast et
socket.on('api-fuel-slip-assigned', (data) => {
console.log('📢 API event received: api-fuel-slip-assigned, broadcasting to all clients', data);
io.emit('fuel-slip-assigned', data);
});
socket.on('api-fuel-slip-updated', (data) => {
console.log('📢 API event received: api-fuel-slip-updated, broadcasting to all clients', data);
io.emit('fuel-slip-updated', data);
});
socket.on('disconnect', () => {
console.log('❌ Bir kullanıcı ayrıldı:', socket.id);
});
});
// API server only - frontend handled by SvelteKit dev server in development
// In production, the build will be served differently
// Sunucuyu başlat
async function startServer() {
try {
// Ensure database directory exists first
await ensureDbDirectory();
console.log(`📄 Database file path: ${dbPath}`);
// Create database connection
await createDatabaseConnection();
// Initialize database and tables
await initializeDatabase();
server.listen(PORT, () => {
console.log(`🚀 Sunucu http://localhost:${PORT} adresinde çalışıyor`);
console.log(`📱 Socket.IO sunucusu aktif`);
console.log(`💾 Veritabanı: ${dbPath}`);
});
} catch (error) {
console.error('❌ Sunucu başlatılamadı:', error);
process.exit(1);
}
}
startServer();