451 lines
18 KiB
JavaScript
451 lines
18 KiB
JavaScript
|
|
'use strict';
|
|||
|
|
|
|||
|
|
const express = require('express');
|
|||
|
|
|
|||
|
|
const BUILDINGS_CACHE_TTL_MS = 30 * 1000; // 30 секунд
|
|||
|
|
|
|||
|
|
function createResponseCache() {
|
|||
|
|
const store = new Map();
|
|||
|
|
return {
|
|||
|
|
get(key) {
|
|||
|
|
const entry = store.get(key);
|
|||
|
|
if (!entry || Date.now() > entry.expiresAt) {
|
|||
|
|
if (entry) store.delete(key);
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
return entry.data;
|
|||
|
|
},
|
|||
|
|
set(key, data, ttlMs = BUILDINGS_CACHE_TTL_MS) {
|
|||
|
|
store.set(key, { data, expiresAt: Date.now() + ttlMs });
|
|||
|
|
},
|
|||
|
|
invalidatePrefix(prefix) {
|
|||
|
|
for (const key of store.keys()) {
|
|||
|
|
if (key.startsWith(prefix)) store.delete(key);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Buildings API router.
|
|||
|
|
* @param {{ query: (text: string, params?: any[]) => Promise<any[]> }} deps
|
|||
|
|
* @returns {express.Router}
|
|||
|
|
*/
|
|||
|
|
function createBuildingsRouter(deps) {
|
|||
|
|
const { query } = deps;
|
|||
|
|
const router = express.Router();
|
|||
|
|
const cache = createResponseCache();
|
|||
|
|
|
|||
|
|
function invalidateBuildingsCache() {
|
|||
|
|
cache.invalidatePrefix('buildings:');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// GET /buildings -> список домов (пагинация, опционально ?light=1). При scope=own_district — дома назначенных участков (employee_districts + fallback assigned_district_id).
|
|||
|
|
router.get('/buildings', async (req, res) => {
|
|||
|
|
try {
|
|||
|
|
const limit = Math.min(Math.max(1, parseInt(req.query.limit, 10) || 100), 500);
|
|||
|
|
const offset = Math.max(0, parseInt(req.query.offset, 10) || 0);
|
|||
|
|
const light = req.query.light === '1' || req.query.light === 'true';
|
|||
|
|
|
|||
|
|
let districtIds = null; // null = все участки, [] = нет доступа, [id, ...] = только эти участки
|
|||
|
|
if (req.user && req.user.userId) {
|
|||
|
|
const userScopeRows = await query(
|
|||
|
|
`SELECT pu.scope, e.id AS employee_id, e.assigned_district_id
|
|||
|
|
FROM portal_users pu
|
|||
|
|
JOIN employees e ON e.id = pu.employee_id
|
|||
|
|
WHERE pu.id = $1`,
|
|||
|
|
[req.user.userId]
|
|||
|
|
);
|
|||
|
|
const row = userScopeRows[0];
|
|||
|
|
if (row && row.scope === 'own_district') {
|
|||
|
|
let ids = [];
|
|||
|
|
try {
|
|||
|
|
const edRows = await query(
|
|||
|
|
'SELECT district_id FROM employee_districts WHERE employee_id = $1 ORDER BY district_id',
|
|||
|
|
[row.employee_id]
|
|||
|
|
);
|
|||
|
|
ids = (edRows || []).map((r) => r.district_id).filter(Boolean);
|
|||
|
|
} catch (_) {}
|
|||
|
|
if (ids.length === 0 && row.assigned_district_id) {
|
|||
|
|
ids = [row.assigned_district_id];
|
|||
|
|
}
|
|||
|
|
districtIds = ids;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const cacheKey = districtIds === null
|
|||
|
|
? `buildings:list:${limit}:${offset}:${light}`
|
|||
|
|
: `buildings:list:${limit}:${offset}:${light}:districts:${[...districtIds].sort().join(',')}`;
|
|||
|
|
|
|||
|
|
const cached = districtIds === null ? cache.get(cacheKey) : null;
|
|||
|
|
if (cached !== null) {
|
|||
|
|
return res.json(cached);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
let rows;
|
|||
|
|
if (districtIds !== null && districtIds.length === 0) {
|
|||
|
|
rows = [];
|
|||
|
|
} else if (districtIds !== null && districtIds.length > 0) {
|
|||
|
|
rows = await query(
|
|||
|
|
`SELECT id, data FROM buildings WHERE (data->>'districtId') = ANY($1::text[]) ORDER BY id LIMIT $2 OFFSET $3`,
|
|||
|
|
[districtIds, limit, offset]
|
|||
|
|
);
|
|||
|
|
} else {
|
|||
|
|
rows = await query(
|
|||
|
|
'SELECT id, data FROM buildings ORDER BY id LIMIT $1 OFFSET $2',
|
|||
|
|
[limit, offset]
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
let data;
|
|||
|
|
if (light) {
|
|||
|
|
data = rows.map((r) => {
|
|||
|
|
const d = r.data || {};
|
|||
|
|
return {
|
|||
|
|
id: d.id || r.id,
|
|||
|
|
districtId: d.districtId ?? null,
|
|||
|
|
passport: d.passport ? { address: d.passport.address || '' } : { address: '' }
|
|||
|
|
};
|
|||
|
|
});
|
|||
|
|
} else {
|
|||
|
|
data = rows.map((r) => r.data);
|
|||
|
|
const ids = rows.map((r) => (r.data && r.data.id) || r.id).filter(Boolean);
|
|||
|
|
if (ids.length > 0) {
|
|||
|
|
try {
|
|||
|
|
const placeholders = ids.map((_, i) => `$${i + 1}`).join(', ');
|
|||
|
|
const accountRows = await query(
|
|||
|
|
`SELECT building_id, data FROM building_personal_accounts WHERE building_id IN (${placeholders})`,
|
|||
|
|
ids
|
|||
|
|
);
|
|||
|
|
const accountsByBuilding = {};
|
|||
|
|
for (const ar of accountRows) {
|
|||
|
|
if (!accountsByBuilding[ar.building_id]) accountsByBuilding[ar.building_id] = [];
|
|||
|
|
accountsByBuilding[ar.building_id].push(ar.data);
|
|||
|
|
}
|
|||
|
|
for (const b of data) {
|
|||
|
|
const bid = b.id;
|
|||
|
|
b.accounts = Array.isArray(accountsByBuilding[bid]) ? accountsByBuilding[bid] : (Array.isArray(b.accounts) ? b.accounts : []);
|
|||
|
|
}
|
|||
|
|
} catch (e) {
|
|||
|
|
if (!e.message || !e.message.includes('building_personal_accounts')) {
|
|||
|
|
console.warn('Error loading accounts for list:', e.message);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
if (districtIds === null) cache.set(cacheKey, data);
|
|||
|
|
res.json(data);
|
|||
|
|
} catch (err) {
|
|||
|
|
console.error('Error fetching buildings:', err);
|
|||
|
|
res.status(500).json({ error: 'Failed to fetch buildings' });
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// POST /buildings -> создание нового дома
|
|||
|
|
router.post('/buildings', async (req, res) => {
|
|||
|
|
const building = req.body;
|
|||
|
|
|
|||
|
|
if (!building || !building.id || !building.passport || !building.passport.address) {
|
|||
|
|
return res
|
|||
|
|
.status(400)
|
|||
|
|
.json({ error: 'Ожидается объект Building с полями id и passport.address' });
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
await query('INSERT INTO buildings (id, data) VALUES ($1, $2)', [
|
|||
|
|
building.id,
|
|||
|
|
building,
|
|||
|
|
]);
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
const existingSurvey = await query(
|
|||
|
|
`SELECT id FROM nps_surveys WHERE building_id = $1`,
|
|||
|
|
[building.id]
|
|||
|
|
);
|
|||
|
|
if (existingSurvey.length === 0) {
|
|||
|
|
const accessKey = `nps-${building.id}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|||
|
|
await query(
|
|||
|
|
`INSERT INTO nps_surveys
|
|||
|
|
(building_id, title, description, status, access_key, created_by)
|
|||
|
|
VALUES ($1, $2, $3, $4, $5, 'system')`,
|
|||
|
|
[
|
|||
|
|
building.id,
|
|||
|
|
'Опрос удовлетворенности жителей',
|
|||
|
|
'Помогите нам стать лучше! Поделитесь своим мнением о качестве обслуживания.',
|
|||
|
|
'draft',
|
|||
|
|
accessKey
|
|||
|
|
]
|
|||
|
|
);
|
|||
|
|
console.log(`[Auto NPS] Создан опрос для нового дома ${building.id}`);
|
|||
|
|
}
|
|||
|
|
} catch (npsErr) {
|
|||
|
|
console.error(`[Auto NPS] Ошибка создания опроса для дома ${building.id}:`, npsErr);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
const now = new Date();
|
|||
|
|
const currentMonthStart = new Date(now.getFullYear(), now.getMonth(), 1);
|
|||
|
|
const currentMonthEnd = new Date(now.getFullYear(), now.getMonth() + 1, 0, 23, 59, 59);
|
|||
|
|
const months = [
|
|||
|
|
'Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь',
|
|||
|
|
'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь'
|
|||
|
|
];
|
|||
|
|
const monthName = months[now.getMonth()];
|
|||
|
|
const reportMonth = `${monthName} ${now.getFullYear()}`;
|
|||
|
|
const existingReport = await query(
|
|||
|
|
`SELECT id FROM resident_reports
|
|||
|
|
WHERE building_id = $1 AND month = $2`,
|
|||
|
|
[building.id, reportMonth]
|
|||
|
|
);
|
|||
|
|
if (existingReport.length === 0) {
|
|||
|
|
const initialContent = {
|
|||
|
|
applications: { total: 156, completed: 153, quality: 98 },
|
|||
|
|
finances: { collected: 2400000, expenses: 1890000, balance: 560000 },
|
|||
|
|
nps: { score: 72, totalResponses: 45 }
|
|||
|
|
};
|
|||
|
|
const newReport = await query(
|
|||
|
|
`INSERT INTO resident_reports
|
|||
|
|
(building_id, month, period_start, period_end, status, content, published_at)
|
|||
|
|
VALUES ($1, $2, $3, $4, $5, $6::jsonb, NOW())
|
|||
|
|
RETURNING id`,
|
|||
|
|
[
|
|||
|
|
building.id,
|
|||
|
|
reportMonth,
|
|||
|
|
currentMonthStart.toISOString().split('T')[0],
|
|||
|
|
currentMonthEnd.toISOString().split('T')[0],
|
|||
|
|
'published',
|
|||
|
|
JSON.stringify(initialContent)
|
|||
|
|
]
|
|||
|
|
);
|
|||
|
|
console.log(`[Auto Report] Создан опубликованный отчет для нового дома ${building.id} (ID: ${newReport[0].id})`);
|
|||
|
|
}
|
|||
|
|
} catch (reportErr) {
|
|||
|
|
console.error(`[Auto Report] Ошибка создания отчета для дома ${building.id}:`, reportErr);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
invalidateBuildingsCache();
|
|||
|
|
res.status(201).json(building);
|
|||
|
|
} catch (err) {
|
|||
|
|
console.error('Error creating building:', err);
|
|||
|
|
res.status(500).json({ error: 'Failed to create building' });
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// GET /buildings/:id — здание + лицевые счета (из building_personal_accounts или из data)
|
|||
|
|
router.get('/buildings/:id', async (req, res) => {
|
|||
|
|
try {
|
|||
|
|
const { id } = req.params;
|
|||
|
|
const cacheKey = `buildings:id:${id}`;
|
|||
|
|
const cached = cache.get(cacheKey);
|
|||
|
|
if (cached !== null) {
|
|||
|
|
return res.json(cached);
|
|||
|
|
}
|
|||
|
|
const rows = await query('SELECT data FROM buildings WHERE id = $1', [id]);
|
|||
|
|
if (!rows.length) {
|
|||
|
|
return res.status(404).json({ error: 'Дом не найден' });
|
|||
|
|
}
|
|||
|
|
const data = rows[0].data;
|
|||
|
|
try {
|
|||
|
|
const accountRows = await query(
|
|||
|
|
'SELECT id, data FROM building_personal_accounts WHERE building_id = $1',
|
|||
|
|
[id]
|
|||
|
|
);
|
|||
|
|
if (accountRows.length > 0) {
|
|||
|
|
data.accounts = accountRows.map((r) => r.data);
|
|||
|
|
} else if (!Array.isArray(data.accounts)) {
|
|||
|
|
data.accounts = [];
|
|||
|
|
}
|
|||
|
|
} catch (e) {
|
|||
|
|
if (!Array.isArray(data.accounts)) data.accounts = [];
|
|||
|
|
}
|
|||
|
|
cache.set(cacheKey, data);
|
|||
|
|
res.json(data);
|
|||
|
|
} catch (err) {
|
|||
|
|
console.error('Error fetching building:', err);
|
|||
|
|
res.status(500).json({ error: err.message || 'Failed to fetch building' });
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// PUT /buildings/:id — если есть таблица building_personal_accounts: не пишем accounts в data; иначе пишем всё (fallback)
|
|||
|
|
router.put('/buildings/:id', async (req, res) => {
|
|||
|
|
const { id } = req.params;
|
|||
|
|
const building = req.body;
|
|||
|
|
if (!building || !building.passport || !building.passport.address) {
|
|||
|
|
return res
|
|||
|
|
.status(400)
|
|||
|
|
.json({ error: 'Ожидается объект Building с полями passport.address' });
|
|||
|
|
}
|
|||
|
|
try {
|
|||
|
|
let useAccountsTable = false;
|
|||
|
|
try {
|
|||
|
|
await query('SELECT 1 FROM building_personal_accounts LIMIT 1');
|
|||
|
|
useAccountsTable = true;
|
|||
|
|
} catch (_) {}
|
|||
|
|
const toSave = { ...building };
|
|||
|
|
if (useAccountsTable) delete toSave.accounts;
|
|||
|
|
await query('UPDATE buildings SET data = $2 WHERE id = $1', [id, toSave]);
|
|||
|
|
invalidateBuildingsCache();
|
|||
|
|
const dataForResponse = { ...toSave };
|
|||
|
|
if (useAccountsTable) {
|
|||
|
|
try {
|
|||
|
|
const accountRows = await query(
|
|||
|
|
'SELECT id, data FROM building_personal_accounts WHERE building_id = $1',
|
|||
|
|
[id]
|
|||
|
|
);
|
|||
|
|
dataForResponse.accounts = accountRows.length > 0 ? accountRows.map((r) => r.data) : [];
|
|||
|
|
} catch (e) {
|
|||
|
|
dataForResponse.accounts = Array.isArray(building.accounts) ? building.accounts : [];
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
dataForResponse.accounts = Array.isArray(building.accounts) ? building.accounts : [];
|
|||
|
|
}
|
|||
|
|
res.json(dataForResponse);
|
|||
|
|
} catch (err) {
|
|||
|
|
console.error('Error updating building:', err);
|
|||
|
|
res.status(500).json({ error: 'Failed to update building' });
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// DELETE /buildings/:id
|
|||
|
|
router.delete('/buildings/:id', async (req, res) => {
|
|||
|
|
const { id } = req.params;
|
|||
|
|
try {
|
|||
|
|
const buildingRows = await query('SELECT id FROM buildings WHERE id = $1', [id]);
|
|||
|
|
if (buildingRows.length === 0) {
|
|||
|
|
return res.status(404).json({ error: 'Дом не найден' });
|
|||
|
|
}
|
|||
|
|
await query('DELETE FROM buildings WHERE id = $1', [id]);
|
|||
|
|
invalidateBuildingsCache();
|
|||
|
|
res.json({ success: true, message: 'Дом успешно удален' });
|
|||
|
|
} catch (err) {
|
|||
|
|
console.error('Error deleting building:', err);
|
|||
|
|
res.status(500).json({ error: 'Ошибка при удалении дома' });
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// POST /buildings/:id/accounts — в building_personal_accounts или в buildings.data (fallback)
|
|||
|
|
router.post('/buildings/:id/accounts', async (req, res) => {
|
|||
|
|
const { id: buildingId } = req.params;
|
|||
|
|
const newAccount = req.body;
|
|||
|
|
if (!newAccount || !newAccount.apartmentNumber) {
|
|||
|
|
return res.status(400).json({ error: 'Ожидается объект PersonalAccount с полем apartmentNumber' });
|
|||
|
|
}
|
|||
|
|
try {
|
|||
|
|
const buildingRows = await query('SELECT data FROM buildings WHERE id = $1', [buildingId]);
|
|||
|
|
if (buildingRows.length === 0) {
|
|||
|
|
return res.status(404).json({ error: 'Building not found' });
|
|||
|
|
}
|
|||
|
|
let useAccountsTable = false;
|
|||
|
|
try {
|
|||
|
|
await query('SELECT 1 FROM building_personal_accounts LIMIT 1');
|
|||
|
|
useAccountsTable = true;
|
|||
|
|
} catch (_) {}
|
|||
|
|
const accounts = Array.isArray(buildingRows[0].data?.accounts) ? buildingRows[0].data.accounts : [];
|
|||
|
|
if (!newAccount.id) newAccount.id = `${buildingId}-acc-${Date.now()}`;
|
|||
|
|
if (!newAccount.accountNumber) newAccount.accountNumber = `${buildingId.replace('b-', '')}00${accounts.length + 1}`;
|
|||
|
|
if (useAccountsTable) {
|
|||
|
|
await query(
|
|||
|
|
'INSERT INTO building_personal_accounts (id, building_id, data) VALUES ($1, $2, $3)',
|
|||
|
|
[newAccount.id, buildingId, newAccount]
|
|||
|
|
);
|
|||
|
|
} else {
|
|||
|
|
const building = buildingRows[0].data;
|
|||
|
|
building.accounts = [...accounts, newAccount];
|
|||
|
|
building.isDirty = true;
|
|||
|
|
await query('UPDATE buildings SET data = $2 WHERE id = $1', [buildingId, building]);
|
|||
|
|
}
|
|||
|
|
invalidateBuildingsCache();
|
|||
|
|
res.status(201).json(newAccount);
|
|||
|
|
} catch (err) {
|
|||
|
|
console.error('Error creating account:', err);
|
|||
|
|
res.status(500).json({ error: 'Failed to create account' });
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// PUT /buildings/:id/accounts/:accountId — обновление в building_personal_accounts или в data (fallback)
|
|||
|
|
router.put('/buildings/:id/accounts/:accountId', async (req, res) => {
|
|||
|
|
const { id: buildingId, accountId } = req.params;
|
|||
|
|
const updatedAccount = req.body;
|
|||
|
|
if (!updatedAccount || !updatedAccount.id) {
|
|||
|
|
return res.status(400).json({ error: 'Ожидается объект PersonalAccount с полем id' });
|
|||
|
|
}
|
|||
|
|
try {
|
|||
|
|
const buildingRows = await query('SELECT data FROM buildings WHERE id = $1', [buildingId]);
|
|||
|
|
if (buildingRows.length === 0) {
|
|||
|
|
return res.status(404).json({ error: 'Building not found' });
|
|||
|
|
}
|
|||
|
|
let useAccountsTable = false;
|
|||
|
|
try {
|
|||
|
|
await query('SELECT 1 FROM building_personal_accounts LIMIT 1');
|
|||
|
|
useAccountsTable = true;
|
|||
|
|
} catch (_) {}
|
|||
|
|
if (useAccountsTable) {
|
|||
|
|
const updateResult = await query(
|
|||
|
|
'UPDATE building_personal_accounts SET data = $3 WHERE id = $1 AND building_id = $2 RETURNING id',
|
|||
|
|
[accountId, buildingId, updatedAccount]
|
|||
|
|
);
|
|||
|
|
if (updateResult.length === 0) {
|
|||
|
|
return res.status(404).json({ error: 'Account not found' });
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
const building = buildingRows[0].data;
|
|||
|
|
const accounts = Array.isArray(building.accounts) ? building.accounts : [];
|
|||
|
|
const idx = accounts.findIndex((a) => a.id === accountId);
|
|||
|
|
if (idx === -1) return res.status(404).json({ error: 'Account not found' });
|
|||
|
|
building.accounts = accounts.map((a) => (a.id === accountId ? updatedAccount : a));
|
|||
|
|
building.isDirty = true;
|
|||
|
|
await query('UPDATE buildings SET data = $2 WHERE id = $1', [buildingId, building]);
|
|||
|
|
}
|
|||
|
|
invalidateBuildingsCache();
|
|||
|
|
res.json(updatedAccount);
|
|||
|
|
} catch (err) {
|
|||
|
|
console.error('Error updating account:', err);
|
|||
|
|
res.status(500).json({ error: 'Failed to update account' });
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// DELETE /buildings/:id/accounts/:accountId — из building_personal_accounts или из data (fallback)
|
|||
|
|
router.delete('/buildings/:id/accounts/:accountId', async (req, res) => {
|
|||
|
|
const { id: buildingId, accountId } = req.params;
|
|||
|
|
try {
|
|||
|
|
const buildingRows = await query('SELECT data FROM buildings WHERE id = $1', [buildingId]);
|
|||
|
|
if (buildingRows.length === 0) {
|
|||
|
|
return res.status(404).json({ error: 'Building not found' });
|
|||
|
|
}
|
|||
|
|
let useAccountsTable = false;
|
|||
|
|
try {
|
|||
|
|
await query('SELECT 1 FROM building_personal_accounts LIMIT 1');
|
|||
|
|
useAccountsTable = true;
|
|||
|
|
} catch (_) {}
|
|||
|
|
if (useAccountsTable) {
|
|||
|
|
const deleteResult = await query(
|
|||
|
|
'DELETE FROM building_personal_accounts WHERE id = $1 AND building_id = $2 RETURNING id',
|
|||
|
|
[accountId, buildingId]
|
|||
|
|
);
|
|||
|
|
if (deleteResult.length === 0) {
|
|||
|
|
return res.status(404).json({ error: 'Account not found' });
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
const building = buildingRows[0].data;
|
|||
|
|
const accounts = Array.isArray(building.accounts) ? building.accounts : [];
|
|||
|
|
const filtered = accounts.filter((a) => a.id !== accountId);
|
|||
|
|
if (filtered.length === accounts.length) {
|
|||
|
|
return res.status(404).json({ error: 'Account not found' });
|
|||
|
|
}
|
|||
|
|
building.accounts = filtered;
|
|||
|
|
building.isDirty = true;
|
|||
|
|
await query('UPDATE buildings SET data = $2 WHERE id = $1', [buildingId, building]);
|
|||
|
|
}
|
|||
|
|
invalidateBuildingsCache();
|
|||
|
|
res.json({ success: true, message: 'Account deleted' });
|
|||
|
|
} catch (err) {
|
|||
|
|
console.error('Error deleting account:', err);
|
|||
|
|
res.status(500).json({ error: 'Failed to delete account' });
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
return router;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
module.exports = createBuildingsRouter;
|