Files
API/api.js
Zombori Péter 568d3eb513 Alap API
Az alap api implementációja, ami kiszolgálja a kéréseket
2025-10-08 20:27:07 +02:00

248 lines
7.8 KiB
JavaScript

import express from 'express';
import dotenv from 'dotenv';
import crypto from 'crypto';
import mysql from 'mysql2/promise';
dotenv.config();
const {
APP_PORT = 3000,
DB_HOST = 'localhost',
DB_PORT = 3306,
DB_NAME = 'telefonkonyv',
DB_USER = 'appuser',
DB_PASSWORD = 'apppass',
} = process.env;
const app = express();
app.use(express.json());
// --- DB pool ---
const pool = mysql.createPool({
host: DB_HOST,
port: Number(DB_PORT),
user: DB_USER,
password: DB_PASSWORD,
database: DB_NAME,
waitForConnections: true,
connectionLimit: 10,
queueLimit: 0,
});
// --- Helper: SHA-256 hash ---
function sha256(text) {
return crypto.createHash('sha256').update(String(text)).digest('hex');
}
// --- Middleware: auth ---
// A body-ban jön: { uname, pw }
async function auth(req, res, next) {
try {
const { authUname, authPw } = req.body || {};
if (!authUname || !authPw) {
return res.status(401).json({ ok: false, error: 'Missing authUname or authPw' });
}
const hashed = sha256(authPw);
const [rows] = await pool.query(
'SELECT id, uname, admin FROM users WHERE uname = ? AND pw = ? LIMIT 1',
[authUname, hashed]
);
if (!rows.length) return res.status(401).json({ ok: false, error: 'Invalid credentials' });
req.user = rows[0];
next();
} catch (e) {
console.error('auth error:', e);
res.status(500).json({ ok: false, error: 'Auth failed' });
}
}
// --- Middleware: adminCheck ---
function adminCheck(req, res, next) {
if (!req.user?.admin) {
return res.status(403).json({ ok: false, error: 'Admin required' });
}
next();
}
// --- Root ---
app.get('/', (req, res) => {
res.json({ status: 'ok' });
});
// --- /auth ---
// POST { uname, pw } -> { ok, admin, userId }
app.post('/auth', async (req, res) => {
try {
const { authUname, authPw } = req.body || {};
if (!authUname || !authPw) {
return res.status(400).json({ ok: false, error: 'Missing authUname or authPw' });
}
const hashed = sha256(authPw);
const [rows] = await pool.query(
'SELECT id, admin FROM users WHERE uname = ? AND pw = ? LIMIT 1',
[authUname, hashed]
);
if (!rows.length) return res.json({ ok: false, admin: false });
res.json({ ok: true, admin: !!rows[0].admin, userId: rows[0].id });
} catch (e) {
console.error('/auth error:', e);
res.status(500).json({ ok: false, error: 'Auth error' });
}
});
// --- /users (admin only) ---
app.get('/users', auth, adminCheck, async (req, res) => {
try {
const [rows] = await pool.query(
'SELECT id, uname, admin, note FROM users ORDER BY id ASC'
);
res.json({ ok: true, users: rows });
} catch (err) {
console.error('GET /users error:', err);
res.status(500).json({ ok: false, error: 'DB error' });
}
});
// POST: { uname, pw, admin?, note? }
app.post('/users', auth, adminCheck, async (req, res) => {
try {
const { uname, pw, admin = false, note = null } = req.body || {};
if (!uname || !pw) {
return res.status(400).json({ ok: false, error: 'uname and pw required' });
}
const hashed = sha256(pw);
const [result] = await pool.query(
'INSERT INTO users (uname, pw, admin, note) VALUES (?, ?, ?, ?)',
[uname, hashed, !!admin, note]
);
res.status(201).json({ ok: true, id: result.insertId });
} catch (err) {
console.error('POST /users error:', err);
res.status(500).json({ ok: false, error: 'DB error' });
}
});
// PATCH: body tartalmazza az id-t és a módosítandó mezőket
// Pl.: { id, uname?, pw?(plain), admin?, note? }
app.patch('/users', auth, adminCheck, async (req, res) => {
try {
const { id, uname, pw, admin, note } = req.body || {};
if (!id) return res.status(400).json({ ok: false, error: 'id required' });
// Olvassuk ki az aktuális rekordot
const [rows] = await pool.query('SELECT * FROM users WHERE id = ? LIMIT 1', [id]);
if (!rows.length) return res.status(404).json({ ok: false, error: 'User not found' });
const current = rows[0];
const newUname = uname ?? current.uname;
const newPw = pw ? sha256(pw) : current.pw;
const newAdmin = (admin === undefined ? current.admin : !!admin);
const newNote = (note === undefined ? current.note : note);
await pool.query(
'UPDATE users SET uname = ?, pw = ?, admin = ?, note = ? WHERE id = ?',
[newUname, newPw, newAdmin, newNote, id]
);
res.json({ ok: true });
} catch (err) {
console.error('PATCH /users error:', err);
res.status(500).json({ ok: false, error: 'DB error' });
}
});
// DELETE: { id }
app.delete('/users', auth, adminCheck, async (req, res) => {
try {
const { id } = req.body || {};
if (!id) return res.status(400).json({ ok: false, error: 'id required' });
const [result] = await pool.query('DELETE FROM users WHERE id = ?', [id]);
res.json({ ok: true, deleted: result.affectedRows });
} catch (err) {
console.error('DELETE /users error:', err);
res.status(500).json({ ok: false, error: 'DB error' });
}
});
// --- /contacts (auth required) ---
// GET: listázás
app.get('/contacts', auth, async (req, res) => {
try {
const [rows] = await pool.query(
'SELECT id, name, phone, address, note FROM contacts ORDER BY id ASC'
);
res.json({ ok: true, contacts: rows });
} catch (err) {
console.error('GET /contacts error:', err);
res.status(500).json({ ok: false, error: 'DB error' });
}
});
// POST: { name, phone, address?, note? } — elfogadja a "phome"-ot is
app.post('/contacts', auth, async (req, res) => {
try {
const { name, phone: rawPhone, phome, address = null, note = null } = req.body || {};
const phone = rawPhone ?? phome; // ha véletlen "phome" érkezik
if (!name || !phone) {
return res.status(400).json({ ok: false, error: 'name and phone required' });
}
const [result] = await pool.query(
'INSERT INTO contacts (name, phone, address, note) VALUES (?, ?, ?, ?)',
[name, phone, address, note]
);
res.status(201).json({ ok: true, id: result.insertId });
} catch (err) {
console.error('POST /contacts error:', err);
res.status(500).json({ ok: false, error: 'DB error' });
}
});
// PATCH: { id, name?, phone?/phome?, address?, note? }
app.patch('/contacts', auth, async (req, res) => {
try {
const { id, name, phone: rawPhone, phome, address, note } = req.body || {};
if (!id) return res.status(400).json({ ok: false, error: 'id required' });
const [rows] = await pool.query('SELECT * FROM contacts WHERE id = ? LIMIT 1', [id]);
if (!rows.length) return res.status(404).json({ ok: false, error: 'Contact not found' });
const current = rows[0];
const phone = (rawPhone ?? phome);
const newName = name ?? current.name;
const newPhone = (phone === undefined ? current.phone : phone);
const newAddress = (address === undefined ? current.address : address);
const newNote = (note === undefined ? current.note : note);
await pool.query(
'UPDATE contacts SET name = ?, phone = ?, address = ?, note = ? WHERE id = ?',
[newName, newPhone, newAddress, newNote, id]
);
res.json({ ok: true });
} catch (err) {
console.error('PATCH /contacts error:', err);
res.status(500).json({ ok: false, error: 'DB error' });
}
});
// DELETE: { id }
app.delete('/contacts', auth, async (req, res) => {
try {
const { id } = req.body || {};
if (!id) return res.status(400).json({ ok: false, error: 'id required' });
const [result] = await pool.query('DELETE FROM contacts WHERE id = ?', [id]);
res.json({ ok: true, deleted: result.affectedRows });
} catch (err) {
console.error('DELETE /contacts error:', err);
res.status(500).json({ ok: false, error: 'DB error' });
}
});
// --- Start ---
app.listen(APP_PORT, () => {
console.log(`API listening on port ${APP_PORT}`);
});