Initial commit: Yakıt Takip Modülü - Akaryakıt İstasyonu Yönetim Sistemi

🚀 Features Implemented:
- Full-stack SvelteKit application with Express backend
- Role-based authentication (Admin, Fuel Manager, Goods Manager)
- Real-time notifications with Socket.IO
- SQLite database with auto-initialization in /db directory
- Comprehensive user management and fuel slip tracking
- Responsive design with Turkish language support

🏗️ Architecture:
- Frontend: Svelte + SvelteKit + Vite
- Backend: Node.js + Express + Socket.IO
- Database: SQLite3 with automatic schema creation
- Security: bcrypt password hashing + session management
- Real-time: Socket.IO for instant notifications

📁 Project Structure:
- Organized documentation in /docs directory
- Database files in /db directory with auto-setup
- Clean separation of API routes and UI components
- Comprehensive documentation including processes, architecture, and user guides

📚 Documentation:
- PROJECT_PROCESSES.md: Comprehensive project documentation
- KNOWLEDGE_BASE.md: Quick reference and user guide
- TEST_GUIDE.md: Testing and quality assurance guide
- README_ADMIN_FEATURES.md: Admin functionality guide
- Full API documentation and system architecture

🔒 Security Features:
- Role-based authorization system
- Encrypted password storage with bcrypt
- Session-based authentication
- SQL injection protection with parameterized queries
- CORS configuration and input validation

🎯 Key Features:
- Fuel slip creation and approval workflow
- Real-time notifications between users
- PDF generation for fuel slips
- User and vehicle management
- Comprehensive audit logging

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-05 08:35:13 +03:00
commit 4205a8d387
31 changed files with 11678 additions and 0 deletions

231
src/server.js Normal file
View File

@@ -0,0 +1,231 @@
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';
const app = express();
const server = createServer(app);
const io = new Server(server, {
cors: {
origin: "http://localhost:5173",
methods: ["GET", "POST"]
}
});
const PORT = process.env.PORT || 3000;
// ES Module equivalent of __dirname
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
// Database path setup - ensure /db directory exists and use it
const projectRoot = dirname(__dirname); // Go up from src/ to project root
const dbDir = join(projectRoot, 'db');
const dbPath = join(dbDir, 'yakit_takip.db');
// Middleware
app.use(express.json());
app.use(express.static('build'));
// Session middleware
app.use(session({
secret: 'yakit-takip-modulu-secret-key-2023',
resave: false,
saveUninitialized: false,
cookie: {
secure: false, // development için false
maxAge: 24 * 60 * 60 * 1000 // 24 saat
}
}));
// Ensure /db directory exists
async function ensureDbDirectory() {
try {
await fs.mkdir(dbDir, { recursive: true });
console.log(`📁 Database directory ensured: ${dbDir}`);
} catch (error) {
console.error('❌ Error creating database directory:', error);
throw error;
}
}
// Veritabanı bağlantısı
const db = new sqlite3.Database(dbPath);
// 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' }
];
// 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
req.session.user = {
id: user.id,
username: user.username,
role: user.role,
full_name: user.full_name
};
res.json({
message: 'Giriş başarılı.',
user: {
id: user.id,
username: user.username,
role: user.role,
full_name: user.full_name
}
});
});
} 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
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 });
});
});
// Socket.IO bağlantıları
io.on('connection', (socket) => {
console.log('Bir kullanıcı bağlandı:', socket.id);
socket.on('disconnect', () => {
console.log('Bir kullanıcı ayrıldı:', socket.id);
});
});
// SvelteKit için tüm route'ları handle et
app.use('*', (req, res) => {
res.sendFile('build/index.html', { root: '.' });
});
// Sunucuyu başlat
async function startServer() {
try {
// Ensure database directory exists first
await ensureDbDirectory();
console.log(`📄 Database file path: ${dbPath}`);
// Initialize database and tables
await initializeDatabase();
server.listen(PORT, () => {
console.log(`🚀 Sunucu http://localhost:${PORT} adresinde çalışıyor`);
console.log(`📱 Socket.IO sunucusu aktif`);
console.log(`💾 Veritabanı: ${dbPath}`);
});
} catch (error) {
console.error('❌ Sunucu başlatılamadı:', error);
process.exit(1);
}
}
startServer();