mirror of
https://github.com/wekan/wekan.git
synced 2026-11-07 02:52:33 -05:00
137 lines
5.9 KiB
JavaScript
137 lines
5.9 KiB
JavaScript
'use strict';
|
|
|
|
// What the REST API is being used for: who called what, and how often.
|
|
//
|
|
// THE QUESTION IT ANSWERS. Admin Panel → Problems shows what went wrong; this
|
|
// shows what is being DONE. An admin wants to know that `POST /api/boards` was
|
|
// called 34 times by alice last month and 812 times by nobody-in-particular
|
|
// yesterday - which endpoints are used, by whom, from where, and whether that
|
|
// changed. Without it the only record of API use is whatever the reverse proxy
|
|
// happens to keep, and most instances have none.
|
|
//
|
|
// A SUMMARY, like everything else on that page: one row per (account, endpoint),
|
|
// with a count and the window it covers, never a row per request. The identity
|
|
// here DOES include the account, which is the one deliberate exception to the
|
|
// rule in models/lib/eventLogSummary.js - for every other stream the question is
|
|
// "what is happening" and the actor would multiply the rows, but here "who
|
|
// called what" IS the report, and the cardinality is bounded by real accounts
|
|
// times real endpoints rather than by whoever is sending.
|
|
//
|
|
// THE NAME IS THE ROUTE PATTERN, NOT THE PATH. `/api/boards/:boardId/lists` is
|
|
// one endpoint; `/api/boards/abc123/lists` and ten thousand of its siblings are
|
|
// one endpoint being used. Recording the concrete path would put a row per board
|
|
// in the collection, which is the cost this whole design removes - so a request
|
|
// that matched no route at all is recorded under a single `(no route)` name
|
|
// rather than under the path it invented.
|
|
//
|
|
// This module is the part with no Meteor in it: naming, and the accumulator that
|
|
// keeps writes bounded. server/lib/apiUsageLog.js is the plumbing.
|
|
|
|
// How many distinct (account, endpoint) pairs are tracked before the rest are
|
|
// counted together. Real use is far under this - an instance has tens of
|
|
// endpoints and, in the API's case, few accounts using them - so reaching it
|
|
// means something unusual, which is what the overflow row is for.
|
|
const MAX_TRACKED = 500;
|
|
|
|
// The name for a request that matched no route. ONE name for all of them: a 404
|
|
// sweep is an attacker walking a wordlist, and a row per guessed path would be
|
|
// the log-per-event bug arriving through the back door. The count still says it
|
|
// happened, and how much.
|
|
const UNMATCHED = '(no route)';
|
|
|
|
// The name of the endpoint a request reached.
|
|
//
|
|
// `routePath` is what the router matched - Express fills `req.route.path` once a
|
|
// handler runs, and that is the pattern with its `:params` intact, which is
|
|
// exactly the name wanted. Everything else is a fallback.
|
|
function apiName(method, routePath) {
|
|
const verb = String(method || 'GET').toUpperCase();
|
|
// No route matched: one name for all of them, never the path that was tried.
|
|
return `${verb} ${routePath || UNMATCHED}`;
|
|
}
|
|
|
|
// Is this a request the report is about? Only /api - the client's own DDP
|
|
// traffic, the static assets and the health check are not API use, and counting
|
|
// them would bury the thing being looked for.
|
|
function isApiRequest(url) {
|
|
const path = String(url || '').split('?')[0];
|
|
return path === '/api' || path.startsWith('/api/');
|
|
}
|
|
|
|
// The key one row accumulates under: the account and the endpoint, and nothing
|
|
// else. The ADDRESS is deliberately not part of it - it is tallied inside the
|
|
// row like every other actor, so one account calling from four addresses is one
|
|
// row that names four addresses rather than four rows.
|
|
function usageKey(name, username) {
|
|
return `${username || ''} |