Full analytics platform with MaxMind GeoLite2 IP-to-location resolution, cross-module dashboard (docs, video, photo), user drill-down, volunteer self-service stats, and ANALYTICS_ADMIN role with feature flag controls. - ANALYTICS_ADMIN role + ANALYTICS_ROLES group across backend and frontend - GeoIP service (MaxMind GeoLite2, lazy-loaded, graceful degradation) - Geo fields (country, region, city, lat/lng) on DocsPageView, VideoView, PhotoView - IP resolved to geo before SHA-256 hashing (privacy-preserving) - Unified analytics module: overview, geo, content, user engagement endpoints - 4 admin dashboard pages: Overview, Geography (Leaflet map), Content, Users - Volunteer MyAnalyticsPage for self-service activity stats - Settings UI: enableAnalytics, analyticsGeoEnabled, trackAuthenticatedUsers, retentionDays - Scheduled cleanup job respecting configurable retention period - config.sh: Analytics + MaxMind prompt in configure_features() - Control panel: enableAnalytics flag, template, discovery, wizard, detail page - Docker: geoip volume mount, MaxMind env vars, entrypoint auto-download - Nginx: X-Forwarded-For fix ($proxy_add_x_forwarded_for) for real client IP - Express trust proxy set to 2 for Pangolin/Newt tunnel chain - CORS updated for docs origin (cmlite.org + docs.cmlite.org) - Lander page: added docs-analytics tracking snippet - Prisma migration: 20260402100000_add_analytics_system Bunker Admin
84 lines
2.3 KiB
JSON
84 lines
2.3 KiB
JSON
{
|
|
"name": "changemaker-v2-api",
|
|
"version": "2.0.0",
|
|
"description": "Unified Express.js API for Changemaker Lite v2",
|
|
"main": "dist/server.js",
|
|
"scripts": {
|
|
"dev": "tsx watch src/server.ts",
|
|
"dev:media": "tsx watch src/media-server.ts",
|
|
"build": "tsc",
|
|
"start": "node dist/server.js",
|
|
"start:media": "node dist/media-server.js",
|
|
"prisma:generate": "prisma generate",
|
|
"prisma:migrate": "prisma migrate dev",
|
|
"prisma:migrate:deploy": "prisma migrate deploy",
|
|
"prisma:seed": "tsx prisma/seed.ts",
|
|
"prisma:studio": "prisma studio"
|
|
},
|
|
"dependencies": {
|
|
"@fastify/cors": "^11.2.0",
|
|
"@fastify/multipart": "^9.4.0",
|
|
"@fastify/static": "^9.0.0",
|
|
"@hocuspocus/server": "^3.4.4",
|
|
"@maxmind/geoip2-node": "^6.3.4",
|
|
"@prisma/client": "^6.3.0",
|
|
"@types/mime-types": "^3.0.1",
|
|
"bcryptjs": "^2.4.3",
|
|
"bullmq": "^5.34.0",
|
|
"compression": "^1.7.5",
|
|
"cookie-parser": "^1.4.7",
|
|
"cors": "^2.8.5",
|
|
"csv-parse": "^6.1.0",
|
|
"csv-stringify": "^6.6.0",
|
|
"dotenv": "^16.4.7",
|
|
"drizzle-orm": "^0.45.1",
|
|
"exif-reader": "^2.0.3",
|
|
"express": "^4.21.2",
|
|
"express-rate-limit": "^7.5.0",
|
|
"fastify": "^5.7.4",
|
|
"helmet": "^8.0.0",
|
|
"ical-generator": "^10.0.0",
|
|
"ioredis": "^5.4.2",
|
|
"jsonwebtoken": "^9.0.2",
|
|
"mime-types": "^3.0.2",
|
|
"multer": "^2.1.1",
|
|
"node-addon-api": "^8.5.0",
|
|
"node-ical": "^0.25.5",
|
|
"nodemailer": "^8.0.1",
|
|
"pg": "^8.18.0",
|
|
"proj4": "^2.20.2",
|
|
"prom-client": "^15.1.3",
|
|
"qrcode": "^1.5.4",
|
|
"rate-limit-redis": "^4.2.0",
|
|
"sharp": "^0.34.5",
|
|
"stripe": "^20.3.1",
|
|
"winston": "^3.17.0",
|
|
"winston-daily-rotate-file": "^5.0.0",
|
|
"ws": "^8.19.0",
|
|
"yaml": "^2.8.2",
|
|
"yjs": "^13.6.29",
|
|
"zod": "^3.24.1"
|
|
},
|
|
"devDependencies": {
|
|
"@types/bcryptjs": "^2.4.6",
|
|
"@types/compression": "^1.7.5",
|
|
"@types/cookie-parser": "^1.4.10",
|
|
"@types/cors": "^2.8.17",
|
|
"@types/express": "^5.0.0",
|
|
"@types/jsonwebtoken": "^9.0.7",
|
|
"@types/multer": "^2.0.0",
|
|
"@types/node": "^22.19.11",
|
|
"@types/nodemailer": "^7.0.11",
|
|
"@types/pg": "^8.16.0",
|
|
"@types/qrcode": "^1.5.6",
|
|
"@types/ws": "^8.18.1",
|
|
"drizzle-kit": "^0.31.9",
|
|
"prisma": "^6.3.0",
|
|
"tsx": "^4.19.2",
|
|
"typescript": "^5.7.3"
|
|
},
|
|
"prisma": {
|
|
"seed": "npx tsx prisma/seed.ts"
|
|
}
|
|
}
|