Auth: assignable-users endpoint + maintenance app seed row
GET /api/auth/users?app=<slug> — non-admin user list for task allocation (requester must have access to the app; returns active users with access, minimal fields). Seed row for the maintenance app (LXC 121). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
608ed4ce19
commit
a03b065c43
2 changed files with 55 additions and 1 deletions
|
|
@ -125,7 +125,8 @@ export async function initDb() {
|
||||||
('hk-planner', 'HK Planner', 'Housekeeping workload and hours planning', '/hk-planner', 'CalendarClock', '#2d6a4f', 'Housekeeping', '10.10.10.118', 3080),
|
('hk-planner', 'HK Planner', 'Housekeeping workload and hours planning', '/hk-planner', 'CalendarClock', '#2d6a4f', 'Housekeeping', '10.10.10.118', 3080),
|
||||||
('twin-optimiser', 'Twin Optimiser', 'Identify twin room opportunities from booking grid', '/twin-optimiser', 'LayoutGrid', '#c9841a', 'Housekeeping', '10.10.10.119', 3080),
|
('twin-optimiser', 'Twin Optimiser', 'Identify twin room opportunities from booking grid', '/twin-optimiser', 'LayoutGrid', '#c9841a', 'Housekeeping', '10.10.10.119', 3080),
|
||||||
('forecasting', 'Forecasting', 'Revenue forecasting and reporting', '/forecast', 'TrendingUp', '#0077b6', 'Finance', '10.10.10.113', 3080),
|
('forecasting', 'Forecasting', 'Revenue forecasting and reporting', '/forecast', 'TrendingUp', '#0077b6', 'Finance', '10.10.10.113', 3080),
|
||||||
('rates', 'Rate Scraper', 'Competitor rate monitoring', '/rates', 'Tag', '#7b4f00', 'Finance', '10.10.10.115', 3080)
|
('rates', 'Rate Scraper', 'Competitor rate monitoring', '/rates', 'Tag', '#7b4f00', 'Finance', '10.10.10.115', 3080),
|
||||||
|
('maintenance', 'Maintenance', 'Maintenance log book — faults, recurring tasks, assets and contractors', '/maintenance', 'Wrench', '#b45309', 'Operations', '10.10.10.121', 3080)
|
||||||
ON CONFLICT (slug) DO UPDATE SET
|
ON CONFLICT (slug) DO UPDATE SET
|
||||||
icon = EXCLUDED.icon,
|
icon = EXCLUDED.icon,
|
||||||
category = EXCLUDED.category,
|
category = EXCLUDED.category,
|
||||||
|
|
|
||||||
|
|
@ -227,4 +227,57 @@ export async function authRoutes(app) {
|
||||||
caps,
|
caps,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// GET /api/auth/users?app=slug
|
||||||
|
// Assignable-user list for apps (e.g. maintenance task allocation). Any user
|
||||||
|
// with access to the requested app may call it; returns the active users who
|
||||||
|
// can access that app (minimal fields only — this is not the admin list).
|
||||||
|
app.get('/users', async (request, reply) => {
|
||||||
|
const token = request.cookies?.hnf_session
|
||||||
|
if (!token) return reply.status(401).send({ error: 'Not authenticated' })
|
||||||
|
|
||||||
|
let payload
|
||||||
|
try { payload = await verifyToken(token) }
|
||||||
|
catch { return reply.status(401).send({ error: 'Invalid session' }) }
|
||||||
|
|
||||||
|
const { app: appSlug } = request.query
|
||||||
|
if (!appSlug) return reply.status(400).send({ error: 'app query parameter required' })
|
||||||
|
|
||||||
|
// Requester must themselves have access to the app
|
||||||
|
const { rows: [requester] } = await pool.query(
|
||||||
|
`SELECT u.id FROM users u
|
||||||
|
WHERE u.id = $1 AND u.active = true
|
||||||
|
AND (
|
||||||
|
u.is_admin = true
|
||||||
|
OR EXISTS (SELECT 1 FROM user_app_perms p
|
||||||
|
JOIN apps a ON a.id = p.app_id
|
||||||
|
WHERE p.user_id = u.id AND a.slug = $2 AND a.active = true)
|
||||||
|
OR EXISTS (SELECT 1 FROM user_roles ur
|
||||||
|
JOIN role_app_perms rap ON rap.role_id = ur.role_id
|
||||||
|
JOIN apps a ON a.id = rap.app_id
|
||||||
|
WHERE ur.user_id = u.id AND a.slug = $2 AND a.active = true)
|
||||||
|
)`,
|
||||||
|
[payload.user_id, appSlug]
|
||||||
|
)
|
||||||
|
if (!requester) return reply.status(403).send({ error: 'Access denied' })
|
||||||
|
|
||||||
|
const { rows: users } = await pool.query(
|
||||||
|
`SELECT DISTINCT u.id, u.email, u.name
|
||||||
|
FROM users u
|
||||||
|
WHERE u.active = true
|
||||||
|
AND (
|
||||||
|
u.is_admin = true
|
||||||
|
OR EXISTS (SELECT 1 FROM user_app_perms p
|
||||||
|
JOIN apps a ON a.id = p.app_id
|
||||||
|
WHERE p.user_id = u.id AND a.slug = $1 AND a.active = true)
|
||||||
|
OR EXISTS (SELECT 1 FROM user_roles ur
|
||||||
|
JOIN role_app_perms rap ON rap.role_id = ur.role_id
|
||||||
|
JOIN apps a ON a.id = rap.app_id
|
||||||
|
WHERE ur.user_id = u.id AND a.slug = $1 AND a.active = true)
|
||||||
|
)
|
||||||
|
ORDER BY u.name`,
|
||||||
|
[appSlug]
|
||||||
|
)
|
||||||
|
return users
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue