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

1
.serena/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/cache

84
.serena/project.yml Normal file
View File

@@ -0,0 +1,84 @@
# list of languages for which language servers are started; choose from:
# al bash clojure cpp csharp csharp_omnisharp
# dart elixir elm erlang fortran go
# haskell java julia kotlin lua markdown
# nix perl php python python_jedi r
# rego ruby ruby_solargraph rust scala swift
# terraform typescript typescript_vts zig
# Note:
# - For C, use cpp
# - For JavaScript, use typescript
# Special requirements:
# - csharp: Requires the presence of a .sln file in the project folder.
# When using multiple languages, the first language server that supports a given file will be used for that file.
# The first language is the default language and the respective language server will be used as a fallback.
# Note that when using the JetBrains backend, language servers are not used and this list is correspondingly ignored.
languages:
- typescript
# the encoding used by text files in the project
# For a list of possible encodings, see https://docs.python.org/3.11/library/codecs.html#standard-encodings
encoding: "utf-8"
# whether to use the project's gitignore file to ignore files
# Added on 2025-04-07
ignore_all_files_in_gitignore: true
# list of additional paths to ignore
# same syntax as gitignore, so you can use * and **
# Was previously called `ignored_dirs`, please update your config if you are using that.
# Added (renamed) on 2025-04-07
ignored_paths: []
# whether the project is in read-only mode
# If set to true, all editing tools will be disabled and attempts to use them will result in an error
# Added on 2025-04-18
read_only: false
# list of tool names to exclude. We recommend not excluding any tools, see the readme for more details.
# Below is the complete list of tools for convenience.
# To make sure you have the latest list of tools, and to view their descriptions,
# execute `uv run scripts/print_tool_overview.py`.
#
# * `activate_project`: Activates a project by name.
# * `check_onboarding_performed`: Checks whether project onboarding was already performed.
# * `create_text_file`: Creates/overwrites a file in the project directory.
# * `delete_lines`: Deletes a range of lines within a file.
# * `delete_memory`: Deletes a memory from Serena's project-specific memory store.
# * `execute_shell_command`: Executes a shell command.
# * `find_referencing_code_snippets`: Finds code snippets in which the symbol at the given location is referenced.
# * `find_referencing_symbols`: Finds symbols that reference the symbol at the given location (optionally filtered by type).
# * `find_symbol`: Performs a global (or local) search for symbols with/containing a given name/substring (optionally filtered by type).
# * `get_current_config`: Prints the current configuration of the agent, including the active and available projects, tools, contexts, and modes.
# * `get_symbols_overview`: Gets an overview of the top-level symbols defined in a given file.
# * `initial_instructions`: Gets the initial instructions for the current project.
# Should only be used in settings where the system prompt cannot be set,
# e.g. in clients you have no control over, like Claude Desktop.
# * `insert_after_symbol`: Inserts content after the end of the definition of a given symbol.
# * `insert_at_line`: Inserts content at a given line in a file.
# * `insert_before_symbol`: Inserts content before the beginning of the definition of a given symbol.
# * `list_dir`: Lists files and directories in the given directory (optionally with recursion).
# * `list_memories`: Lists memories in Serena's project-specific memory store.
# * `onboarding`: Performs onboarding (identifying the project structure and essential tasks, e.g. for testing or building).
# * `prepare_for_new_conversation`: Provides instructions for preparing for a new conversation (in order to continue with the necessary context).
# * `read_file`: Reads a file within the project directory.
# * `read_memory`: Reads the memory with the given name from Serena's project-specific memory store.
# * `remove_project`: Removes a project from the Serena configuration.
# * `replace_lines`: Replaces a range of lines within a file with new content.
# * `replace_symbol_body`: Replaces the full definition of a symbol.
# * `restart_language_server`: Restarts the language server, may be necessary when edits not through Serena happen.
# * `search_for_pattern`: Performs a search for a pattern in the project.
# * `summarize_changes`: Provides instructions for summarizing the changes made to the codebase.
# * `switch_modes`: Activates modes by providing a list of their names
# * `think_about_collected_information`: Thinking tool for pondering the completeness of collected information.
# * `think_about_task_adherence`: Thinking tool for determining whether the agent is still on track with the current task.
# * `think_about_whether_you_are_done`: Thinking tool for determining whether the task is truly completed.
# * `write_memory`: Writes a named memory (for future reference) to Serena's project-specific memory store.
excluded_tools: []
# initial prompt for the project. It will always be given to the LLM upon activating the project
# (contrary to the memories, which are loaded on demand).
initial_prompt: ""
project_name: "ytp-glm"
included_optional_tools: []

View File

@@ -5,7 +5,7 @@
KNOWLEDGE_BASE.md'deki iş akışlarına göre implement edilen tam fonksiyonel yakıt takip sistemi.
### 🚀 **Sunucu Durumu**
- **Frontend**: `http://localhost:5174/` (SvelteKit)
- **Frontend**: `http://localhost:5173/` (SvelteKit)
- **Backend**: `http://localhost:3000` (Express/Socket.IO)
- **Status**: ✅ Çalışıyor
@@ -23,7 +23,7 @@ KNOWLEDGE_BASE.md'deki iş akışlarına göre implement edilen tam fonksiyonel
#### 1.1 Giriş Testi
```bash
✓ Adım 1: http://localhost:5174/ adresine git
✓ Adım 1: http://localhost:5173/ adresine git
✓ Adım 2: "admin" / "admin123" ile giriş yap
✓ Beklenen: /dashboard sayfasına yönlendirme
✓ Kontrol: Sol menüde "Yönetim Paneli" başlığı

View File

@@ -9,7 +9,8 @@
"client": "vite dev",
"build": "vite build",
"preview": "vite preview",
"start": "node src/server.js",
"start": "node src/production-server.js",
"prod": "npm run build && npm run start",
"setup": "node -e \"import('./src/server.js').then(() => console.log('Database setup completed')).catch(console.error);\"",
"init-db": "node -e \"import('./src/server.js').catch(console.error)\""
},

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() {

View File

@@ -7,7 +7,7 @@ export default defineConfig({
port: 5173,
proxy: {
'/api': {
target: 'http://localhost:3002',
target: 'http://localhost:3001',
changeOrigin: true
}
}