Files
dztps-iskalnik-urejevalnik/server/server/index.js
matej cd020e3eb8 Redesigned UI
Fixed SQL query issues with server
2025-09-06 14:27:54 +02:00

1027 lines
37 KiB
JavaScript

// 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}`); });