Initial
This commit is contained in:
131
server/routes/admin.js
Normal file
131
server/routes/admin.js
Normal file
@@ -0,0 +1,131 @@
|
||||
const express = require('express');
|
||||
const bcrypt = require('bcryptjs');
|
||||
const { all, audit, now, one, run } = require('../db');
|
||||
const { requireRole } = require('../middleware/auth');
|
||||
const { isValidRole, roleCapabilities, rolePermissions, roles } = require('../services/accessControl');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
const userSelect = `
|
||||
SELECT u.id, u.team_id, t.name AS team_name, u.name, u.email, u.role, u.status, u.created_at, u.updated_at
|
||||
FROM users u
|
||||
LEFT JOIN teams t ON t.id = u.team_id
|
||||
`;
|
||||
|
||||
function publicAdminUser(id) {
|
||||
return one(`${userSelect} WHERE u.id = ?`, [id]);
|
||||
}
|
||||
|
||||
function validateRole(role) {
|
||||
if (!isValidRole(role)) throw Object.assign(new Error('Invalid role'), { status: 400 });
|
||||
}
|
||||
|
||||
router.get('/settings', requireRole('admin'), (req, res) => {
|
||||
const rows = all('SELECT * FROM app_settings ORDER BY key');
|
||||
res.json({ settings: Object.fromEntries(rows.map((row) => [row.key, row.value])) });
|
||||
});
|
||||
|
||||
router.put('/settings', requireRole('admin'), (req, res) => {
|
||||
for (const [key, value] of Object.entries(req.body.settings || req.body)) {
|
||||
run('INSERT INTO app_settings (key, value, updated_at) VALUES (?, ?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value, updated_at = excluded.updated_at', [
|
||||
key,
|
||||
String(value),
|
||||
now()
|
||||
]);
|
||||
}
|
||||
audit(req.user.id, 'update', 'settings', 'app', null, req.body);
|
||||
res.json({ settings: Object.fromEntries(all('SELECT * FROM app_settings ORDER BY key').map((row) => [row.key, row.value])) });
|
||||
});
|
||||
|
||||
router.get('/users', requireRole('admin'), (req, res) => {
|
||||
res.json({ users: all(`${userSelect} ORDER BY u.name`) });
|
||||
});
|
||||
|
||||
router.post('/users', requireRole('admin'), (req, res) => {
|
||||
validateRole(req.body.role || 'viewer');
|
||||
if (req.body.status && !['active', 'inactive'].includes(req.body.status)) return res.status(400).json({ error: 'Invalid status' });
|
||||
const created = now();
|
||||
const result = run(
|
||||
'INSERT INTO users (team_id, name, email, password_hash, role, status, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)',
|
||||
[
|
||||
req.body.team_id || one('SELECT id FROM teams ORDER BY id LIMIT 1')?.id,
|
||||
req.body.name,
|
||||
req.body.email,
|
||||
bcrypt.hashSync(req.body.password || 'ChangeMe123!', 12),
|
||||
req.body.role || 'viewer',
|
||||
req.body.status || 'active',
|
||||
created,
|
||||
created
|
||||
]
|
||||
);
|
||||
audit(req.user.id, 'create', 'user', result.lastInsertRowid, null, { ...req.body, password: '[redacted]' });
|
||||
res.status(201).json({ user: publicAdminUser(result.lastInsertRowid) });
|
||||
});
|
||||
|
||||
router.put('/users/:id', requireRole('admin'), (req, res) => {
|
||||
const before = publicAdminUser(req.params.id);
|
||||
if (!before) return res.status(404).json({ error: 'User not found' });
|
||||
const nextRole = req.body.role || before.role;
|
||||
validateRole(nextRole);
|
||||
|
||||
const nextStatus = req.body.status || before.status;
|
||||
if (!['active', 'inactive'].includes(nextStatus)) return res.status(400).json({ error: 'Invalid status' });
|
||||
|
||||
const activeAdmins = one("SELECT COUNT(*) AS count FROM users WHERE role = 'admin' AND status = 'active'").count;
|
||||
if (before.role === 'admin' && before.status === 'active' && activeAdmins <= 1 && (nextRole !== 'admin' || nextStatus !== 'active')) {
|
||||
return res.status(400).json({ error: 'At least one active admin is required' });
|
||||
}
|
||||
|
||||
const updated = now();
|
||||
run(
|
||||
`UPDATE users SET
|
||||
team_id = ?,
|
||||
name = ?,
|
||||
email = ?,
|
||||
role = ?,
|
||||
status = ?,
|
||||
updated_at = ?
|
||||
WHERE id = ?`,
|
||||
[
|
||||
req.body.team_id ?? before.team_id,
|
||||
req.body.name || before.name,
|
||||
req.body.email || before.email,
|
||||
nextRole,
|
||||
nextStatus,
|
||||
updated,
|
||||
req.params.id
|
||||
]
|
||||
);
|
||||
|
||||
if (req.body.password) {
|
||||
run('UPDATE users SET password_hash = ?, updated_at = ? WHERE id = ?', [
|
||||
bcrypt.hashSync(req.body.password, 12),
|
||||
updated,
|
||||
req.params.id
|
||||
]);
|
||||
}
|
||||
|
||||
const user = publicAdminUser(req.params.id);
|
||||
audit(req.user.id, 'update', 'user', req.params.id, before, { ...user, password: req.body.password ? '[changed]' : undefined });
|
||||
return res.json({ user });
|
||||
});
|
||||
|
||||
router.get('/teams', requireRole('admin'), (req, res) => {
|
||||
res.json({ teams: all('SELECT * FROM teams ORDER BY name') });
|
||||
});
|
||||
|
||||
router.post('/teams', requireRole('admin'), (req, res) => {
|
||||
const result = run('INSERT INTO teams (name, description, created_at) VALUES (?, ?, ?)', [
|
||||
req.body.name,
|
||||
req.body.description || null,
|
||||
now()
|
||||
]);
|
||||
audit(req.user.id, 'create', 'team', result.lastInsertRowid, null, req.body);
|
||||
res.status(201).json({ team: one('SELECT * FROM teams WHERE id = ?', [result.lastInsertRowid]) });
|
||||
});
|
||||
|
||||
router.get('/access-control', requireRole('admin'), (req, res) => {
|
||||
res.json({ roles, rolePermissions, roleCapabilities });
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
83
server/routes/assets.js
Normal file
83
server/routes/assets.js
Normal file
@@ -0,0 +1,83 @@
|
||||
const express = require('express');
|
||||
const multer = require('multer');
|
||||
const { audit, now, one, run } = require('../db');
|
||||
const { annualSchedule } = require('../depreciation');
|
||||
const { requireRole } = require('../middleware/auth');
|
||||
const {
|
||||
createAsset,
|
||||
deleteAsset,
|
||||
hydrateAsset,
|
||||
listAssets,
|
||||
massUpdateAssets,
|
||||
updateAsset
|
||||
} = require('../services/assets');
|
||||
const { activeRuleSet } = require('../services/reports');
|
||||
|
||||
const upload = multer({ storage: multer.memoryStorage(), limits: { fileSize: 16 * 1024 * 1024 } });
|
||||
const router = express.Router();
|
||||
|
||||
router.get('/assets', (req, res) => {
|
||||
res.json({ assets: listAssets(req.query) });
|
||||
});
|
||||
|
||||
router.post('/assets', requireRole('admin', 'finance', 'operations'), (req, res) => {
|
||||
res.status(201).json({ asset: createAsset(req.body, req.user.id) });
|
||||
});
|
||||
|
||||
router.get('/assets/:id', (req, res) => {
|
||||
const asset = hydrateAsset(req.params.id);
|
||||
if (!asset) return res.status(404).json({ error: 'Asset not found' });
|
||||
return res.json({ asset });
|
||||
});
|
||||
|
||||
router.put('/assets/:id', requireRole('admin', 'finance', 'operations'), (req, res) => {
|
||||
const asset = updateAsset(req.params.id, req.body, req.user.id);
|
||||
if (!asset) return res.status(404).json({ error: 'Asset not found' });
|
||||
return res.json({ asset });
|
||||
});
|
||||
|
||||
router.delete('/assets/:id', requireRole('admin', 'finance'), (req, res) => {
|
||||
if (!deleteAsset(req.params.id, req.user.id)) return res.status(404).json({ error: 'Asset not found' });
|
||||
return res.status(204).end();
|
||||
});
|
||||
|
||||
router.post('/assets/mass-update', requireRole('admin', 'finance'), (req, res) => {
|
||||
const ids = Array.isArray(req.body.ids) ? req.body.ids : [];
|
||||
const updated = massUpdateAssets(ids, req.body.changes || {}, req.user.id);
|
||||
if (!updated) return res.status(400).json({ error: 'Choose assets and editable fields' });
|
||||
return res.json({ updated });
|
||||
});
|
||||
|
||||
router.get('/assets/:id/depreciation', (req, res) => {
|
||||
const asset = hydrateAsset(req.params.id);
|
||||
if (!asset) return res.status(404).json({ error: 'Asset not found' });
|
||||
const rules = activeRuleSet();
|
||||
const startYear = Number(req.query.startYear || new Date().getFullYear());
|
||||
const endYear = Number(req.query.endYear || startYear + 5);
|
||||
const schedules = asset.books
|
||||
.filter((book) => book.active)
|
||||
.flatMap((book) => annualSchedule(asset, book, rules, { startYear, endYear }));
|
||||
res.json({ assetId: asset.asset_id, schedules });
|
||||
});
|
||||
|
||||
router.post('/assets/:id/files', upload.single('file'), requireRole('admin', 'finance', 'operations'), (req, res) => {
|
||||
if (!req.file) return res.status(400).json({ error: 'File is required' });
|
||||
const result = run(
|
||||
'INSERT INTO asset_files (asset_id, file_name, mime_type, size, content_base64, uploaded_by, uploaded_at) VALUES (?, ?, ?, ?, ?, ?, ?)',
|
||||
[req.params.id, req.file.originalname, req.file.mimetype, req.file.size, req.file.buffer.toString('base64'), req.user.id, now()]
|
||||
);
|
||||
audit(req.user.id, 'upload_file', 'asset', req.params.id, null, { file: req.file.originalname });
|
||||
res.status(201).json({ file: one('SELECT id, file_name, mime_type, size, uploaded_at FROM asset_files WHERE id = ?', [result.lastInsertRowid]) });
|
||||
});
|
||||
|
||||
router.post('/assets/:id/tasks', requireRole('admin', 'finance', 'operations'), (req, res) => {
|
||||
const created = now();
|
||||
const result = run(
|
||||
'INSERT INTO tasks (asset_id, title, type, status, due_date, assigned_to, notes, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)',
|
||||
[req.params.id, req.body.title, req.body.type || 'maintenance', req.body.status || 'open', req.body.due_date || null, req.body.assigned_to || null, req.body.notes || null, created, created]
|
||||
);
|
||||
audit(req.user.id, 'create', 'task', result.lastInsertRowid, null, req.body);
|
||||
res.status(201).json({ task: one('SELECT * FROM tasks WHERE id = ?', [result.lastInsertRowid]) });
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
47
server/routes/assignments.js
Normal file
47
server/routes/assignments.js
Normal file
@@ -0,0 +1,47 @@
|
||||
const express = require('express');
|
||||
const { requireRole } = require('../middleware/auth');
|
||||
const {
|
||||
assignAsset,
|
||||
assignmentRows,
|
||||
assignmentSummary,
|
||||
listEmployees,
|
||||
releaseAssignment,
|
||||
upsertEmployee
|
||||
} = require('../services/assignments');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
router.get('/employees', (req, res) => {
|
||||
res.json({ employees: listEmployees(req.query) });
|
||||
});
|
||||
|
||||
router.post('/employees', requireRole('admin', 'finance', 'operations'), (req, res) => {
|
||||
res.status(201).json({ employee: upsertEmployee({ ...req.body, source: req.body.source || 'manual' }) });
|
||||
});
|
||||
|
||||
router.get('/assignments', (req, res) => {
|
||||
res.json({
|
||||
summary: assignmentSummary(),
|
||||
assignments: assignmentRows({
|
||||
asset_id: req.query.asset_id || null,
|
||||
employee_id: req.query.employee_id || null,
|
||||
active: req.query.active === 'true'
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
router.post('/assignments', requireRole('admin', 'finance', 'operations'), (req, res, next) => {
|
||||
try {
|
||||
res.status(201).json({ assignment: assignAsset(req.body, req.user.id) });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
router.put('/assignments/:id/release', requireRole('admin', 'finance', 'operations'), (req, res) => {
|
||||
const assignment = releaseAssignment(req.params.id, req.body, req.user.id);
|
||||
if (!assignment) return res.status(404).json({ error: 'Assignment not found' });
|
||||
return res.json({ assignment });
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
26
server/routes/auth.js
Normal file
26
server/routes/auth.js
Normal file
@@ -0,0 +1,26 @@
|
||||
const express = require('express');
|
||||
const bcrypt = require('bcryptjs');
|
||||
const { audit, one } = require('../db');
|
||||
const { publicUser, tokenFor } = require('../middleware/auth');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
router.get('/public/bootstrap', (req, res) => {
|
||||
res.json({
|
||||
appName: 'MixedAssets',
|
||||
setupComplete: Boolean(one('SELECT id FROM users LIMIT 1')),
|
||||
database: 'sqlite'
|
||||
});
|
||||
});
|
||||
|
||||
router.post('/auth/login', (req, res) => {
|
||||
const { email, password } = req.body;
|
||||
const user = one('SELECT * FROM users WHERE lower(email) = lower(?) AND status = ?', [email || '', 'active']);
|
||||
if (!user || !bcrypt.compareSync(password || '', user.password_hash)) {
|
||||
return res.status(401).json({ error: 'Invalid email or password' });
|
||||
}
|
||||
audit(user.id, 'login', 'user', user.id, null, { email: user.email });
|
||||
return res.json({ token: tokenFor(user), user: publicUser(user) });
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
29
server/routes/dashboard.js
Normal file
29
server/routes/dashboard.js
Normal file
@@ -0,0 +1,29 @@
|
||||
const express = require('express');
|
||||
const { all, one } = require('../db');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
router.get('/dashboard', (req, res) => {
|
||||
const stats = one(`
|
||||
SELECT
|
||||
COUNT(*) AS asset_count,
|
||||
COALESCE(SUM(acquisition_cost), 0) AS total_cost,
|
||||
COALESCE(SUM(CASE WHEN status = 'disposed' THEN 1 ELSE 0 END), 0) AS disposed_count,
|
||||
COALESCE(SUM(CASE WHEN status = 'in_service' THEN 1 ELSE 0 END), 0) AS in_service_count
|
||||
FROM assets
|
||||
`);
|
||||
const byCategory = all('SELECT category, COUNT(*) AS count, COALESCE(SUM(acquisition_cost), 0) AS value FROM assets GROUP BY category ORDER BY value DESC');
|
||||
const byStatus = all('SELECT status, COUNT(*) AS count FROM assets GROUP BY status ORDER BY count DESC');
|
||||
const upcomingTasks = all(`
|
||||
SELECT t.*, a.asset_id, a.description
|
||||
FROM tasks t
|
||||
LEFT JOIN assets a ON a.id = t.asset_id
|
||||
WHERE t.status != 'complete'
|
||||
ORDER BY t.due_date IS NULL, t.due_date
|
||||
LIMIT 8
|
||||
`);
|
||||
const auditTrail = all('SELECT al.*, u.name AS actor_name FROM audit_logs al LEFT JOIN users u ON u.id = al.actor_id ORDER BY al.id DESC LIMIT 10');
|
||||
res.json({ stats, byCategory, byStatus, upcomingTasks, auditTrail });
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
27
server/routes/dataPortability.js
Normal file
27
server/routes/dataPortability.js
Normal file
@@ -0,0 +1,27 @@
|
||||
const express = require('express');
|
||||
const multer = require('multer');
|
||||
const { audit } = require('../db');
|
||||
const { requireRole } = require('../middleware/auth');
|
||||
const { upsertImportedAssets } = require('../services/assets');
|
||||
const { assetExport, extensionForUpload, rowsFromImport } = require('../services/importExport');
|
||||
|
||||
const upload = multer({ storage: multer.memoryStorage(), limits: { fileSize: 16 * 1024 * 1024 } });
|
||||
const router = express.Router();
|
||||
|
||||
router.get('/export/assets', async (req, res) => {
|
||||
const format = String(req.query.format || 'json').toLowerCase();
|
||||
const output = await assetExport(format);
|
||||
res.setHeader('Content-Type', output.contentType);
|
||||
res.setHeader('Content-Disposition', `attachment; filename="${output.filename}"`);
|
||||
return res.send(output.body);
|
||||
});
|
||||
|
||||
router.post('/import/assets', requireRole('admin', 'finance'), upload.single('file'), async (req, res) => {
|
||||
if (!req.file) return res.status(400).json({ error: 'File is required' });
|
||||
const rows = await rowsFromImport(extensionForUpload(req.file.originalname), req.file.buffer);
|
||||
const imported = upsertImportedAssets(rows, req.user.id);
|
||||
audit(req.user.id, 'import', 'asset', null, null, { file: req.file.originalname, imported });
|
||||
res.json({ imported });
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
17
server/routes/profile.js
Normal file
17
server/routes/profile.js
Normal file
@@ -0,0 +1,17 @@
|
||||
const express = require('express');
|
||||
const { audit } = require('../db');
|
||||
const { getProfile, savePreferences } = require('../services/profile');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
router.get('/profile', (req, res) => {
|
||||
res.json(getProfile(req.user));
|
||||
});
|
||||
|
||||
router.put('/profile', (req, res) => {
|
||||
const preferences = savePreferences(req.user.id, req.body.preferences || {});
|
||||
audit(req.user.id, 'update', 'profile_preferences', req.user.id, null, { preferences });
|
||||
res.json({ user: getProfile(req.user).user, preferences });
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
34
server/routes/reference.js
Normal file
34
server/routes/reference.js
Normal file
@@ -0,0 +1,34 @@
|
||||
const express = require('express');
|
||||
const { all, audit, now, one, parseJson, run } = require('../db');
|
||||
const { requireRole } = require('../middleware/auth');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
router.get('/entities', (req, res) => {
|
||||
res.json({ entities: all('SELECT * FROM entities ORDER BY name') });
|
||||
});
|
||||
|
||||
router.post('/entities', requireRole('admin', 'finance'), (req, res) => {
|
||||
const created = now();
|
||||
const result = run(
|
||||
'INSERT INTO entities (name, legal_name, tax_id, fiscal_year_end_month, base_currency, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?)',
|
||||
[
|
||||
req.body.name,
|
||||
req.body.legal_name || null,
|
||||
req.body.tax_id || null,
|
||||
Number(req.body.fiscal_year_end_month || 12),
|
||||
req.body.base_currency || 'USD',
|
||||
created,
|
||||
created
|
||||
]
|
||||
);
|
||||
audit(req.user.id, 'create', 'entity', result.lastInsertRowid, null, req.body);
|
||||
res.status(201).json({ entity: one('SELECT * FROM entities WHERE id = ?', [result.lastInsertRowid]) });
|
||||
});
|
||||
|
||||
router.get('/references', (req, res) => {
|
||||
const rows = all('SELECT * FROM reference_values ORDER BY type, name');
|
||||
res.json({ references: rows.map((row) => ({ ...row, metadata: parseJson(row.metadata, {}) })) });
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
28
server/routes/reports.js
Normal file
28
server/routes/reports.js
Normal file
@@ -0,0 +1,28 @@
|
||||
const express = require('express');
|
||||
const {
|
||||
depreciationReport,
|
||||
monthlyDepreciationReport,
|
||||
writeDepreciationPdf
|
||||
} = require('../services/reports');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
router.get('/reports/depreciation', (req, res) => {
|
||||
const year = Number(req.query.year || new Date().getFullYear());
|
||||
const bookType = req.query.book || 'GAAP';
|
||||
res.json(depreciationReport(year, bookType));
|
||||
});
|
||||
|
||||
router.get('/reports/monthly-depreciation', (req, res) => {
|
||||
const year = Number(req.query.year || new Date().getFullYear());
|
||||
const bookType = req.query.book || 'GAAP';
|
||||
res.json(monthlyDepreciationReport(year, bookType));
|
||||
});
|
||||
|
||||
router.get('/reports/depreciation.pdf', (req, res) => {
|
||||
const year = Number(req.query.year || new Date().getFullYear());
|
||||
const book = req.query.book || 'GAAP';
|
||||
writeDepreciationPdf(res, year, book);
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
34
server/routes/taxRules.js
Normal file
34
server/routes/taxRules.js
Normal file
@@ -0,0 +1,34 @@
|
||||
const express = require('express');
|
||||
const { all, audit, now, one, parseJson, run } = require('../db');
|
||||
const { requireRole } = require('../middleware/auth');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
router.get('/tax-rules', (req, res) => {
|
||||
const ruleSets = all('SELECT * FROM tax_rule_sets ORDER BY active DESC, effective_date DESC, id DESC').map((row) => ({
|
||||
...row,
|
||||
active: Boolean(row.active),
|
||||
rules_json: parseJson(row.rules_json, {})
|
||||
}));
|
||||
res.json({ ruleSets });
|
||||
});
|
||||
|
||||
router.post('/tax-rules', requireRole('admin', 'finance'), (req, res) => {
|
||||
const result = run(
|
||||
'INSERT INTO tax_rule_sets (jurisdiction, name, effective_date, version, rules_json, active, source_note, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)',
|
||||
[
|
||||
req.body.jurisdiction,
|
||||
req.body.name,
|
||||
req.body.effective_date,
|
||||
req.body.version,
|
||||
JSON.stringify(req.body.rules_json || req.body.rules || {}),
|
||||
req.body.active === false ? 0 : 1,
|
||||
req.body.source_note || null,
|
||||
now()
|
||||
]
|
||||
);
|
||||
audit(req.user.id, 'create', 'tax_rule_set', result.lastInsertRowid, null, req.body);
|
||||
res.status(201).json({ ruleSet: one('SELECT * FROM tax_rule_sets WHERE id = ?', [result.lastInsertRowid]) });
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
26
server/routes/templates.js
Normal file
26
server/routes/templates.js
Normal file
@@ -0,0 +1,26 @@
|
||||
const express = require('express');
|
||||
const { all, audit, json, now, one, parseJson, run } = require('../db');
|
||||
const { requireRole } = require('../middleware/auth');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
router.get('/templates', (req, res) => {
|
||||
const templates = all('SELECT * FROM asset_templates ORDER BY name').map((row) => ({
|
||||
...row,
|
||||
defaults: parseJson(row.defaults, {}),
|
||||
custom_fields: parseJson(row.custom_fields, [])
|
||||
}));
|
||||
res.json({ templates });
|
||||
});
|
||||
|
||||
router.post('/templates', requireRole('admin', 'finance'), (req, res) => {
|
||||
const created = now();
|
||||
const result = run(
|
||||
'INSERT INTO asset_templates (name, description, defaults, custom_fields, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)',
|
||||
[req.body.name, req.body.description || null, json(req.body.defaults || {}), json(req.body.custom_fields || []), created, created]
|
||||
);
|
||||
audit(req.user.id, 'create', 'asset_template', result.lastInsertRowid, null, req.body);
|
||||
res.status(201).json({ template: one('SELECT * FROM asset_templates WHERE id = ?', [result.lastInsertRowid]) });
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
32
server/routes/workday.js
Normal file
32
server/routes/workday.js
Normal file
@@ -0,0 +1,32 @@
|
||||
const express = require('express');
|
||||
const { requireRole } = require('../middleware/auth');
|
||||
const {
|
||||
getConnection,
|
||||
importWorkersFromPayload,
|
||||
saveConnection,
|
||||
syncWorkers
|
||||
} = require('../services/workday');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
router.get('/workday/connection', requireRole('admin'), (req, res) => {
|
||||
res.json({ connection: getConnection() });
|
||||
});
|
||||
|
||||
router.put('/workday/connection', requireRole('admin'), (req, res) => {
|
||||
res.json({ connection: saveConnection(req.body, req.user.id) });
|
||||
});
|
||||
|
||||
router.post('/workday/sync-workers', requireRole('admin'), async (req, res, next) => {
|
||||
try {
|
||||
res.json(await syncWorkers(req.user.id));
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/workday/import-workers', requireRole('admin'), (req, res) => {
|
||||
res.json(importWorkersFromPayload(req.body.workers || req.body, req.user.id));
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
Reference in New Issue
Block a user