1519 lines
40 KiB
JavaScript
1519 lines
40 KiB
JavaScript
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(); |