fixed
This commit is contained in:
1
.serena/.gitignore
vendored
Normal file
1
.serena/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/cache
|
||||||
84
.serena/project.yml
Normal file
84
.serena/project.yml
Normal 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: []
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
KNOWLEDGE_BASE.md'deki iş akışlarına göre implement edilen tam fonksiyonel yakıt takip sistemi.
|
KNOWLEDGE_BASE.md'deki iş akışlarına göre implement edilen tam fonksiyonel yakıt takip sistemi.
|
||||||
|
|
||||||
### 🚀 **Sunucu Durumu**
|
### 🚀 **Sunucu Durumu**
|
||||||
- **Frontend**: `http://localhost:5174/` (SvelteKit)
|
- **Frontend**: `http://localhost:5173/` (SvelteKit)
|
||||||
- **Backend**: `http://localhost:3000` (Express/Socket.IO)
|
- **Backend**: `http://localhost:3000` (Express/Socket.IO)
|
||||||
- **Status**: ✅ Çalışıyor
|
- **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
|
#### 1.1 Giriş Testi
|
||||||
```bash
|
```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
|
✓ Adım 2: "admin" / "admin123" ile giriş yap
|
||||||
✓ Beklenen: /dashboard sayfasına yönlendirme
|
✓ Beklenen: /dashboard sayfasına yönlendirme
|
||||||
✓ Kontrol: Sol menüde "Yönetim Paneli" başlığı
|
✓ Kontrol: Sol menüde "Yönetim Paneli" başlığı
|
||||||
|
|||||||
@@ -9,7 +9,8 @@
|
|||||||
"client": "vite dev",
|
"client": "vite dev",
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
"preview": "vite preview",
|
"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);\"",
|
"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)\""
|
"init-db": "node -e \"import('./src/server.js').catch(console.error)\""
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
|
|
||||||
console.log('🔌 Connecting to Socket.IO...');
|
console.log('🔌 Connecting to Socket.IO...');
|
||||||
// Socket.IO bağlantısı
|
// Socket.IO bağlantısı
|
||||||
socket = io('http://localhost:3000');
|
socket = io('http://localhost:3001');
|
||||||
|
|
||||||
console.log('👤 Goods Manager ID:', user.id);
|
console.log('👤 Goods Manager ID:', user.id);
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ let socket = null;
|
|||||||
|
|
||||||
export function getSocketClient() {
|
export function getSocketClient() {
|
||||||
if (!socket) {
|
if (!socket) {
|
||||||
socket = io('http://localhost:3000', {
|
socket = io('http://localhost:3001', {
|
||||||
transports: ['websocket', 'polling']
|
transports: ['websocket', 'polling']
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
630
src/production-server.js
Normal file
630
src/production-server.js
Normal 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();
|
||||||
592
src/server.js
592
src/server.js
@@ -65,6 +65,54 @@ let units = [
|
|||||||
|
|
||||||
let nextUnitId = 4;
|
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 app = express();
|
||||||
const server = createServer(app);
|
const server = createServer(app);
|
||||||
const io = new Server(server, {
|
const io = new Server(server, {
|
||||||
@@ -77,7 +125,7 @@ const io = new Server(server, {
|
|||||||
// Export io for use in other modules
|
// Export io for use in other modules
|
||||||
export { io };
|
export { io };
|
||||||
|
|
||||||
const PORT = process.env.PORT || 3002;
|
const PORT = process.env.PORT || 3001;
|
||||||
|
|
||||||
// ES Module equivalent of __dirname
|
// ES Module equivalent of __dirname
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
@@ -90,7 +138,7 @@ const dbPath = join(dbDir, 'yakit_takip.db');
|
|||||||
|
|
||||||
// Middleware
|
// Middleware
|
||||||
app.use(express.json());
|
app.use(express.json());
|
||||||
app.use(express.static('build'));
|
// Note: static file serving removed - SvelteKit handles frontend in development
|
||||||
|
|
||||||
// Session middleware
|
// Session middleware
|
||||||
app.use(session({
|
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ı
|
// Socket.IO bağlantıları
|
||||||
io.on('connection', (socket) => {
|
io.on('connection', (socket) => {
|
||||||
console.log('✅ Bir kullanıcı bağlandı:', socket.id);
|
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
|
// API server only - frontend handled by SvelteKit dev server in development
|
||||||
app.use('*', (req, res) => {
|
// In production, the build will be served differently
|
||||||
res.sendFile('build/index.html', { root: '.' });
|
|
||||||
});
|
|
||||||
|
|
||||||
// Sunucuyu başlat
|
// Sunucuyu başlat
|
||||||
async function startServer() {
|
async function startServer() {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ export default defineConfig({
|
|||||||
port: 5173,
|
port: 5173,
|
||||||
proxy: {
|
proxy: {
|
||||||
'/api': {
|
'/api': {
|
||||||
target: 'http://localhost:3002',
|
target: 'http://localhost:3001',
|
||||||
changeOrigin: true
|
changeOrigin: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user