// Import necessary modules const express = require('express'); const cors = require('cors'); const admin = require('firebase-admin'); const mysql = require('mysql'); const dotenv = require('dotenv'); const util = require('util'); // Load environment variables from .env file dotenv.config(); // Firebase Admin SDK configuration const serviceAccount = require('./config/serviceAccountKey.json'); // Update path if needed admin.initializeApp({ credential: admin.credential.cert(serviceAccount) }); // MySQL database connection setup const db = mysql.createConnection({ host: process.env.MYSQL_HOST, user: process.env.MYSQL_USER, password: process.env.MYSQL_PASSWORD, database: process.env.MYSQL_DATABASE, }); // Alternative local config (commented) // const db = mysql.createConnection({ // host: "localhost", // user: "root", // password: "", // database:"iskalnik_dztps" // }); db.connect((err) => { if (err) { console.error('MySQL connection failed:', err); } else { console.log('Connected to MySQL database'); } }); // --- Promise helpers for mysql (node-mysql, not mysql2) --- const query = util.promisify(db.query).bind(db); const beginTransaction = util.promisify(db.beginTransaction).bind(db); const commit = util.promisify(db.commit).bind(db); const rollback = util.promisify(db.rollback).bind(db); // Express application const app = express(); // Middleware app.use(cors()); app.use(express.json()); // Firebase authentication middleware const authenticateFirebaseToken = async (req, res, next) => { try { const { authorization } = req.headers; if (!authorization || !authorization.startsWith('Bearer ')) { return res.status(401).json({ message: 'Unauthorized' }); } const idToken = authorization.split('Bearer ')[1]; const decodedToken = await admin.auth().verifyIdToken(idToken); req.uid = decodedToken.uid; next(); } catch (error) { console.error('Error authenticating Firebase token:', error); return res.status(401).json({ message: 'Unauthorized' }); } }; // Protected route example app.get('/express', authenticateFirebaseToken, (req, res) => { res.json({ message: 'Protected route accessed successfully' }); }); // Start the Express server const PORT = process.env.PORT || 3000; app.listen(PORT, () => { console.log(`Server is running on port ${PORT}`); }); /********************************** * clan *********************************/ function formClanStruct(from, id = null) { if (!id) id = from.id; return { id, id_izkaznica: from.id_izkaznica || "", ime: from.ime || "", priimek: from.priimek || "", spol: from.spol || "", datum_rojstva: from.datum_rojstva || "", kraj_rojstva: from.kraj_rojstva || "", drzavljanstvo: from.drzavljanstvo || "", solska_izobrazba: from.solska_izobrazba || "", telefondoma: from.telefondoma || "", telefonsluzba: from.telefonsluzba || "", telefonmobi: from.telefonmobi || "", email: from.email || "", spletnastran: from.spletnastran || "", naslovbivalisca: from.naslovbivalisca || "", ulica: from.ulica || "", postna_stevilka: from.postna_stevilka || "", posta: from.posta || "", osnovni_poklic: from.osnovni_poklic || "", sedanja_zaposlitev: from.sedanja_zaposlitev || "", prevajalska_praksa: from.prevajalska_praksa || "", nacin_pridobivanja_znanja_tujih_jezikov: from.nacin_pridobivanja_znanja_tujih_jezikov || "", datum_vclanitve: from.datum_vclanitve || "", materni_jezik: from.materni_jezik || "", objava_v_iskalniku: from.objava_v_iskalniku || 0 } } function formClanValueArray(req) { const body = req.body || {}; return [ body.id_izkaznica, body.ime, body.priimek, body.spol, body.datum_rojstva ? new Date(body.datum_rojstva) : null, body.kraj_rojstva, body.drzavljanstvo, body.solska_izobrazba, body.telefondoma, body.telefonsluzba, body.telefonmobi, body.email, body.spletnastran, body.naslovbivalisca, body.ulica, body.postna_stevilka, body.posta, body.osnovni_poklic, body.sedanja_zaposlitev, body.prevajalska_praksa, body.nacin_pridobivanja_znanja_tujih_jezikov, body.datum_vclanitve ? new Date(body.datum_vclanitve) : null, body.materni_jezik, body.objava_v_iskalniku ]; } app.get("/express/api/v1/clan/", authenticateFirebaseToken, (req, res) => { db.query(`SELECT * FROM clan`, (err, result) => { if (err) return res.sendStatus(500); const out = result.map(row => formClanStruct(row)); res.status(200).json(out); }); }); app.get("/express/api/v1/clan/:id", authenticateFirebaseToken, (req, res) => { db.query(`SELECT * FROM clan WHERE id = ?`, [req.params.id], (err, result) => { if (err) return res.sendStatus(500); if (!result.length) return res.sendStatus(404); res.status(200).json(formClanStruct(result[0])); }); }); app.post("/express/api/v1/clan/", authenticateFirebaseToken, (req, res) => { db.query( `INSERT INTO clan ( id_izkaznica, ime, priimek, spol, datum_rojstva, kraj_rojstva, drzavljanstvo, solska_izobrazba, telefondoma, telefonsluzba, telefonmobi, email, spletnastran, naslovbivalisca, ulica, postna_stevilka, posta, osnovni_poklic, sedanja_zaposlitev, prevajalska_praksa, nacin_pridobivanja_znanja_tujih_jezikov, datum_vclanitve, materni_jezik, objava_v_iskalniku ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, formClanValueArray(req), (err, result) => { if (!err && result.affectedRows === 1) { res.status(200).json(formClanStruct(req.body, result.insertId)); } else { console.error("INSERT /clan error:", err); res.sendStatus(400); } } ); }); app.put("/express/api/v1/clan/:id", authenticateFirebaseToken, (req, res) => { const valueArr = formClanValueArray(req); valueArr.push(req.params.id); db.query( `UPDATE clan SET id_izkaznica=?, ime=?, priimek=?, spol=?, datum_rojstva=?, kraj_rojstva=?, drzavljanstvo=?, solska_izobrazba=?, telefondoma=?, telefonsluzba=?, telefonmobi=?, email=?, spletnastran=?, naslovbivalisca=?, ulica=?, postna_stevilka=?, posta=?, osnovni_poklic=?, sedanja_zaposlitev=?, prevajalska_praksa=?, nacin_pridobivanja_znanja_tujih_jezikov=?, datum_vclanitve=?, materni_jezik=?, objava_v_iskalniku=? WHERE id=?`, valueArr, (err, result) => { if (!err && result.affectedRows === 1) { res.status(200).json(formClanStruct(req.body, req.params.id)); } else { console.error("UPDATE /clan error:", err); res.sendStatus(400); } } ); }); app.delete("/express/api/v1/clan/:id", authenticateFirebaseToken, (req, res) => { db.query(`DELETE FROM clan WHERE id = ?`, [req.params.id], (err, result) => { if (!err && result.affectedRows === 1) { res.sendStatus(200); } else { res.sendStatus(400); } }); }); /********************************** * prevod (translations) *********************************/ // Safe helpers that reject on error function getVloge(prevodID) { return new Promise((resolve, reject) => { db.query( `SELECT v.id AS id, v.vloga, v.izpis_zenska, v.izpis_moski FROM prevod_vloga pv JOIN vloga v ON pv.vloga_id = v.id WHERE pv.prevod_id = ?`, [prevodID], (err, result) => { if (err) return reject(err); resolve(result.map(row => ({ id: row.id, vloga: row.vloga, izpis_zenska: row.izpis_zenska, izpis_moski: row.izpis_moski }))); } ); }); } function getPodrocja(prevodID) { return new Promise((resolve, reject) => { db.query( `SELECT p.id AS id, p.podrocje FROM prevod_podrocje pp JOIN podrocje p ON pp.podrocje_id = p.id WHERE pp.prevod_id = ?`, [prevodID], (err, result) => { if (err) return reject(err); resolve(result.map(row => ({ id: row.id, podrocje: row.podrocje }))); } ); }); } async function formPrevodStruct(from, id = null) { const pid = id || from.id; const out = { id: pid, id_clan: from.id_clan, rang: from.rang, iz: from.iz, v: from.v, licenca_DZTPS: from.licenca_DZTPS }; out.podrocja = await getPodrocja(pid); out.vloge = await getVloge(pid); return out; } app.get("/express/api/v1/clan/:id/prevod/", authenticateFirebaseToken, (req, res) => { db.query( `SELECT p.id AS id, p.id_clan, p.rang, iz.jezik AS iz, v.jezik AS v, p.licenca_DZTPS FROM prevod p JOIN jezik iz ON p.iz_id = iz.id JOIN jezik v ON p.v_id = v.id WHERE p.id_clan = ?`, [req.params.id], async (err, result) => { if (err) { console.error("DB error (prevod list):", err); return res.sendStatus(500); } try { const out = await Promise.all(result.map(row => formPrevodStruct(row))); res.status(200).json(out); } catch (e) { console.error("Error assembling prevod response:", e); res.sendStatus(500); } } ); }); app.get("/express/api/v1/clan/:id/prevod/:pid", authenticateFirebaseToken, (req, res) => { db.query( `SELECT p.id AS id, p.id_clan, p.rang, iz.jezik AS iz, v.jezik AS v, p.licenca_DZTPS FROM prevod p JOIN jezik iz ON p.iz_id = iz.id JOIN jezik v ON p.v_id = v.id WHERE p.id_clan = ? AND p.id = ?`, [req.params.id, req.params.pid], async (err, result) => { if (err) return res.sendStatus(500); if (!result.length) return res.sendStatus(404); try { const out = await formPrevodStruct(result[0]); res.status(200).json(out); } catch (e) { console.error("Error assembling single prevod:", e); res.sendStatus(500); } } ); }); // Helpers to add/remove relations async function addPrevodVloga(prevodID, vloga) { await query( `INSERT INTO prevod_vloga (prevod_id, vloga_id) VALUES (?, (SELECT v.id FROM vloga v WHERE v.vloga = ?))`, [prevodID, vloga] ); } async function removePrevodVloga(prevodID, vloga) { await query( `DELETE FROM prevod_vloga WHERE prevod_id = ? AND vloga_id = (SELECT v.id FROM vloga v WHERE v.vloga = ?)`, [prevodID, vloga] ); } async function addPrevodPodrocje(prevodID, podrocje) { await query( `INSERT INTO prevod_podrocje (prevod_id, podrocje_id) VALUES (?, (SELECT p.id FROM podrocje p WHERE p.podrocje = ?))`, [prevodID, podrocje] ); } async function removePrevodPodrocje(prevodID, podrocje) { await query( `DELETE FROM prevod_podrocje WHERE prevod_id = ? AND podrocje_id = (SELECT p.id FROM podrocje p WHERE p.podrocje = ?)`, [prevodID, podrocje] ); } function namesFromArray(arr, key) { if (!Array.isArray(arr)) return []; return arr.map(x => (typeof x === 'string' ? x : (x?.[key] ?? ''))).filter(Boolean); } // Create translation app.post("/express/api/v1/clan/:id/prevod/", authenticateFirebaseToken, async (req, res) => { const { id: pathId } = req.params; const { id_clan, rang, iz, v, licenca_DZTPS, podrocja = [], vloge = [] } = req.body || {}; if (String(pathId) !== String(id_clan)) { return res.status(400).json({ message: "id_clan in body must match :id in path" }); } if (!rang || !iz || !v) { return res.status(400).json({ message: "Missing required fields: rang, iz, v" }); } try { await beginTransaction(); // Resolve language IDs const langs = await query(`SELECT id, jezik FROM jezik WHERE jezik IN (?, ?)`, [iz, v]); const izRow = langs.find(r => r.jezik === iz); const vRow = langs.find(r => r.jezik === v); if (!izRow || !vRow) { await rollback(); return res.status(400).json({ message: "Unknown language in iz or v" }); } const insert = await query( `INSERT INTO prevod (id_clan, rang, iz_id, v_id, licenca_DZTPS) VALUES (?, ?, ?, ?, ?)`, [id_clan, rang, izRow.id, vRow.id, licenca_DZTPS ?? 0] ); const prevodID = insert.insertId; // Podrocja const podNames = namesFromArray(podrocja, 'podrocje'); if (podNames.length) { const rows = await query( `SELECT id, podrocje FROM podrocje WHERE podrocje IN (${podNames.map(() => '?').join(',')})`, podNames ); const mapIds = new Map(rows.map(r => [r.podrocje, r.id])); const missing = podNames.filter(n => !mapIds.has(n)); if (missing.length) { await rollback(); return res.status(400).json({ message: "Unknown podrocje", details: missing }); } const values = podNames.map(n => [prevodID, mapIds.get(n)]); await query( `INSERT INTO prevod_podrocje (prevod_id, podrocje_id) VALUES ${values.map(() => '(?, ?)').join(',')}`, values.flat() ); } // Vloge const vlogaNames = namesFromArray(vloge, 'vloga'); if (vlogaNames.length) { const rows = await query( `SELECT id, vloga FROM vloga WHERE vloga IN (${vlogaNames.map(() => '?').join(',')})`, vlogaNames ); const mapIds = new Map(rows.map(r => [r.vloga, r.id])); const missing = vlogaNames.filter(n => !mapIds.has(n)); if (missing.length) { await rollback(); return res.status(400).json({ message: "Unknown vloga", details: missing }); } const values = vlogaNames.map(n => [prevodID, mapIds.get(n)]); await query( `INSERT INTO prevod_vloga (prevod_id, vloga_id) VALUES ${values.map(() => '(?, ?)').join(',')}`, values.flat() ); } await commit(); const [row] = await query( `SELECT p.id AS id, p.id_clan, p.rang, iz.jezik AS iz, v.jezik AS v, p.licenca_DZTPS FROM prevod p JOIN jezik iz ON p.iz_id = iz.id JOIN jezik v ON p.v_id = v.id WHERE p.id = ?`, [prevodID] ); const payload = await formPrevodStruct(row); return res.status(200).json(payload); } catch (e) { try { await rollback(); } catch {} console.error("POST /clan/:id/prevod error:", e); return res.status(400).json({ message: "Failed to create prevod", error: e && e.message }); } }); // Update translation app.put("/express/api/v1/clan/:id/prevod/:pid", authenticateFirebaseToken, async (req, res) => { const { id: pathId, pid } = req.params; const { id_clan, rang, iz, v, licenca_DZTPS, podrocja = [], vloge = [] } = req.body || {}; if (String(pathId) !== String(id_clan)) { return res.status(400).json({ message: "id_clan in body must match :id in path" }); } try { await beginTransaction(); // Resolve language IDs if provided strings const langsNeeded = []; if (iz) langsNeeded.push(iz); if (v) langsNeeded.push(v); let izId = null, vId = null; if (langsNeeded.length) { const langRows = await query( `SELECT id, jezik FROM jezik WHERE jezik IN (${langsNeeded.map(() => '?').join(',')})`, langsNeeded ); const map = new Map(langRows.map(r => [r.jezik, r.id])); if (iz && !map.has(iz)) { await rollback(); return res.status(400).json({ message: "Unknown language (iz)" }); } if (v && !map.has(v)) { await rollback(); return res.status(400).json({ message: "Unknown language (v)" }); } if (iz) izId = map.get(iz); if (v) vId = map.get(v); } // Build dynamic UPDATE (keep current values if not provided) const sets = []; const params = []; if (id_clan != null) { sets.push(`id_clan = ?`); params.push(id_clan); } if (rang != null) { sets.push(`rang = ?`); params.push(rang); } if (izId != null) { sets.push(`iz_id = ?`); params.push(izId); } if (vId != null) { sets.push(`v_id = ?`); params.push(vId); } if (licenca_DZTPS != null) { sets.push(`licenca_DZTPS = ?`); params.push(licenca_DZTPS); } if (sets.length) { params.push(pathId, pid); const sql = `UPDATE prevod SET ${sets.join(', ')} WHERE id_clan = ? AND id = ?`; const upd = await query(sql, params); if (upd.affectedRows === 0) { await rollback(); return res.sendStatus(404); } } // Fetch old relations BEFORE syncing const oldPodrocja = await getPodrocja(pid); const oldVloge = await getVloge(pid); // Sync Podrocja const newPodrocjaNames = namesFromArray(podrocja, 'podrocje'); if (newPodrocjaNames.length) { // Map names -> ids const rows = await query( `SELECT id, podrocje FROM podrocje WHERE podrocje IN (${newPodrocjaNames.map(() => '?').join(',')})`, newPodrocjaNames ); const mapIds = new Map(rows.map(r => [r.podrocje, r.id])); const missing = newPodrocjaNames.filter(n => !mapIds.has(n)); if (missing.length) { await rollback(); return res.status(400).json({ message: "Unknown podrocje", details: missing }); } // Compute deltas by name const oldNames = new Set(oldPodrocja.map(p => p.podrocje)); const newNames = new Set(newPodrocjaNames); // Deletes for (const name of oldNames) { if (!newNames.has(name)) { await removePrevodPodrocje(pid, name); } } // Adds for (const name of newNames) { if (!oldNames.has(name)) { await addPrevodPodrocje(pid, name); } } } // Sync Vloge const newVlogaNames = namesFromArray(vloge, 'vloga'); if (newVlogaNames.length) { const rows = await query( `SELECT id, vloga FROM vloga WHERE vloga IN (${newVlogaNames.map(() => '?').join(',')})`, newVlogaNames ); const mapIds = new Map(rows.map(r => [r.vloga, r.id])); const missing = newVlogaNames.filter(n => !mapIds.has(n)); if (missing.length) { await rollback(); return res.status(400).json({ message: "Unknown vloga", details: missing }); } const oldNames = new Set(oldVloge.map(v => v.vloga)); const newNames = new Set(newVlogaNames); for (const name of oldNames) { if (!newNames.has(name)) { await removePrevodVloga(pid, name); } } for (const name of newNames) { if (!oldNames.has(name)) { await addPrevodVloga(pid, name); } } } await commit(); // Return the updated record const [row] = await query( `SELECT p.id AS id, p.id_clan, p.rang, iz.jezik AS iz, v.jezik AS v, p.licenca_DZTPS FROM prevod p JOIN jezik iz ON p.iz_id = iz.id JOIN jezik v ON p.v_id = v.id WHERE p.id = ?`, [pid] ); if (!row) return res.sendStatus(404); const payload = await formPrevodStruct(row); res.status(200).json(payload); } catch (e) { try { await rollback(); } catch {} console.error("PUT /clan/:id/prevod/:pid error:", e); res.status(400).json({ message: "Failed to update prevod", error: e && e.message }); } }); app.delete("/express/api/v1/clan/:id/prevod/:pid", authenticateFirebaseToken, async (req, res) => { try { await beginTransaction(); await query(`DELETE FROM prevod_podrocje WHERE prevod_id = ?`, [req.params.pid]); await query(`DELETE FROM prevod_vloga WHERE prevod_id = ?`, [req.params.pid]); const del = await query(`DELETE FROM prevod WHERE id_clan = ? AND id = ?`, [req.params.id, req.params.pid]); if (!del.affectedRows) { await rollback(); return res.sendStatus(404); } await commit(); res.sendStatus(200); } catch (e) { try { await rollback(); } catch {} console.error("DELETE /clan/:id/prevod/:pid error:", e); res.sendStatus(400); } }); /********************************** * jezik *********************************/ function formJezikStruct(from, id = null) { if (!id) id = from.id; return { id, jezik: from.jezik }; } app.get("/express/api/v1/jezik/", authenticateFirebaseToken, (req, res) => { db.query(`SELECT id, jezik FROM jezik`, (err, result) => { if (err) return res.sendStatus(500); res.status(200).json(result.map(row => formJezikStruct(row))); }); }); app.get("/express/api/v1/jezik/:id", authenticateFirebaseToken, (req, res) => { db.query(`SELECT id, jezik FROM jezik WHERE id = ?`, [req.params.id], (err, result) => { if (err) return res.sendStatus(500); if (!result.length) return res.sendStatus(404); res.status(200).json(formJezikStruct(result[0])); }); }); app.post("/express/api/v1/jezik/", authenticateFirebaseToken, (req, res) => { db.query(`INSERT INTO jezik (jezik) VALUES (?)`, [req.body.jezik], (err, result) => { if (!err && result.affectedRows === 1) { res.status(200).json(formJezikStruct(req.body, result.insertId)); } else { res.sendStatus(400); } }); }); app.put("/express/api/v1/jezik/:id", authenticateFirebaseToken, (req, res) => { db.query(`UPDATE jezik SET jezik = ? WHERE id = ?`, [req.body.jezik, req.params.id], (err, result) => { if (!err && result.affectedRows === 1) { res.status(200).json(formJezikStruct(req.body, req.params.id)); } else { res.sendStatus(400); } }); }); app.delete("/express/api/v1/jezik/:id", authenticateFirebaseToken, (req, res) => { db.query(`DELETE FROM jezik WHERE id = ?`, [req.params.id], (err, result) => { if (!err && result.affectedRows === 1) { res.sendStatus(200); } else { res.sendStatus(400); } }); }); /********************************** * vloga *********************************/ function formVlogaStruct(from, id = null) { if (!id) id = from.id; return { id, vloga: from.vloga, izpis_zenska: from.izpis_zenska, izpis_moski: from.izpis_moski }; } app.get("/express/api/v1/vloga/", authenticateFirebaseToken, (req, res) => { db.query(`SELECT id, vloga, izpis_zenska, izpis_moski FROM vloga`, (err, result) => { if (err) return res.sendStatus(500); res.status(200).json(result.map(row => formVlogaStruct(row))); }); }); app.get("/express/api/v1/vloga/:id", authenticateFirebaseToken, (req, res) => { db.query(`SELECT id, vloga, izpis_zenska, izpis_moski FROM vloga WHERE id = ?`, [req.params.id], (err, result) => { if (err) return res.sendStatus(500); if (!result.length) return res.sendStatus(404); res.status(200).json(formVlogaStruct(result[0])); }); }); app.post("/express/api/v1/vloga/", authenticateFirebaseToken, (req, res) => { db.query( `INSERT INTO vloga (vloga, izpis_zenska, izpis_moski) VALUES (?, ?, ?)`, [req.body.vloga, req.body.izpis_zenska, req.body.izpis_moski], (err, result) => { if (!err && result.affectedRows === 1) { res.status(200).json(formVlogaStruct(req.body, result.insertId)); } else { res.sendStatus(400); } } ); }); app.put("/express/api/v1/vloga/:id", authenticateFirebaseToken, (req, res) => { db.query( `UPDATE vloga SET vloga = ?, izpis_zenska = ?, izpis_moski = ? WHERE id = ?`, [req.body.vloga, req.body.izpis_zenska, req.body.izpis_moski, req.params.id], (err, result) => { if (!err && result.affectedRows === 1) { res.status(200).json(formVlogaStruct(req.body, req.params.id)); } else { res.sendStatus(400); } } ); }); app.delete("/express/api/v1/vloga/:id", authenticateFirebaseToken, (req, res) => { db.query(`DELETE FROM vloga WHERE id = ?`, [req.params.id], (err, result) => { if (!err && result.affectedRows === 1) { res.sendStatus(200); } else { res.sendStatus(400); } }); }); /********************************** * podrocje *********************************/ function formPodrocjeStruct(from, id = null) { if (!id) id = from.id; return { id, podrocje: from.podrocje }; } app.get("/express/api/v1/podrocje", authenticateFirebaseToken, (req, res) => { db.query(`SELECT id, podrocje FROM podrocje`, (err, result) => { if (err) return res.sendStatus(500); res.status(200).json(result.map(row => formPodrocjeStruct(row))); }); }); app.get("/express/api/v1/podrocje/:id", authenticateFirebaseToken, (req, res) => { db.query(`SELECT id, podrocje FROM podrocje WHERE id = ?`, [req.params.id], (err, result) => { if (err) return res.sendStatus(500); if (!result.length) return res.sendStatus(404); res.status(200).json(formPodrocjeStruct(result[0])); }); }); app.post("/express/api/v1/podrocje/", authenticateFirebaseToken, (req, res) => { db.query(`INSERT INTO podrocje (podrocje) VALUES (?)`, [req.body.podrocje], (err, result) => { if (!err && result.affectedRows === 1) { res.status(200).json(formPodrocjeStruct(req.body, result.insertId)); } else { res.sendStatus(400); } }); }); app.put("/express/api/v1/podrocje/:id", authenticateFirebaseToken, (req, res) => { db.query( `UPDATE podrocje SET podrocje = ? WHERE id = ?`, [req.body.podrocje, req.params.id], (err, result) => { if (!err && result.affectedRows === 1) { res.status(200).json(formPodrocjeStruct(req.body, req.params.id)); } else { res.sendStatus(400); } } ); }); app.delete("/express/api/v1/podrocje/:id", authenticateFirebaseToken, (req, res) => { db.query(`DELETE FROM podrocje WHERE id = ?`, [req.params.id], (err, result) => { if (!err && result.affectedRows === 1) { res.sendStatus(200); } else { res.sendStatus(400); } }); }); /********************************** * ISKALNIK (search) *********************************/ app.get('/express_backend', (req, res) => { res.send({ express: `YOUR BACKEND IS CONNECTED` }); }); app.post('/express_backend/api/v1/iskanje', (req, res) => { const iskanjeIme = req.body.iskanjeNiz; const iskanjeCrka = req.body.iskanjeCrkaPriimek; const iskanjeIz = req.body.iskanjeIzvirniJezik; const iskanjeV = req.body.iskanjeCiljniJezik; const iskanjeRangi = req.body.iskanjeRang; const iskanjePodrocja = new Set(req.body.iskanjeStrokovnoPodrocje); const iskanjeVloge = new Set(req.body.iskanjeVloga); const iskanjeLicenca = req.body.iskanjeLicenca; // Build query const params = []; const queryConditions = []; if (iskanjeIme) { queryConditions.push("(LOWER(CONCAT(c.ime, ' ', c.priimek)) LIKE LOWER(?))"); params.push(`%${iskanjeIme}%`); } if (iskanjeCrka) { queryConditions.push("(c.priimek LIKE (?))"); params.push(`${iskanjeCrka}%`); } if (iskanjeIz) { queryConditions.push("(iz.jezik = (?))"); params.push(iskanjeIz); } if (iskanjeV) { queryConditions.push("(v.jezik = (?))"); params.push(iskanjeV); } const generateQuestionMarks = (n) => Array.from({ length: n }, () => '?').join(','); if (Array.isArray(iskanjeRangi) && iskanjeRangi.length) { queryConditions.push(`(p.rang IN (${generateQuestionMarks(iskanjeRangi.length)}))`); params.push(...iskanjeRangi); } if (iskanjeLicenca != null) { queryConditions.push("(p.licenca_DZTPS = ?)"); params.push(iskanjeLicenca); } const where = [ "c.objava_v_iskalniku = 1", ...(queryConditions.length ? queryConditions : []) ].join(" AND "); db.query( `SELECT c.id AS id, c.ime AS ime, c.priimek AS priimek, c.spol AS spol, c.telefondoma AS telefondoma, c.telefonsluzba AS telefonsluzba, c.telefonmobi AS telefonmobi, c.email AS email, c.spletnastran AS spletnastran, p.rang AS rang, iz.jezik AS iz, v.jezik AS v, p.licenca_DZTPS AS licenca, ( SELECT GROUP_CONCAT(po.podrocje SEPARATOR ';') FROM prevod_podrocje pp JOIN podrocje po ON pp.podrocje_id = po.id WHERE pp.prevod_id = p.id GROUP BY pp.prevod_id ) AS podrocja, ( SELECT GROUP_CONCAT(vl.vloga SEPARATOR ';') FROM prevod_vloga pv JOIN vloga vl ON pv.vloga_id = vl.id WHERE pv.prevod_id = p.id GROUP BY pv.prevod_id ) AS vloge FROM clan c JOIN prevod p ON c.id = p.id_clan JOIN jezik iz ON p.iz_id = iz.id JOIN jezik v ON p.v_id = v.id WHERE ${where}`, params, (err, rows) => { if (err) { console.error("Search error:", err); return res.sendStatus(500); } if (!rows.length) return res.send({}); let out = []; for (let row of rows) { if (out.length <= 0 || out[out.length - 1].id !== row.id) { out.push({ id: row.id, ime: row.ime, priimek: row.priimek, spol: row.spol, telefondoma: row.telefondoma, telefonmobi: row.telefonmobi, telefonsluzba: row.telefonsluzba, email: row.email, spletnastran: row.spletnastran, prevodi: [], }); } let podrocja = row.podrocja?.split(';') || []; let vloge = row.vloge?.split(';') || []; if (iskanjePodrocja.size) { podrocja = podrocja.filter(el => iskanjePodrocja.has(el)); if (!podrocja.length) continue; } if (iskanjeVloge.size) { vloge = vloge.filter(el => iskanjeVloge.has(el)); if (!vloge.length) continue; } out[out.length - 1].prevodi.push({ rang: row.rang, iz: row.iz, v: row.v, strokovnopodrocje: row.podrocja?.split(';') || [], vloga: row.vloge?.split(';') || [], }); } out = out.filter(row => row.prevodi.length > 0); res.send(JSON.stringify(out, null, 4)); } ); }); app.get("/express_backend/api/get/user/:user_id", (req, res) => { db.query( `SELECT c.id AS id, c.ime AS ime, c.priimek AS priimek, c.spol AS spol, c.telefondoma AS telefondoma, c.telefonsluzba AS telefonsluzba, c.telefonmobi AS telefonmobi, c.email AS email, c.spletnastran AS spletnastran, p.id AS id_prevod, p.rang AS rang, iz.jezik AS iz, v.jezik AS v, ( SELECT GROUP_CONCAT(po.podrocje SEPARATOR ';') FROM prevod_podrocje pp JOIN podrocje po ON pp.podrocje_id = po.id WHERE pp.prevod_id = p.id GROUP BY pp.prevod_id ) AS podrocja, ( SELECT GROUP_CONCAT(vl.vloga SEPARATOR ';') FROM prevod_vloga pv JOIN vloga vl ON pv.vloga_id = vl.id WHERE pv.prevod_id = p.id GROUP BY pv.prevod_id ) AS vloge FROM clan c JOIN prevod p ON c.id = p.id_clan JOIN jezik iz ON p.iz_id = iz.id JOIN jezik v ON p.v_id = v.id WHERE c.id = ? AND c.objava_v_iskalniku = 1 ORDER BY p.rang`, [req.params.user_id], (err, rows) => { if (err) { console.error("get/user error:", err); return res.sendStatus(500); } if (!rows.length) return res.send({}); const first = rows[0]; const out = { id: first.id, ime: first.ime, priimek: first.priimek, telefondoma: first.telefondoma, telefonmobi: first.telefonmobi, telefonsluzba: first.telefonsluzba, email: first.email, spletnastran: first.spletnastran, prevodi: [], }; for (let row of rows) { out.prevodi.push({ id: row.id_prevod, rang: row.rang, iz: row.iz, v: row.v, strokovnopodrocje: row.podrocja?.split(';') || [], vloga: row.vloge?.split(';') || [], }); } res.send(JSON.stringify(out, null, 4)); } ); }); app.get("/express_backend/api/getAll", (req, res) => { db.query( `SELECT c.id AS id, c.ime AS ime, c.priimek AS priimek, c.spol AS spol, c.telefondoma AS telefondoma, c.telefonsluzba AS telefonsluzba, c.telefonmobi AS telefonmobi, c.email AS email, c.spletnastran AS spletnastran, p.rang AS rang, iz.jezik AS iz, v.jezik AS v, ( SELECT GROUP_CONCAT(po.podrocje SEPARATOR ';') FROM prevod_podrocje pp JOIN podrocje po ON pp.podrocje_id = po.id WHERE pp.prevod_id = p.id GROUP BY pp.prevod_id ) AS podrocja, ( SELECT GROUP_CONCAT(vl.vloga SEPARATOR ';') FROM prevod_vloga pv JOIN vloga vl ON pv.vloga_id = vl.id WHERE pv.prevod_id = p.id GROUP BY pv.prevod_id ) AS vloge FROM clan c JOIN prevod p ON c.id = p.id_clan JOIN jezik iz ON p.iz_id = iz.id JOIN jezik v ON p.v_id = v.id WHERE c.objava_v_iskalniku = 1`, (err, rows) => { if (err) { console.error("getAll error:", err); return res.sendStatus(500); } // Group prevodi per user let out = []; for (let row of rows) { if (out.length <= 0 || out[out.length - 1].id !== row.id) { out.push({ id: row.id, ime: row.ime, priimek: row.priimek, spol: row.spol, telefondoma: row.telefondoma, telefonmobi: row.telefonmobi, telefonsluzba: row.telefonsluzba, email: row.email, spletnastran: row.spletnastran, prevodi: [], }); } out[out.length - 1].prevodi.push({ rang: row.rang, iz: row.iz, v: row.v, strokovnopodrocje: row.podrocja?.split(';') || [], vloga: row.vloge?.split(';') || [], }); } res.send(JSON.stringify(out, null, 4)); } ); }); // app.listen(PORT, () => { console.log(`Server listening on port ${PORT}`); });