fix: P0/P1 fixes (auth+limit+schema) + project audit docs
This commit is contained in:
+2
-2
@@ -29,8 +29,8 @@ function createApiRouter({ auth, q }) {
|
||||
|
||||
// Все /api/v1/* требуют Bearer — 401 JSON если нет токена
|
||||
router.use(requireBearer);
|
||||
// Валидация токена — переиспользуем auth.middleware из src/auth.js
|
||||
router.use(auth.middleware);
|
||||
// Валидация токена — Bearer-only (игнорирует сессию, всегда проверяет JWT)
|
||||
router.use(auth.bearerMiddleware);
|
||||
|
||||
router.use('/entries', createEntriesRouter({ q }));
|
||||
router.use('/', createAdminRouter({ q }));
|
||||
|
||||
@@ -26,15 +26,17 @@ function createAdminRouter({ q }) {
|
||||
|
||||
// PATCH /api/v1/companies/:id/limit — установить лимит (admin only)
|
||||
router.patch('/companies/:id/limit', apiRequireAdmin, json, async (req, res) => {
|
||||
const limit = parseInt((req.body || {}).limit, 10);
|
||||
if (isNaN(limit) || limit < 0) {
|
||||
return res.status(400).json({ error: 'limit must be non-negative integer' });
|
||||
const rawLimit = (req.body || {}).limit;
|
||||
const isReset = rawLimit === null || rawLimit === undefined;
|
||||
const limit = isReset ? null : parseInt(rawLimit, 10);
|
||||
if (!isReset && (isNaN(limit) || limit < 0)) {
|
||||
return res.status(400).json({ error: 'limit must be non-negative integer or null to reset' });
|
||||
}
|
||||
try {
|
||||
await q.setLimit(req.params.id, limit);
|
||||
res.json({ ok: true, limit });
|
||||
} catch (e) {
|
||||
res.status(500).json({ error: e.message });
|
||||
res.status(e.status || 500).json({ error: e.message });
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
+21
-1
@@ -93,7 +93,8 @@ async function initAuth() {
|
||||
|
||||
return {
|
||||
isOidc,
|
||||
middleware: createMiddleware(isOidc),
|
||||
middleware: createMiddleware(isOidc),
|
||||
bearerMiddleware: createBearerMiddleware(isOidc),
|
||||
// OIDC-хелперы (используются в src/routes/auth.js)
|
||||
buildAuthUrl,
|
||||
exchangeCode,
|
||||
@@ -252,6 +253,25 @@ function userFromPayload(payload) {
|
||||
};
|
||||
}
|
||||
|
||||
// ── Bearer-only middleware (для /api/v1) ────────────────────────────────────
|
||||
// Не смотрит на сессию — всегда проверяет Bearer JWT явно.
|
||||
// Используется в src/api/index.js вместо auth.middleware.
|
||||
function createBearerMiddleware(isOidc) {
|
||||
return function bearerAuthMiddleware(req, res, next) {
|
||||
const bearer = (req.headers.authorization || '').replace(/^Bearer\s+/i, '').trim();
|
||||
if (!bearer) {
|
||||
return res.status(401).json({ error: 'Bearer token required' });
|
||||
}
|
||||
try {
|
||||
const payload = isOidc ? verifyOidcToken(bearer) : verifyMockToken(bearer);
|
||||
req.user = userFromPayload(payload);
|
||||
return next();
|
||||
} catch (e) {
|
||||
return res.status(401).json({ error: 'Invalid token: ' + e.message });
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// ── requireAdmin middleware ──────────────────────────────────────────────────
|
||||
/**
|
||||
* Middleware-защита для admin-only маршрутов.
|
||||
|
||||
+6
-1
@@ -187,10 +187,15 @@ async function getAllCompanies() {
|
||||
* @param {number|null} newLimit — новый лимит или null для сброса
|
||||
*/
|
||||
async function setLimit(companyId, newLimit) {
|
||||
await pool.query(
|
||||
const result = await pool.query(
|
||||
'UPDATE companies SET custom_limit = $1, updated_at = NOW() WHERE id = $2',
|
||||
[newLimit, companyId]
|
||||
);
|
||||
if (result.rowCount === 0) {
|
||||
const err = new Error('Company not found: ' + companyId);
|
||||
err.status = 404;
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
// ── Export ──
|
||||
|
||||
Reference in New Issue
Block a user