This commit is contained in:
2025-11-07 00:53:00 +03:00
parent c9abe6cc55
commit 673d53f126
9 changed files with 1308 additions and 12 deletions

View File

@@ -29,7 +29,7 @@
console.log('🔌 Connecting to Socket.IO...');
// Socket.IO bağlantısı
socket = io('http://localhost:3000');
socket = io('http://localhost:3001');
console.log('👤 Goods Manager ID:', user.id);

View File

@@ -4,7 +4,7 @@ let socket = null;
export function getSocketClient() {
if (!socket) {
socket = io('http://localhost:3000', {
socket = io('http://localhost:3001', {
transports: ['websocket', 'polling']
});

630
src/production-server.js Normal file
View File

@@ -0,0 +1,630 @@
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';
// Import the handler from the built SvelteKit app
import { handler } from '../build/handler.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;
const app = express();
const server = createServer(app);
const io = new Server(server, {
cors: {
origin: process.env.NODE_ENV === 'production' ? false : ["http://localhost:5173"],
methods: ["GET", "POST"]
}
});
// Export io for use in other modules
export { io };
const PORT = process.env.PORT || 3001;
// 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());
// Session middleware
app.use(session({
secret: 'yakit-takip-modulu-secret-key-2023',
resave: false,
saveUninitialized: false,
cookie: {
secure: process.env.NODE_ENV === 'production',
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);
});
// Ö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' }
];
// 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);
});
// Her kullanıcıyı kontrol et ve yoksa ekle
users.forEach(async (user) => {
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();
});
});
}
// 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
app.get('/api/units', (req, res) => {
res.json({ units });
});
app.post('/api/units', (req, res) => {
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 } = commander;
if (!full_name || !rank || !registration_number || !tc_kimlik || !phone) {
return res.status(400).json({ message: 'Birlik 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()
},
created_at: new Date().toISOString()
};
units.push(newUnit);
res.json({
message: 'Birlik başarıyla eklendi.',
unit: newUnit
});
} catch (error) {
res.status(500).json({ message: 'Sunucu hatası.' });
}
});
app.put('/api/units', (req, res) => {
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 } = commander;
if (!full_name || !rank || !registration_number || !tc_kimlik || !phone) {
return res.status(400).json({ message: 'Birlik 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
units[unitIndex] = {
...units[unitIndex],
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()
}
};
res.json({
message: 'Birlik başarıyla güncellendi.',
unit: units[unitIndex]
});
} catch (error) {
res.status(500).json({ message: 'Sunucu hatası.' });
}
});
app.delete('/api/units', (req, res) => {
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
app.get('/api/goods-managers', (req, res) => {
try {
const goodsManagers = getGoodsManagers();
res.json({ goodsManagers });
} catch (error) {
res.status(500).json({ message: 'Mal sorumluları alınırken hata oluştu.' });
}
});
app.post('/api/goods-managers', async (req, res) => {
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ı.' });
}
});
app.put('/api/goods-managers', async (req, res) => {
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ı.' });
}
});
app.delete('/api/goods-managers', async (req, res) => {
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ı.' });
}
});
// 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);
});
});
// Let SvelteKit handle everything else
app.use(handler);
// 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(`🚀 Production server 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();

View File

@@ -65,6 +65,54 @@ let units = [
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;
const app = express();
const server = createServer(app);
const io = new Server(server, {
@@ -77,7 +125,7 @@ const io = new Server(server, {
// Export io for use in other modules
export { io };
const PORT = process.env.PORT || 3002;
const PORT = process.env.PORT || 3001;
// ES Module equivalent of __dirname
const __filename = fileURLToPath(import.meta.url);
@@ -90,7 +138,7 @@ const dbPath = join(dbDir, 'yakit_takip.db');
// Middleware
app.use(express.json());
app.use(express.static('build'));
// Note: static file serving removed - SvelteKit handles frontend in development
// Session middleware
app.use(session({
@@ -602,6 +650,540 @@ app.delete('/api/goods-managers', async (req, res) => {
}
});
// 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)
const 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()
}
];
res.json({ fuelPersonnel });
});
// 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,
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,
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,
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,
status: status.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) {
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);
@@ -622,10 +1204,8 @@ io.on('connection', (socket) => {
});
});
// SvelteKit için tüm route'ları handle et
app.use('*', (req, res) => {
res.sendFile('build/index.html', { root: '.' });
});
// 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() {