Really huge mass update; Getting everything up-to-spec and implementing a wide range of features
Deploy / Build (pull_request) Successful in 40s
Deploy / Build and Push Docker Image (pull_request) Has been skipped

This commit is contained in:
2026-07-26 14:24:18 +02:00
parent 2c563685bd
commit 8b53698f29
72 changed files with 4275 additions and 693 deletions
+135 -33
View File
@@ -1,9 +1,75 @@
import React, { useEffect, useState } from "react";
import { Link } from "react-router-dom";
import { api } from "../services/api";
import { usePageTitle } from "../hooks/usePageTitle";
import {
ArrowLeft,
Clock3,
Database,
KeyRound,
Mail,
Server,
Share2,
ShieldCheck,
UserCheck,
} from "lucide-react";
const policySections = [
{
title: "Information We Process",
Icon: Database,
items: [
"Account information, including your username and a one-way hashed password.",
"Gitea connection details, including your Gitea username, instance URL, and Personal Access Token.",
"AWS credentials and region settings used to create and manage preview infrastructure.",
"Repository and pull request metadata, such as repository name, PR number, title, and commit SHA.",
"Preview deployment logs, job status, preview URLs, EC2 instance identifiers, and related operational metadata.",
"Ephemeral SSH private keys generated for preview instances.",
],
},
{
title: "How Information Is Used",
Icon: KeyRound,
items: [
"Gitea credentials are used to register and maintain webhooks, clone repositories, read pull request metadata, and post preview status comments.",
"AWS credentials are used to provision, tag, inspect, and terminate EC2 resources for previews in your AWS account.",
"Deployment logs and metadata are used to show preview status, diagnose failed builds, and support administrative operation of this instance.",
"Webhook secrets are used to verify that incoming webhook requests were sent by the configured Gitea instance.",
],
},
];
const detailSections = [
{
title: "Security",
Icon: ShieldCheck,
body: "Sensitive credentials, including Gitea tokens, AWS access keys, AWS secret access keys, and SSH private keys, are encrypted at rest with AES-256-GCM. Passwords are stored as bcrypt hashes. Webhook signatures are verified before webhook payloads are processed.",
},
{
title: "Infrastructure",
Icon: Server,
body: "Preview instances are launched in your AWS account using the credentials you provide. PR Previews manages only the resources required to operate previews, including EC2 instances, security groups, and temporary SSH keys. Preview instances are terminated when a pull request is closed, a preview is manually stopped, or the configured inactivity timeout is reached.",
},
{
title: "Retention",
Icon: Clock3,
body: "Preview records, logs, and metadata are retained according to the retention period configured by the instance administrator. By default, stopped and failed preview records are eligible for cleanup after 30 days. Ephemeral SSH keys are removed when their associated preview instance is terminated.",
},
{
title: "Data Sharing",
Icon: Share2,
body: "This instance does not include third-party analytics, advertising trackers, or external data sharing features. Data is processed by this PR Previews instance, the configured Gitea instance, and AWS services in the account used for preview infrastructure.",
},
{
title: "Your Responsibilities",
Icon: UserCheck,
body: "Users are responsible for providing credentials with appropriate scopes and for managing access to the Gitea repositories and AWS accounts connected to this instance. Administrators are responsible for configuring retention, access control, and operational policies for this deployment.",
},
];
export function Privacy() {
const [contactEmail, setContactEmail] = useState<string | null>(null);
usePageTitle("Privacy Policy");
useEffect(() => {
api.admin.getSettings().then(res => {
@@ -12,43 +78,79 @@ export function Privacy() {
}, []);
return (
<div className="max-w-2xl prose dark:prose-invert">
<Link to="/" className="text-sm text-blue-600 dark:text-blue-400 hover:underline mb-4 inline-block"> Back</Link>
<h1>Privacy Policy</h1>
<p>This is a self-hosted instance of PR Previews (PP). The following describes what data PP stores and how it is used.</p>
<div className="mx-auto max-w-5xl space-y-6">
<Link to="/" className="inline-flex items-center gap-1.5 text-sm font-medium text-blue-600 dark:text-blue-400 hover:underline">
<ArrowLeft size={16} aria-hidden="true" />
Back
</Link>
<h2>What We Store</h2>
<ul>
<li>Your username and hashed password (bcrypt).</li>
<li>Your Gitea Personal Access Token (PAT), encrypted at rest with AES-256-GCM.</li>
<li>Your AWS Access Key ID and Secret Access Key, encrypted at rest with AES-256-GCM.</li>
<li>Preview logs, PR metadata (PR number, title, commit SHA), and EC2 instance details.</li>
<li>SSH private keys (ephemeral per launch, encrypted at rest, deleted on instance termination).</li>
</ul>
<section className="border-b border-gray-200 dark:border-slate-700 pb-6">
<div className="inline-flex items-center gap-2 rounded-full bg-blue-50 dark:bg-blue-950/40 px-3 py-1 text-xs font-medium text-blue-700 dark:text-blue-300">
<ShieldCheck size={14} aria-hidden="true" />
Self-hosted privacy policy
</div>
<h1 className="mt-4 text-3xl font-bold tracking-tight text-gray-950 dark:text-slate-50 sm:text-4xl">
Privacy Policy
</h1>
<p className="mt-3 max-w-3xl text-base leading-7 text-gray-600 dark:text-slate-300">
PR Previews is a self-hosted preview deployment service for Gitea pull requests. This policy explains what information this instance processes, why it is needed, and how it is protected.
</p>
</section>
<h2>How We Use Your Data</h2>
<ul>
<li>Your Gitea PAT is used solely to register webhooks, clone repositories, and post preview status comments on PRs.</li>
<li>Your AWS credentials are used solely to provision EC2 instances for previews in your own AWS account.</li>
<li>PP does not have access to data on EC2 instances beyond what it deploys.</li>
<li>No data is shared with third parties.</li>
</ul>
<div className="grid gap-4 lg:grid-cols-2">
{policySections.map(({ title, Icon, items }) => (
<section key={title} className="rounded-xl border border-gray-200 bg-white p-5 dark:border-slate-700 dark:bg-slate-800">
<div className="mb-4 flex items-center gap-3">
<span className="flex h-10 w-10 items-center justify-center rounded-lg bg-blue-50 text-blue-700 dark:bg-blue-950/50 dark:text-blue-300">
<Icon size={20} aria-hidden="true" />
</span>
<h2 className="text-lg font-semibold text-gray-950 dark:text-slate-50">{title}</h2>
</div>
<ul className="space-y-2.5 text-sm leading-6 text-gray-600 dark:text-slate-300">
{items.map(item => (
<li key={item} className="flex gap-2">
<span className="mt-2 h-1.5 w-1.5 shrink-0 rounded-full bg-blue-500 dark:bg-blue-400" />
<span>{item}</span>
</li>
))}
</ul>
</section>
))}
</div>
<h2>Data Retention</h2>
<p>Preview records (logs, metadata) are retained for the number of days configured by the administrator (default: 30 days after a preview is stopped or failed). You can view this setting in the admin panel.</p>
<div className="grid gap-4 md:grid-cols-2">
{detailSections.map(({ title, Icon, body }) => (
<section key={title} className="rounded-xl border border-gray-200 bg-white p-5 dark:border-slate-700 dark:bg-slate-800">
<div className="mb-3 flex items-center gap-3">
<span className="flex h-9 w-9 items-center justify-center rounded-lg bg-gray-100 text-gray-700 dark:bg-slate-700 dark:text-slate-200">
<Icon size={18} aria-hidden="true" />
</span>
<h2 className="text-base font-semibold text-gray-950 dark:text-slate-50">{title}</h2>
</div>
<p className="text-sm leading-6 text-gray-600 dark:text-slate-300">{body}</p>
</section>
))}
</div>
<h2>EC2 Instances</h2>
<p>Preview instances are launched in your own AWS account using your credentials. PP terminates them on PR close, inactivity timeout, or manual stop. PP does not retain any data from inside EC2 instances beyond what is captured in preview logs.</p>
<h2>Analytics &amp; Tracking</h2>
<p>No analytics, no tracking, no external data sharing. PP is fully self-contained.</p>
<h2>Contact</h2>
{contactEmail ? (
<p>For questions or concerns, contact the instance administrator at <a href={`mailto:${contactEmail}`} className="text-blue-600 dark:text-blue-400 underline">{contactEmail}</a>.</p>
) : (
<p>For questions or concerns, contact the instance administrator.</p>
)}
<section className="rounded-xl border border-blue-200 bg-blue-50 p-5 dark:border-blue-900/70 dark:bg-blue-950/30">
<div className="flex items-start gap-3">
<span className="flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-white text-blue-700 dark:bg-slate-900 dark:text-blue-300">
<Mail size={20} aria-hidden="true" />
</span>
<div>
<h2 className="text-base font-semibold text-gray-950 dark:text-slate-50">Contact</h2>
{contactEmail ? (
<p className="mt-1 text-sm leading-6 text-gray-600 dark:text-slate-300">
For privacy questions or requests related to this instance, contact the instance administrator at <a href={`mailto:${contactEmail}`} className="font-medium text-blue-700 underline dark:text-blue-300">{contactEmail}</a>.
</p>
) : (
<p className="mt-1 text-sm leading-6 text-gray-600 dark:text-slate-300">
For privacy questions or requests related to this instance, contact the instance administrator.
</p>
)}
</div>
</div>
</section>
</div>
);
}