const crypto = require('crypto'); const geoip = require('fxa-geodb')(); const fetch = require('node-fetch'); const config = require('./config'); const pkg = require('../package.json'); const HOUR = 1000 * 60 * 60; function truncateToHour(timestamp) { return Math.floor(timestamp / HOUR) * HOUR; } function orderOfMagnitude(n) { return Math.floor(Math.log10(n)); } function userId(fileId, ownerId) { const hash = crypto.createHash('sha256'); hash.update(fileId); hash.update(ownerId); return hash.digest('hex').substring(32); } function location(ip) { try { return geoip(ip); } catch (e) { return {}; } } function statUploadEvent(data) { const loc = location(data.ip); const event = { session_id: -1, country: loc.country, region: loc.state, user_id: userId(data.id, data.owner), app_version: pkg.version, time: truncateToHour(Date.now()), event_type: 'server_upload', user_properties: { download_limit: data.dlimit, time_limit: data.timeLimit, size: orderOfMagnitude(data.size), anonymous: data.anonymous }, event_properties: { agent: data.agent }, event_id: 0 }; return sendBatch([event]); } function statDownloadEvent(data) { const loc = location(data.ip); const event = { session_id: -1, country: loc.country, region: loc.state, user_id: userId(data.id, data.owner), app_version: pkg.version, time: truncateToHour(Date.now()), event_type: 'server_download', event_properties: { agent: data.agent, download_count: data.download_count, ttl: data.ttl }, event_id: data.download_count }; return sendBatch([event]); } function statDeleteEvent(data) { const loc = location(data.ip); const event = { session_id: -1, country: loc.country, region: loc.state, user_id: userId(data.id, data.owner), app_version: pkg.version, time: truncateToHour(Date.now()), event_type: 'server_delete', event_properties: { agent: data.agent, download_count: data.download_count, ttl: data.ttl }, event_id: data.download_count + 1 }; return sendBatch([event]); } function clientEvent(event, ua, language, session_id, deltaT, platform, ip) { const loc = location(ip); const ep = event.event_properties || {}; const up = event.user_properties || {}; const event_properties = { browser: ua.browser.name, browser_version: ua.browser.version, status: ep.status, age: ep.age, downloaded: ep.downloaded, download_limit: ep.download_limit, duration: ep.duration, entrypoint: ep.entrypoint, file_count: ep.file_count, password_protected: ep.password_protected, referrer: ep.referrer, size: ep.size, time_limit: ep.time_limit, trigger: ep.trigger, ttl: ep.ttl, utm_campaign: ep.utm_campaign, utm_content: ep.utm_content, utm_medium: ep.utm_medium, utm_source: ep.utm_source, utm_term: ep.utm_term }; const user_properties = { active_count: up.active_count, anonymous: up.anonymous, experiments: up.experiments, first_action: up.first_action }; return { app_version: pkg.version, country: loc.country, device_id: event.device_id, event_properties, event_type: event.event_type, language, os_name: ua.os.name, os_version: ua.os.version, platform, region: loc.state, session_id, time: event.time + deltaT, user_id: event.user_id, user_properties }; } async function sendBatch(events, timeout = 1000) { if (!config.amplitude_id) { return 200; } try { const result = await fetch('https://api.amplitude.com/batch', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ api_key: config.amplitude_id, events }), timeout }); return result.status; } catch (e) { return 500; } } module.exports = { statUploadEvent, statDownloadEvent, statDeleteEvent, clientEvent, sendBatch };