mirror of
https://github.com/penpot/penpot.git
synced 2026-05-11 19:13:49 +00:00
* 🎉 Add telemetry anonymous event collection Rewrite the audit logging subsystem to support three operating modes and add anonymous telemetry event collection: Modes: - A (audit-log only): events persisted with full context - B (audit-log + telemetry): same as A, plus events are collected for telemetry shipping - C (telemetry-only): events stored anonymously with PII stripped, telemetry flag active, audit-log flag inactive Audit system refactoring (app.loggers.audit): - Replace qualified map keys (::audit/name etc.) with plain keywords - Rename submit! -> submit, insert! -> insert, prepare-event -> prepare-rpc-event - Add submit* as a lower-level public API - Add process-event dispatch function that handles all three modes and webhooks in a single tx-run! - Add :id to event schema (auto-generated if omitted) - Add filter-telemetry-props: anonymises event props per event type. Keeps UUID/boolean/number values; for login/identify events preserves lang, auth-backend, email-domain; for navigate events preserves route, file-id, team-id, page-id; instance-start trigger passes through. - Add filter-telemetry-context: retains only safe context keys. Backend: version, initiator, client-version, client-user-agent. Frontend: browser, os, locale, screen metrics, event-origin. - Timestamps truncated to day precision via ct/truncate for telemetry storage - PII stripped: props emptied, ip-addr zeroed, session-linking and access-token fields removed from context Config (app.config): - Derive :enable-telemetry flag from telemetry-enabled config option Email utilities (app.email): - Add email/clean and email/get-domain helper functions for domain extraction from email addresses Setup (app.setup): - Emit instance-start trigger event at system startup - Simplify handle-instance-id (remove read-only check) RPC layer (app.rpc): - wrap-audit now activates when :telemetry flag is set - Add :request-id to RPC params context for event correlation RPC commands (management, teams_invitations, verify_token, OIDC auth, webhooks): migrate all audit call sites to use the new plain-key API SREPL (app.srepl.main): - Migrate all audit/insert! calls to audit/insert with plain keys Telemetry task (app.tasks.telemetry): - Restructure legacy report into make-legacy-request; distinguish payload type as :telemetry-legacy-report - Add collect-and-send-audit-events: loop fetching up to 10,000 rows per iteration, encodes and sends each page, deletes on success, stops immediately on failure for retry - Add send-event-batch: POSTs fressian+zstd batch (base64 via blob/encode-str) to the telemetry endpoint with instance-id per event - Add gc-telemetry-events: enforces 100,000-row safety cap by dropping oldest rows first - Add delete-sent-events: deletes successfully shipped rows by id Blob utilities (app.util.blob): - Add encode-str/decode-str: combine fressian+zstd encoding with URL- safe base64 for JSON-safe string transport Database: - Add migration 0145: index on audit_log (source, created_at ASC) for efficient telemetry batch collection queries Frontend: - Always initialize event system regardless of :audit-log flag - Defer auth events (signin identify) to after profile is set - Refactor event subsystem for telemetry support Tests (21 test vars, 94 assertions in tasks-telemetry-test): - Cover all code paths: disabled/enabled telemetry, no-events no-op, happy-path batch send and delete, failure retention, payload anonymity, context stripping, timestamp day precision, batch encoding round-trip, multi-page iteration, GC cap enforcement, partial failure handling - blob encode-str/decode-str round-trip tests (14 test vars) - RPC audit integration tests (5 test vars) Signed-off-by: Andrey Antukh <niwi@niwi.nz> * 📎 Add pr feedback changes --------- Signed-off-by: Andrey Antukh <niwi@niwi.nz>
133 lines
5.5 KiB
JSON
133 lines
5.5 KiB
JSON
{
|
|
"name": "frontend",
|
|
"version": "1.0.0",
|
|
"license": "MPL-2.0",
|
|
"author": "Kaleidos INC",
|
|
"private": true,
|
|
"packageManager": "pnpm@10.31.0+sha512.e3927388bfaa8078ceb79b748ffc1e8274e84d75163e67bc22e06c0d3aed43dd153151cbf11d7f8301ff4acb98c68bdc5cadf6989532801ffafe3b3e4a63c268",
|
|
"browserslist": [
|
|
"defaults"
|
|
],
|
|
"type": "module",
|
|
"repository": {
|
|
"type": "git",
|
|
"url": "https://github.com/penpot/penpot"
|
|
},
|
|
"scripts": {
|
|
"build:app:assets": "node ./scripts/build-app-assets.js",
|
|
"build:storybook": "pnpm run build:storybook:assets && pnpm run build:storybook:cljs && storybook build",
|
|
"build:storybook:assets": "node ./scripts/build-storybook-assets.js",
|
|
"build:wasm": "../render-wasm/build",
|
|
"build:storybook:cljs": "clojure -M:dev:shadow-cljs compile storybook",
|
|
"build:app:libs": "node ./scripts/build-libs.js",
|
|
"build:app:main": "clojure -M:dev:shadow-cljs release main worker",
|
|
"build:app:worker": "clojure -M:dev:shadow-cljs release worker",
|
|
"build:app": "pnpm run clear:shadow-cache && pnpm run build:app:main && pnpm run build:app:libs",
|
|
"check-fmt:clj": "cljfmt check --parallel=true src/ test/",
|
|
"check-fmt:js": "prettier -c src/**/*.stories.jsx -c playwright/**/*.js -c scripts/**/*.js -c text-editor/**/*.js",
|
|
"check-fmt:scss": "prettier -c resources/styles -c src/**/*.scss",
|
|
"fmt:clj": "cljfmt fix --parallel=true src/ test/",
|
|
"fmt:js": "prettier -c src/**/*.stories.jsx -c playwright/**/*.js -c scripts/**/*.js -c text-editor/**/*.js -w",
|
|
"fmt:scss": "prettier -c resources/styles -c src/**/*.scss -w",
|
|
"lint:clj": "clj-kondo --parallel --lint ../common/src src/",
|
|
"lint:js": "exit 0",
|
|
"lint:scss": "pnpm exec stylelint '{src,resources}/**/*.scss'",
|
|
"build:test": "clojure -M:dev:shadow-cljs compile test",
|
|
"test": "pnpm run build:wasm && pnpm run build:test && node target/tests/test.js",
|
|
"test:storybook": "vitest run --project=storybook",
|
|
"watch:test": "mkdir -p target/tests && concurrently \"clojure -M:dev:shadow-cljs watch test\" \"nodemon -C -d 2 -w target/tests --exec 'node target/tests/test.js'\"",
|
|
"test:e2e": "playwright test --project default",
|
|
"translations": "node ./scripts/translations.js",
|
|
"watch:app:assets": "node ./scripts/watch.js",
|
|
"watch:app:libs": "node ./scripts/build-libs.js --watch",
|
|
"watch:app:main": "clojure -M:dev:shadow-cljs watch main worker storybook",
|
|
"clear:shadow-cache": "rm -rf .shadow-cljs",
|
|
"clear:wasm": "cargo clean --manifest-path ../render-wasm/Cargo.toml",
|
|
"watch": "exit 0",
|
|
"watch:app": "pnpm run clear:shadow-cache && pnpm run clear:wasm && pnpm run build:wasm && concurrently --kill-others-on-fail \"pnpm run watch:app:assets\" \"pnpm run watch:app:main\" \"pnpm run watch:app:libs\"",
|
|
"watch:storybook": "pnpm run build:storybook:assets && concurrently --kill-others-on-fail \"storybook dev -p 6006 --no-open\" \"node ./scripts/watch-storybook.js\"",
|
|
"postinstall": "(cd ../plugins/libs/plugins-runtime; pnpm install; pnpm run build)"
|
|
},
|
|
"devDependencies": {
|
|
"@penpot/draft-js": "workspace:./packages/draft-js",
|
|
"@penpot/mousetrap": "workspace:./packages/mousetrap",
|
|
"@penpot/plugins-runtime": "link:../plugins/libs/plugins-runtime",
|
|
"@penpot/svgo": "penpot/svgo#v3.2",
|
|
"@penpot/text-editor": "workspace:./text-editor",
|
|
"@penpot/tokenscript": "workspace:./packages/tokenscript",
|
|
"@penpot/ui": "workspace:./packages/ui",
|
|
"@playwright/test": "1.59.1",
|
|
"@storybook/addon-docs": "10.3.5",
|
|
"@storybook/addon-themes": "10.3.5",
|
|
"@storybook/addon-vitest": "10.3.5",
|
|
"@storybook/react-vite": "10.3.5",
|
|
"@tokens-studio/sd-transforms": "1.2.11",
|
|
"@types/node": "^25.5.2",
|
|
"@vitest/browser": "4.1.3",
|
|
"@vitest/browser-playwright": "^4.1.3",
|
|
"@vitest/coverage-v8": "4.1.3",
|
|
"@zip.js/zip.js": "2.8.26",
|
|
"autoprefixer": "^10.4.27",
|
|
"compression": "^1.8.1",
|
|
"concurrently": "^9.2.1",
|
|
"date-fns": "^4.1.0",
|
|
"esbuild": "^0.28.0",
|
|
"eventsource-parser": "^3.0.8",
|
|
"express": "^5.1.0",
|
|
"fancy-log": "^2.0.0",
|
|
"getopts": "^2.3.0",
|
|
"gettext-parser": "^9.0.2",
|
|
"highlight.js": "^11.10.0",
|
|
"js-beautify": "^1.15.4",
|
|
"jsdom": "^29.0.2",
|
|
"lodash": "^4.18.1",
|
|
"lodash.debounce": "^4.0.8",
|
|
"map-stream": "0.0.7",
|
|
"marked": "^17.0.5",
|
|
"mkdirp": "^3.0.1",
|
|
"mustache": "^4.2.0",
|
|
"nodemon": "^3.1.14",
|
|
"npm-run-all": "^4.1.5",
|
|
"opentype.js": "^1.3.4",
|
|
"p-limit": "^7.3.0",
|
|
"playwright": "1.59.1",
|
|
"postcss": "^8.5.8",
|
|
"postcss-clean": "^1.2.2",
|
|
"postcss-modules": "^6.0.1",
|
|
"postcss-scss": "^4.0.9",
|
|
"prettier": "3.8.1",
|
|
"pretty-time": "^1.1.0",
|
|
"prop-types": "^15.8.1",
|
|
"randomcolor": "^0.6.2",
|
|
"react": "19.2.4",
|
|
"react-dom": "19.2.4",
|
|
"react-error-boundary": "^6.1.1",
|
|
"react-virtualized": "^9.22.6",
|
|
"rimraf": "^6.1.3",
|
|
"rxjs": "8.0.0-alpha.14",
|
|
"sass": "^1.98.0",
|
|
"sass-embedded": "^1.98.0",
|
|
"sax": "^1.4.1",
|
|
"scheduler": "^0.27.0",
|
|
"source-map-support": "^0.5.21",
|
|
"storybook": "10.3.5",
|
|
"style-dictionary": "5.0.0-rc.1",
|
|
"stylelint": "^17.4.0",
|
|
"stylelint-config-standard-scss": "^17.0.0",
|
|
"stylelint-scss": "^7.0.0",
|
|
"stylelint-use-logical-spec": "^5.0.1",
|
|
"svg-sprite": "^2.0.4",
|
|
"tdigest": "^0.1.2",
|
|
"tinycolor2": "^1.6.0",
|
|
"typescript": "^6.0.2",
|
|
"ua-parser-js": "2.0.9",
|
|
"vite": "^8.0.7",
|
|
"vitest": "^4.1.3",
|
|
"wait-on": "^9.0.4",
|
|
"wasm-pack": "^0.13.1",
|
|
"watcher": "^2.3.1",
|
|
"workerpool": "^10.0.1",
|
|
"xregexp": "^5.1.2"
|
|
}
|
|
}
|