251 lines
7.8 KiB
JavaScript
251 lines
7.8 KiB
JavaScript
import express from 'express';
|
|
import dotenv from 'dotenv';
|
|
import crypto from 'crypto';
|
|
import mysql from 'mysql2/promise';
|
|
import cors from 'cors';
|
|
|
|
|
|
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());
|
|
app.use(cors())
|
|
|
|
// --- 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}`);
|
|
});
|