mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-09 18:15:50 +00:00
fix(frontend): ship telemetry disclosure docs locally
This commit is contained in:
@@ -125,6 +125,7 @@ work extends shared components instead of creating new local variants.
|
||||
94. `frontend-modern/src/components/Settings/DockerRuntimeSettingsCard.tsx`
|
||||
95. `frontend-modern/src/components/shared/EnvironmentLockBadge.tsx`
|
||||
96. `frontend-modern/src/utils/environmentLockPresentation.ts`
|
||||
97. `frontend-modern/src/utils/docsLinks.ts`
|
||||
|
||||
## Shared Boundaries
|
||||
|
||||
@@ -680,6 +681,10 @@ product copy, or external links back into the shared shell. Internal product
|
||||
navigation from that shell should still route through canonical shared helpers
|
||||
such as `frontend-modern/src/routing/resourceLinks.ts` rather than freezing raw
|
||||
`/recovery?...` route strings into the modal itself.
|
||||
Canonical customer disclosures inside those shared shells now route through
|
||||
`frontend-modern/src/utils/docsLinks.ts`, so settings and what's-new privacy
|
||||
links resolve to shipped `/docs/...` assets instead of hard-coded GitHub
|
||||
`main` URLs that can drift from the running build.
|
||||
The shared summary strip primitives now follow that same owner split.
|
||||
`frontend-modern/src/components/shared/SummaryPanel.tsx` and
|
||||
`frontend-modern/src/components/shared/SummaryMetricCard.tsx` stay the render
|
||||
@@ -1403,6 +1408,10 @@ The security-facing settings panels within that shell now also follow an
|
||||
explicit shared boundary with `security-privacy` so shell framing stays here
|
||||
while auth posture, token controls, and privacy semantics remain governed as a
|
||||
trust surface instead of generic UX copy.
|
||||
That shared shell boundary now also covers version-matched docs-link framing:
|
||||
customer-facing privacy disclosures in shared settings surfaces must route
|
||||
through `frontend-modern/src/utils/docsLinks.ts` rather than panel-local
|
||||
external URLs.
|
||||
|
||||
Single-surface settings pages that only render one canonical `SettingsPanel`
|
||||
must stay rooted directly at that panel instead of wrapping it in an extra
|
||||
|
||||
@@ -25,27 +25,29 @@ token-management visibility, and privacy controls to operators.
|
||||
|
||||
1. `SECURITY.md`
|
||||
2. `docs/PRIVACY.md`
|
||||
3. `frontend-modern/src/api/security.ts`
|
||||
4. `frontend-modern/src/components/Settings/APITokenManager.tsx`
|
||||
5. `frontend-modern/src/components/Settings/apiTokenManagerModel.ts`
|
||||
6. `frontend-modern/src/components/Settings/GeneralSettingsPanel.tsx`
|
||||
7. `frontend-modern/src/components/Settings/SecurityAuthPanel.tsx`
|
||||
8. `frontend-modern/src/components/Settings/SecurityOverviewPanel.tsx`
|
||||
9. `frontend-modern/src/components/Settings/QuickSecuritySetup.tsx`
|
||||
10. `frontend-modern/src/components/Settings/SecurityPostureSummary.tsx`
|
||||
11. `frontend-modern/src/components/Settings/SSOProviderTypeIcon.tsx`
|
||||
12. `frontend-modern/src/components/Settings/useAPITokenManagerState.ts`
|
||||
13. `frontend-modern/src/components/Settings/useSystemSettingsState.ts`
|
||||
14. `frontend-modern/src/utils/apiTokenPresentation.ts`
|
||||
15. `frontend-modern/src/utils/auditLogPresentation.ts`
|
||||
16. `frontend-modern/src/utils/auditWebhookPresentation.ts`
|
||||
17. `frontend-modern/src/utils/securityAuthPresentation.ts`
|
||||
18. `frontend-modern/src/utils/securityScorePresentation.ts`
|
||||
19. `internal/api/security.go`
|
||||
20. `internal/api/security_tokens.go`
|
||||
21. `internal/api/system_settings.go`
|
||||
22. `internal/telemetry/telemetry.go`
|
||||
23. `internal/api/router_routes_auth_security.go`
|
||||
3. `frontend-modern/public/docs/PRIVACY.md`
|
||||
4. `frontend-modern/src/utils/docsLinks.ts`
|
||||
5. `frontend-modern/src/api/security.ts`
|
||||
6. `frontend-modern/src/components/Settings/APITokenManager.tsx`
|
||||
7. `frontend-modern/src/components/Settings/apiTokenManagerModel.ts`
|
||||
8. `frontend-modern/src/components/Settings/GeneralSettingsPanel.tsx`
|
||||
9. `frontend-modern/src/components/Settings/SecurityAuthPanel.tsx`
|
||||
10. `frontend-modern/src/components/Settings/SecurityOverviewPanel.tsx`
|
||||
11. `frontend-modern/src/components/Settings/QuickSecuritySetup.tsx`
|
||||
12. `frontend-modern/src/components/Settings/SecurityPostureSummary.tsx`
|
||||
13. `frontend-modern/src/components/Settings/SSOProviderTypeIcon.tsx`
|
||||
14. `frontend-modern/src/components/Settings/useAPITokenManagerState.ts`
|
||||
15. `frontend-modern/src/components/Settings/useSystemSettingsState.ts`
|
||||
16. `frontend-modern/src/utils/apiTokenPresentation.ts`
|
||||
17. `frontend-modern/src/utils/auditLogPresentation.ts`
|
||||
18. `frontend-modern/src/utils/auditWebhookPresentation.ts`
|
||||
19. `frontend-modern/src/utils/securityAuthPresentation.ts`
|
||||
20. `frontend-modern/src/utils/securityScorePresentation.ts`
|
||||
21. `internal/api/security.go`
|
||||
22. `internal/api/security_tokens.go`
|
||||
23. `internal/api/system_settings.go`
|
||||
24. `internal/telemetry/telemetry.go`
|
||||
25. `internal/api/router_routes_auth_security.go`
|
||||
|
||||
## Shared Boundaries
|
||||
|
||||
@@ -117,6 +119,12 @@ That shared token-management boundary now also includes
|
||||
`frontend-modern/src/utils/apiTokenPresentation.ts`, so API-token load,
|
||||
generate, and revoke errors stay on one governed customer-facing wording path
|
||||
instead of drifting back into hook-local notification strings.
|
||||
Telemetry/privacy disclosures now also route through the shipped frontend docs
|
||||
boundary: `frontend-modern/src/utils/docsLinks.ts` is the canonical frontend
|
||||
owner for privacy-document URLs, while `frontend-modern/public/docs/PRIVACY.md`
|
||||
is the version-matched asset served by the running build. Privacy disclosures
|
||||
must not drift back to GitHub `main` links that can describe a different
|
||||
revision than the installed runtime.
|
||||
That same governed settings trust boundary now also includes
|
||||
`frontend-modern/src/components/Settings/QuickSecuritySetup.tsx`,
|
||||
`frontend-modern/src/components/Settings/SecurityPostureSummary.tsx`,
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
# Privacy
|
||||
|
||||
Pulse is designed to run locally. By default, your monitoring data stays on your server.
|
||||
|
||||
## Anonymous Telemetry
|
||||
|
||||
Pulse includes anonymous telemetry that is **enabled by default**. It sends a lightweight ping on startup and once every 24 hours to help the developer understand how many active installations exist and which features are in use.
|
||||
|
||||
No hostnames, credentials, IP addresses, or personally identifiable information is ever sent. See the full field list below.
|
||||
|
||||
### How to disable
|
||||
|
||||
- **Settings → System → General → Anonymous telemetry** (toggle off), or
|
||||
- Set the environment variable `PULSE_TELEMETRY=false`
|
||||
|
||||
### Exactly what is sent
|
||||
|
||||
Every field is listed below — nothing else leaves your server:
|
||||
|
||||
| Field | Example | Purpose |
|
||||
|-------|---------|---------|
|
||||
| Install ID | `a1b2c3d4-...` | Random UUID generated locally, not tied to any account |
|
||||
| Version | `6.0.0` | Pulse version |
|
||||
| Platform | `docker` or `binary` | Deployment method |
|
||||
| OS | `linux` | Operating system |
|
||||
| Arch | `amd64` | CPU architecture |
|
||||
| Event | `startup` or `heartbeat` | Whether this is a startup or daily ping |
|
||||
| PVE nodes | `3` | Number of Proxmox VE nodes connected |
|
||||
| PBS instances | `1` | Number of Proxmox Backup Server instances |
|
||||
| PMG instances | `0` | Number of Proxmox Mail Gateway instances |
|
||||
| VMs | `25` | Total VM count |
|
||||
| Containers | `12` | Total LXC container count |
|
||||
| Docker hosts | `2` | Number of Docker hosts monitored |
|
||||
| Kubernetes clusters | `0` | Number of Kubernetes clusters |
|
||||
| AI enabled | `true`/`false` | Whether AI features are turned on |
|
||||
| Active alerts | `4` | Number of active alerts |
|
||||
| Relay enabled | `true`/`false` | Whether remote access is enabled |
|
||||
| SSO enabled | `true`/`false` | Whether OIDC/SSO is configured |
|
||||
| Multi-tenant | `true`/`false` | Whether multi-tenant mode is on |
|
||||
| License tier | `free`, `pro`, etc. | Current license tier |
|
||||
| API tokens | `3` | Number of API tokens configured |
|
||||
|
||||
### What is NOT sent
|
||||
|
||||
- No IP addresses are stored server-side
|
||||
- No hostnames, node names, VM names, or any infrastructure identifiers
|
||||
- No Proxmox credentials, API tokens, or passwords
|
||||
- No alert content, AI prompts, or chat messages
|
||||
- No personally identifiable information of any kind
|
||||
|
||||
### Source code
|
||||
|
||||
The telemetry implementation is in [`internal/telemetry/telemetry.go`](../internal/telemetry/telemetry.go). You can read the `Ping` struct to see every field that is transmitted.
|
||||
|
||||
## No Third-Party Analytics
|
||||
|
||||
- There is no third-party analytics SDK in the frontend.
|
||||
- Telemetry pings go only to the Pulse license server (`license.pulserelay.pro`), not to any third-party service.
|
||||
|
||||
## Optional Outbound Connections (Explicitly Enabled)
|
||||
|
||||
Pulse can make outbound connections when you enable specific features:
|
||||
|
||||
- **AI (BYOK)**: when AI features are enabled, Pulse sends only the context required for your request to the provider you configured (OpenAI, Anthropic, etc.). See `docs/AI.md`.
|
||||
- **Relay / Remote Access**: when relay is enabled, Pulse connects to the configured relay endpoint to enable mobile access. See Settings → Remote Access.
|
||||
- **Update checks**: Pulse can check for new releases/updates (for example via GitHub release metadata) depending on your deployment and configuration.
|
||||
|
||||
## Local Upgrade Metrics (Can Be Disabled)
|
||||
|
||||
Pulse can record local-only events such as "paywall viewed" or "trial started" to improve and debug in-app upgrade flows.
|
||||
|
||||
- These events are stored locally and are not exported to third parties.
|
||||
- Disable via **Settings → System → General → Disable local upgrade metrics** or set:
|
||||
- `PULSE_DISABLE_LOCAL_UPGRADE_METRICS=true`
|
||||
|
||||
If you prefer fewer upgrade prompts, you can also enable:
|
||||
- **Settings → System → General → Reduce Pro prompts**
|
||||
@@ -0,0 +1,126 @@
|
||||
# 📚 Pulse Documentation
|
||||
|
||||
Welcome to the Pulse documentation portal. Here you'll find everything you need to install, configure, and master Pulse.
|
||||
|
||||
---
|
||||
|
||||
## v6 Execution Canonical Source
|
||||
|
||||
For Pulse v6 build/release execution work, do not start from this broad docs index.
|
||||
Use:
|
||||
|
||||
1. `docs/release-control/v6/internal/SOURCE_OF_TRUTH.md` for stable human governance and locked decisions
|
||||
2. `docs/release-control/v6/internal/status.json` for live lane state, lane-to-subsystem ownership, structured evidence references, typed lane/subsystem decision records, and canonical ordered lists
|
||||
3. `docs/release-control/v6/status.schema.json` for the machine-readable status contract
|
||||
4. `docs/release-control/v6/internal/subsystems/registry.json` and `docs/release-control/v6/internal/subsystems/registry.schema.json` for subsystem ownership, explicit shared-ownership exceptions, and proof-routing rules
|
||||
5. `python3 scripts/release_control/status_audit.py --check` if you need a machine-derived evidence health audit
|
||||
6. `python3 scripts/release_control/registry_audit.py --check` if you need a machine-derived subsystem registry audit
|
||||
7. `python3 scripts/release_control/contract_audit.py --check` if you need a machine-derived subsystem contract audit, including explicit cross-subsystem dependency checks and exact registry-derived shared-boundary wording
|
||||
Local pre-commit runs the v6 machine audits against staged control-file content so partial staging cannot hide governance drift.
|
||||
Local pre-commit also blocks partial staging for hook-sensitive governance files under `docs/release-control/v6/`, `scripts/release_control/`, `internal/repoctl/`, `.husky/pre-commit`, and `.github/workflows/canonical-governance.yml`, because those checks still execute or structurally read the working-tree versions locally.
|
||||
8. `python3 scripts/release_control/subsystem_lookup.py <path> [<path> ...]` if you need subsystem ownership, proof routing, lane context, relevant decision records, and dependent contract-update obligations for a change
|
||||
|
||||
For governed runtime changes, a staged subsystem contract only counts if its
|
||||
diff updates a substantive contract section such as `Purpose`, `Canonical Files`,
|
||||
`Shared Boundaries`, `Extension Points`, `Forbidden Paths`,
|
||||
`Completion Obligations`, or `Current State`, rather than metadata alone.
|
||||
|
||||
All other documents are supporting references unless explicitly required for evidence.
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Getting Started
|
||||
|
||||
- **[Installation Guide](INSTALL.md)**
|
||||
Step-by-step guides for Docker, Kubernetes, and bare metal.
|
||||
- **[Configuration](CONFIGURATION.md)**
|
||||
Learn how to configure authentication, notifications (Email, Discord, etc.), and system settings.
|
||||
- **[Deployment Models](DEPLOYMENT_MODELS.md)**
|
||||
Where config lives, how updates work, and what differs per deployment.
|
||||
- **[Migration Guide](MIGRATION.md)**
|
||||
Moving to a new server? Here's how to export and import your data safely.
|
||||
- **[Upgrade to v6](UPGRADE_v6.md)**
|
||||
Practical upgrade guidance and post-upgrade checks for Pulse v6.
|
||||
- **[FAQ](FAQ.md)**
|
||||
Common questions and quick answers.
|
||||
|
||||
## 🛠️ Deployment & Operations
|
||||
|
||||
- **[Docker Guide](DOCKER.md)** – Advanced Docker & Compose configurations.
|
||||
- **[Kubernetes](KUBERNETES.md)** – Helm charts, ingress, and HA setups.
|
||||
- **[Reverse Proxy](REVERSE_PROXY.md)** – Nginx, Caddy, Traefik, and Cloudflare Tunnel recipes.
|
||||
- **[Troubleshooting](TROUBLESHOOTING.md)** – Deep dive into common issues and logs.
|
||||
|
||||
## 🔐 Security
|
||||
|
||||
- **[Security Policy](../SECURITY.md)** – The core security model (Encryption, Auth, API Scopes).
|
||||
- **[Privacy](PRIVACY.md)** – What leaves your network (and what doesn’t).
|
||||
- **[OIDC / SSO](OIDC.md)** – OIDC Single Sign-On configuration (Authentik, Keycloak, Azure AD, etc.).
|
||||
- **[Proxy Auth](PROXY_AUTH.md)** – Authentik/Authelia/Cloudflare proxy authentication configuration.
|
||||
- **[Agent Security](AGENT_SECURITY.md)** – Agent self-update verification and API security.
|
||||
|
||||
## 📖 Advanced Topics (Relay / Pro / Pro+ / Cloud)
|
||||
|
||||
- **[AI Autonomy & Safety](AI_AUTONOMY.md)** – Configure patrol autonomy levels, assistant control levels, investigation tuning, and safety guardrails.
|
||||
- **[Role-Based Access Control (RBAC)](RBAC.md)** – Define custom roles, assign permissions, and integrate with OIDC group mapping.
|
||||
- **[Audit Logging](AUDIT_LOGGING.md)** – Tamper-evident event logging for compliance, with query, export, and signature verification.
|
||||
|
||||
## ✨ New in 6.0
|
||||
|
||||
- **[Unified Resource Model](UNIFIED_RESOURCES.md)** – How all platforms merge into one model with task-based navigation.
|
||||
- **[Unified Navigation Migration](MIGRATION_UNIFIED_NAV.md)** – Upgrading from platform-specific tabs to v6 navigation.
|
||||
- **[TrueNAS Integration](TRUENAS.md)** – First-class TrueNAS SCALE/CORE monitoring (pools, datasets, disks, snapshots, replication).
|
||||
- **[Relay / Mobile Remote Access](RELAY.md)** – End-to-end encrypted relay (mobile app public rollout is coming soon; Relay and above).
|
||||
- **[Recovery Central](RECOVERY.md)** – Unified backup, snapshot, and replication view across all providers.
|
||||
- **[Pulse Cloud (Hosted)](CLOUD.md)** – Fully managed hosting with automatic updates and backups.
|
||||
- **[Pulse AI](AI.md)** – Chat assistant, patrol findings, alert analysis, intelligence, and forecasts.
|
||||
- **[Metrics History](METRICS_HISTORY.md)** – Persistent metrics storage with configurable retention.
|
||||
- **[Mail Gateway](MAIL_GATEWAY.md)** – Proxmox Mail Gateway (PMG) monitoring.
|
||||
- **[Auto Updates](AUTO_UPDATE.md)** – One-click updates for supported deployments.
|
||||
- **[Multi-Tenant Organizations](MULTI_TENANT.md)** – Isolate infrastructure by organization (Enterprise, opt-in).
|
||||
- **[Entitlements Overhaul](PULSE_PRO.md)** – Capability-key-based feature gating across Community/Relay/Pro/Pro+/Cloud.
|
||||
|
||||
## 💳 Plans (Community / Relay / Pro / Pro+ / Cloud)
|
||||
|
||||
Pulse is available in four self-hosted tiers plus hosted Cloud:
|
||||
|
||||
- **Community**: Free self-hosted monitoring for up to 5 monitored systems with 7-day history.
|
||||
- **Relay**: Adds remote access, mobile, push notifications, 14-day history, and raises the monitored-system limit to 8.
|
||||
- **Pro**: Adds AI investigation, auto-fix, operations tooling, and raises the monitored-system limit to 15 with 90-day history.
|
||||
- **Pro+**: Everything in Pro with room for up to 50 monitored systems.
|
||||
- **Cloud**: Hosted Pulse with Pro-level capabilities; hosted pricing is unchanged by the self-hosted model lock.
|
||||
|
||||
- **[Learn more at pulserelay.pro](https://pulserelay.pro)**
|
||||
- **[Plans and entitlements](PULSE_PRO.md)** (includes the Community/Relay/Pro/Pro+/Cloud matrix)
|
||||
- **[AI deep dive](AI.md)**
|
||||
- **[Multi-Tenant Organizations (Enterprise)](MULTI_TENANT.md)** — Isolate infrastructure by organization for MSPs and multi-datacenter deployments.
|
||||
|
||||
## 📡 Monitoring & Agents
|
||||
|
||||
- **[Unified Agent](UNIFIED_AGENT.md)** – Single binary for host, Docker, and Kubernetes monitoring.
|
||||
- **[Centralized Agent Management (Pro/Pro+/Cloud)](CENTRALIZED_MANAGEMENT.md)** – Agent profiles and remote config.
|
||||
- **[Proxmox Backup Server](PBS.md)** – PBS integration, direct API vs PVE passthrough, token setup.
|
||||
- **[TrueNAS](TRUENAS.md)** – TrueNAS SCALE/CORE integration.
|
||||
- **[ZFS Monitoring](ZFS_MONITORING.md)** – Proxmox-native ZFS pool monitoring.
|
||||
- **[Storage Architecture](STORAGE_ARCHITECTURE.md)** – Proposed canonical storage, disk, S.M.A.R.T., and topology model for making storage genuinely operator-useful.
|
||||
- **[VM Disk Monitoring](VM_DISK_MONITORING.md)** – Enabling QEMU Guest Agent for disk stats.
|
||||
- **[Temperature Monitoring](TEMPERATURE_MONITORING.md)** – Agent-based temperature monitoring (`pulse-agent --enable-proxmox`). Sensor proxy has been removed.
|
||||
- **[Webhooks](WEBHOOKS.md)** – Custom notification payloads.
|
||||
|
||||
## 💻 Development
|
||||
|
||||
- **[API Reference](API.md)** – Complete REST API documentation.
|
||||
- **[Architecture](../ARCHITECTURE.md)** – System design and component interaction.
|
||||
- **[Contributing](../CONTRIBUTING.md)** – How to contribute to Pulse.
|
||||
|
||||
## 📁 Previous Versions
|
||||
|
||||
- **[Upgrade to v5](UPGRADE_v5.md)** – Upgrade guidance for v4 → v5 migrations.
|
||||
- **[v6 Release Promotion Policy](release-control/v6/internal/RELEASE_PROMOTION_POLICY.md)** – Canonical stable-vs-prerelease promotion rules and rollback expectations.
|
||||
- **[v6 Prerelease Runbook](releases/V6_PRERELEASE_RUNBOOK.md)** – Internal release operations used during the v6 prerelease period.
|
||||
|
||||
---
|
||||
|
||||
Found a bug or have a suggestion?
|
||||
|
||||
[](https://github.com/rcourtman/Pulse/issues)
|
||||
@@ -4,6 +4,7 @@ import { fileURLToPath } from 'node:url';
|
||||
|
||||
import { withExclusiveLock } from '../../scripts/exclusive-lock.mjs';
|
||||
import { syncEmbedDir } from './sync-embed-dist.mjs';
|
||||
import { syncPublicDocs } from './sync-public-docs.mjs';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
@@ -29,6 +30,7 @@ function run(command, args, options = {}) {
|
||||
await withExclusiveLock(
|
||||
lockPath,
|
||||
async () => {
|
||||
await syncPublicDocs();
|
||||
await run(npxCmd, ['vite', 'build'], { cwd: frontendRoot });
|
||||
await syncEmbedDir({ lock: false });
|
||||
},
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import { copyFile, mkdir } from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath, pathToFileURL } from 'node:url';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
const frontendRoot = path.resolve(__dirname, '..');
|
||||
const repoRoot = path.resolve(frontendRoot, '..');
|
||||
const sourceDocsDir = path.join(repoRoot, 'docs');
|
||||
const targetDocsDir = path.join(frontendRoot, 'public', 'docs');
|
||||
|
||||
const shippedDocs = ['README.md', 'PRIVACY.md'];
|
||||
|
||||
export async function syncPublicDocs() {
|
||||
await mkdir(targetDocsDir, { recursive: true });
|
||||
|
||||
for (const filename of shippedDocs) {
|
||||
await copyFile(path.join(sourceDocsDir, filename), path.join(targetDocsDir, filename));
|
||||
}
|
||||
|
||||
console.log(`Synced shipped docs to ${targetDocsDir}`);
|
||||
}
|
||||
|
||||
const isMainModule = process.argv[1]
|
||||
? pathToFileURL(process.argv[1]).href === import.meta.url
|
||||
: false;
|
||||
|
||||
if (isMainModule) {
|
||||
syncPublicDocs().catch((error) => {
|
||||
console.error(error instanceof Error ? error.message : String(error));
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
PVE_POLLING_MIN_SECONDS,
|
||||
PVE_POLLING_PRESETS,
|
||||
} from '@/utils/systemSettingsPresentation';
|
||||
import { PRIVACY_DOC_URL } from '@/utils/docsLinks';
|
||||
|
||||
import Laptop from 'lucide-solid/icons/laptop';
|
||||
|
||||
@@ -214,7 +215,7 @@ export const GeneralSettingsPanel: Component<GeneralSettingsPanelProps> = (props
|
||||
platform, resource counts, and feature flags. No hostnames, credentials, or personal
|
||||
information is ever sent.{' '}
|
||||
<a
|
||||
href="https://github.com/rcourtman/Pulse/blob/main/docs/PRIVACY.md"
|
||||
href={PRIVACY_DOC_URL}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="underline hover:text-base-content"
|
||||
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import generalSettingsPanelSource from '@/components/Settings/GeneralSettingsPanel.tsx?raw';
|
||||
|
||||
describe('GeneralSettingsPanel guardrails', () => {
|
||||
it('routes telemetry disclosure through the shipped privacy doc URL', () => {
|
||||
expect(generalSettingsPanelSource).toContain('PRIVACY_DOC_URL');
|
||||
expect(generalSettingsPanelSource).not.toContain(
|
||||
'https://github.com/rcourtman/Pulse/blob/main/docs/PRIVACY.md',
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -58,6 +58,7 @@ import billingAdminOrganizationsTableSource from '../BillingAdminOrganizationsTa
|
||||
import billingAdminPanelSource from '../BillingAdminPanel.tsx?raw';
|
||||
import billingAdminPanelStateSource from '../useBillingAdminPanelState.ts?raw';
|
||||
import generalSettingsPanelSource from '../GeneralSettingsPanel.tsx?raw';
|
||||
import docsLinksSource from '@/utils/docsLinks.ts?raw';
|
||||
import aiSettingsPanelSource from '../AISettings.tsx?raw';
|
||||
import aiChatMaintenanceSectionSource from '../AIChatMaintenanceSection.tsx?raw';
|
||||
import aiProviderConfigurationSectionSource from '../AIProviderConfigurationSection.tsx?raw';
|
||||
@@ -1247,6 +1248,8 @@ describe('Settings architecture guardrails', () => {
|
||||
|
||||
it('keeps shared system settings presentation extracted from panel and state owners', () => {
|
||||
expect(generalSettingsPanelSource).toContain('@/utils/systemSettingsPresentation');
|
||||
expect(generalSettingsPanelSource).toContain('@/utils/docsLinks');
|
||||
expect(generalSettingsPanelSource).toContain('PRIVACY_DOC_URL');
|
||||
expect(recoverySettingsPanelSource).toContain('@/utils/systemSettingsPresentation');
|
||||
expect(networkDiscoverySectionSource).toContain('@/utils/systemSettingsPresentation');
|
||||
expect(systemSettingsStateSource).toContain('@/utils/systemSettingsPresentation');
|
||||
@@ -1257,6 +1260,8 @@ describe('Settings architecture guardrails', () => {
|
||||
expect(systemSettingsPresentationSource).toContain(
|
||||
'export function getStartUpdateErrorMessage',
|
||||
);
|
||||
expect(docsLinksSource).toContain("export const SHIPPED_DOCS_ROOT = '/docs'");
|
||||
expect(docsLinksSource).toContain("export const PRIVACY_DOC_URL = getShippedDocUrl('PRIVACY.md')");
|
||||
});
|
||||
|
||||
it('routes every top-level settings surface through the canonical panel shell framing', () => {
|
||||
|
||||
@@ -37,6 +37,10 @@ describe('WhatsNewModal', () => {
|
||||
expect(whatsNewModalModelSource).toContain('WHATS_NEW_TELEMETRY_TITLE');
|
||||
expect(whatsNewModalModelSource).toContain('WHATS_NEW_DOCS_URL');
|
||||
expect(whatsNewModalModelSource).toContain('WHATS_NEW_PRIVACY_URL');
|
||||
expect(whatsNewModalModelSource).toContain('README_DOC_URL');
|
||||
expect(whatsNewModalModelSource).toContain('PRIVACY_DOC_URL');
|
||||
expect(whatsNewModalModelSource).not.toContain('https://github.com/rcourtman/Pulse/blob/main/docs/README.md');
|
||||
expect(whatsNewModalModelSource).not.toContain('https://github.com/rcourtman/Pulse/blob/main/docs/PRIVACY.md');
|
||||
expect(whatsNewModalModelSource).toContain('WHATS_NEW_DOCS_LABEL');
|
||||
expect(whatsNewModalModelSource).toContain("title: 'Infrastructure'");
|
||||
});
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { PRIVACY_DOC_URL, README_DOC_URL } from '@/utils/docsLinks';
|
||||
|
||||
export interface WhatsNewFeatureCard {
|
||||
accent: string;
|
||||
description: string;
|
||||
@@ -5,9 +7,8 @@ export interface WhatsNewFeatureCard {
|
||||
title: string;
|
||||
}
|
||||
|
||||
export const WHATS_NEW_DOCS_URL = 'https://github.com/rcourtman/Pulse/blob/main/docs/README.md';
|
||||
export const WHATS_NEW_PRIVACY_URL =
|
||||
'https://github.com/rcourtman/Pulse/blob/main/docs/PRIVACY.md';
|
||||
export const WHATS_NEW_DOCS_URL = README_DOC_URL;
|
||||
export const WHATS_NEW_PRIVACY_URL = PRIVACY_DOC_URL;
|
||||
|
||||
export const WHATS_NEW_FEATURE_CARDS: WhatsNewFeatureCard[] = [
|
||||
{
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import {
|
||||
PRIVACY_DOC_URL,
|
||||
README_DOC_URL,
|
||||
SHIPPED_DOCS_ROOT,
|
||||
getShippedDocUrl,
|
||||
} from '@/utils/docsLinks';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
const frontendRoot = path.resolve(__dirname, '..', '..', '..');
|
||||
const repoRoot = path.resolve(frontendRoot, '..');
|
||||
|
||||
describe('docsLinks', () => {
|
||||
it('returns canonical shipped doc URLs', () => {
|
||||
expect(SHIPPED_DOCS_ROOT).toBe('/docs');
|
||||
expect(getShippedDocUrl('PRIVACY.md')).toBe('/docs/PRIVACY.md');
|
||||
expect(PRIVACY_DOC_URL).toBe('/docs/PRIVACY.md');
|
||||
expect(README_DOC_URL).toBe('/docs/README.md');
|
||||
});
|
||||
|
||||
it('keeps shipped privacy and docs content synced with repo docs', () => {
|
||||
const rootPrivacy = readFileSync(path.join(repoRoot, 'docs', 'PRIVACY.md'), 'utf8');
|
||||
const publicPrivacy = readFileSync(
|
||||
path.join(frontendRoot, 'public', 'docs', 'PRIVACY.md'),
|
||||
'utf8',
|
||||
);
|
||||
const rootReadme = readFileSync(path.join(repoRoot, 'docs', 'README.md'), 'utf8');
|
||||
const publicReadme = readFileSync(path.join(frontendRoot, 'public', 'docs', 'README.md'), 'utf8');
|
||||
|
||||
expect(publicPrivacy).toBe(rootPrivacy);
|
||||
expect(publicReadme).toBe(rootReadme);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,8 @@
|
||||
export const SHIPPED_DOCS_ROOT = '/docs';
|
||||
|
||||
export function getShippedDocUrl(filename: string): string {
|
||||
return `${SHIPPED_DOCS_ROOT}/${filename}`;
|
||||
}
|
||||
|
||||
export const README_DOC_URL = getShippedDocUrl('README.md');
|
||||
export const PRIVACY_DOC_URL = getShippedDocUrl('PRIVACY.md');
|
||||
Reference in New Issue
Block a user