diff --git a/.env.sample b/.env.sample index a2480472..89fef601 100644 --- a/.env.sample +++ b/.env.sample @@ -46,3 +46,4 @@ PROXY_URL= # To use only if you are behind a proxy DISTRICT_TO_SNAPSHOT= # Comma separated list of district to snapshot (used only for dev toolbox) MATOMO_URL= MATOMO_SITE_ID= +MATOMO_TOKEN_AUTH= diff --git a/docker-compose.yml b/docker-compose.yml index 28ca2a26..8c7589e0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,7 +7,7 @@ services: volumes: - db-mongo:/data/db db-postgres: - build: + build: dockerfile: docker-resources/postgres/Dockerfile.dev ports: - "${POSTGRES_PORT:-5432}:5432" @@ -23,7 +23,7 @@ services: ports: - "${REDIS_PORT:-6379}:6379" api: - build: + build: dockerfile: docker-resources/api/Dockerfile.dev depends_on: - db-mongo @@ -53,6 +53,7 @@ services: - JOB_STATUS_LIMIT_DURATION=${JOB_STATUS_LIMIT_DURATION} - MATOMO_URL=${MATOMO_URL} - MATOMO_SITE_ID=${MATOMO_SITE_ID} + - MATOMO_TOKEN_AUTH=${MATOMO_TOKEN_AUTH} - FORCE_DOWNLOAD_CONTOUR= - FORCE_DOWNLOAD_DATASETS= ports: @@ -69,4 +70,4 @@ volumes: data: dist: db-mongo: - db-postgres: \ No newline at end of file + db-postgres: diff --git a/lib/api/legacy-routes.cjs b/lib/api/legacy-routes.cjs index f654a8fa..912a20e0 100644 --- a/lib/api/legacy-routes.cjs +++ b/lib/api/legacy-routes.cjs @@ -32,6 +32,7 @@ const { ADMIN_TOKEN = '', API_IDFIX_URL = 'https://plateforme.adresse.data.gouv.fr/api-idfix', API_IDFIX_TOKEN = '', + BAN_API_URL = '', } = process.env const prepareMethode = { @@ -56,10 +57,25 @@ const getLegacyTypeFromId = id => { } } -let sendToTracker = () => console.warn('Legacy-routes: sendToTracker not yet implemented') -import('../util/analytics-tracker.js').then(s => { - sendToTracker = s.sendToTracker -}) +const sendToTracker = async ({url: paramUrl, download: paramDownload, ...params} = {}, ...args) => { + const module = await import('../util/analytics-tracker.js') + if (!module.sendToTracker) { + console.warn('Legacy-routes: sendToTracker not found') + return + } + + const url = paramUrl && `${BAN_API_URL}${paramUrl}` + const download = paramDownload && `${BAN_API_URL}${paramDownload}` + + return module.sendToTracker( + { + ...(url ? {url} : {}), + ...(download ? {download} : {}), + ...params + }, + ...args) +} + const trackEventDefault = { category: 'API Legacy', value: 1, @@ -133,12 +149,13 @@ const analyticsMiddleware = { next?.() }, downloadCommuneData(req, res, next) { - const {url} = req + const {url, commune = {}} = req const {codeCommune, downloadFormat, downloadType} = req.params + const nomCommune = commune?.nomCommune const trackEvent = { - category: 'Download Commune DATA API (From Legacy)', - action: `Download ${downloadType} (Format ${downloadFormat})`, - name: codeCommune, + category: 'Download', + action: `Download Commune ${downloadType} (Format ${downloadFormat})`, + name: `${codeCommune}${nomCommune ? ` - ${nomCommune}` : ''}`, value: 1, } sendToTracker({url, download: url, trackEvent}) @@ -186,6 +203,7 @@ app.param( }) ) +// API Download Commune Data app.get( '/ban/communes/:codeCommune/download/:downloadFormat/:downloadType', analyticsMiddleware.downloadCommuneData, @@ -213,6 +231,7 @@ app.get( }) ) +// API-Legacy app.post( '/ban/communes/:codeCommune/compose', ensureIsAdmin, @@ -367,6 +386,7 @@ app.get( }) ) +// BAN Stats app.get( '/ban/stats', w(async (req, res) => { @@ -375,23 +395,26 @@ app.get( }) ) -app.post('/ban/stats', w(async (req, res) => { - const {codesCommune} = req.body - if (!codesCommune || codesCommune.length === 0) { - return res - .status(404) - .send({code: 404, message: 'Paramètre codesCommune manquant ou invalide'}) - } +app.post( + '/ban/stats', + w(async (req, res) => { + const {codesCommune} = req.body + if (!codesCommune || codesCommune.length === 0) { + return res + .status(404) + .send({code: 404, message: 'Paramètre codesCommune manquant ou invalide'}) + } - try { - const stats = await computeFilteredStats(codesCommune) - res.send(stats) - } catch (error) { - console.error(error) - res.status(500).send({code: 500, message: 'Une erreur est survenu lors du calcul des statistiques'}) - } -})) + try { + const stats = await computeFilteredStats(codesCommune) + res.send(stats) + } catch (error) { + console.error(error) + res.status(500).send({code: 500, message: 'Une erreur est survenu lors du calcul des statistiques'}) + } + })) +// BAN Carto Tiles app.get( '/tiles/ban/:z/:x/:y.pbf', w(async (req, res) => { diff --git a/lib/util/analytics-tracker.js b/lib/util/analytics-tracker.js index b625954d..31ce8679 100644 --- a/lib/util/analytics-tracker.js +++ b/lib/util/analytics-tracker.js @@ -1,51 +1,66 @@ /* eslint-disable camelcase */ -import os from 'node:os' -import MatomoTracker from 'matomo-tracker' - -const {MATOMO_URL, MATOMO_SITE_ID, NODE_ENV} = process.env - -let MATOMO_ERROR = false +// CF. Tracking HTTP API documentation : +// https://developer.matomo.org/api-reference/tracking-api -let matomo +import os from 'node:os' +import fetch from '../util/fetch.cjs' -const isDevMode = () => NODE_ENV !== 'production' +const { + MATOMO_URL, + MATOMO_SITE_ID, + MATOMO_TOKEN_AUTH, + NODE_ENV +} = process.env +const isDevMode = NODE_ENV !== 'production' +const matomoUrl = `${MATOMO_URL}/matomo.php` const logMatomoError = err => { - MATOMO_ERROR = true - console.warn('[WARNING] tracking request:', err) + console.log('error tracking request:', err) } -const matomoUrl = `${MATOMO_URL}/matomo.php` +const encodeParams = params => Object.fromEntries( + Object + .entries(params || {}) + .map(([key, value]) => [key, typeof value === 'string' ? encodeURIComponent(value) : value]), +) -try { - const matomo = new MatomoTracker(MATOMO_SITE_ID, matomoUrl) - matomo.on('error', logMatomoError) -} catch (error) { - logMatomoError(error) -} +export const getTrackEvent = ({category, action, name, value = 1}) => ({ + e_c: encodeURIComponent(`${isDevMode ? 'DEVMODE - ' : ''}${category}`), // Category name + e_a: encodeURIComponent(action), // Action name + e_n: encodeURIComponent(name), // Name + e_v: value, // Value +}) -export const getTrackEvent = ({category, action, name, value}) => { - const DEVMODE = isDevMode() +export const sendToTracker = async (params = {}) => { + const {url, ua, download, trackEvent, ...otherParams} = params + const safeOtherParams = encodeParams(otherParams) + const requiredParams = { + idsite: MATOMO_SITE_ID, + rec: 1, + ua: ua || encodeURIComponent(`${os.hostname()} / Node.JS ${process.version} / ${os.version()}`), + ...(MATOMO_TOKEN_AUTH ? {token_auth: MATOMO_TOKEN_AUTH} : {}), + } + const urlSearchParams = new URLSearchParams({ + ...requiredParams, + ...safeOtherParams, + ...(url ? {url: encodeURIComponent(url)} : {}), + ...(download ? {download: encodeURIComponent(download)} : {}), + ...(trackEvent ? getTrackEvent(trackEvent) : {}), + }) - // CF. Tracking HTTP API documentation : - // https://developer.matomo.org/api-reference/tracking-api + try { + const sentToMatomoWithHTTP = await fetch(matomoUrl, { + method: 'POST', + body: urlSearchParams, + }) - return { - e_c: `${DEVMODE ? 'DEVMODE - ' : ''}${category}`, // Category name - e_a: action, // Action name - e_n: name, // Name - ...(value ? {e_v: value} : {}), // Value + if (sentToMatomoWithHTTP.status !== 200) { + throw new Error(`Matomo HTTP API returned ${sentToMatomoWithHTTP.status}`) + } + } catch (error) { + logMatomoError(error) } } -export const sendToTracker = ({url, download, trackEvent}) => - !MATOMO_ERROR - && matomo?.track({ - ua: `${os.hostname()} / Node.JS ${process.version} / ${os.version()}`, - ...(url ? {url} : {}), - ...(download ? {download} : {}), - ...(trackEvent ? getTrackEvent(trackEvent) : {}) - }) - export default sendToTracker diff --git a/package.json b/package.json index 7270502b..b75ca3a1 100644 --- a/package.json +++ b/package.json @@ -67,7 +67,6 @@ "leven": "^3.1.0", "lodash": "^4.17.21", "lru-cache": "^6.0.0", - "matomo-tracker": "^2.2.4", "mongodb": "^4.7.0", "morgan": "^1.10.0", "ms": "^2.1.3", diff --git a/yarn.lock b/yarn.lock index 0519140d..ac56d731 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6303,11 +6303,6 @@ map-obj@^4.1.0: resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-4.3.0.tgz#9304f906e93faae70880da102a9f1df0ea8bb05a" integrity sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ== -matomo-tracker@^2.2.4: - version "2.2.4" - resolved "https://registry.yarnpkg.com/matomo-tracker/-/matomo-tracker-2.2.4.tgz#ee397d915d7b2e7964996ca28a0a03f4f0692453" - integrity sha512-7fDy4wRhDQ1dnSxVqmnVqmmos9ACKag0fCBtBD3/Qeoqks7MFqXcO35nfS6S8xU/IXNf7534q/4Gx8fuWKYW6A== - media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"