From 2a6fde86a1407cdfc653dac207c000425059c3e4 Mon Sep 17 00:00:00 2001 From: npeter83 Date: Sun, 14 Jun 2026 01:29:44 +0200 Subject: [PATCH] feat(legal): public privacy policy, terms, and homepage for OAuth verification Adds login-free /privacy and /terms pages (rendered outside the authenticated tree via a pathname switch in main.tsx) carrying the Google API Services Limited Use disclosure, YouTube ToS / Google Privacy links, and contact + data-deletion info. The sign-in screen now describes the app and links to both, satisfying Google's homepage + privacy-policy requirements for the OAuth consent screen. --- frontend/src/components/Login.tsx | 10 +- frontend/src/components/legal/LegalLayout.tsx | 47 ++++++++ .../src/components/legal/PrivacyPolicy.tsx | 111 ++++++++++++++++++ frontend/src/components/legal/Terms.tsx | 67 +++++++++++ frontend/src/main.tsx | 16 ++- 5 files changed, 245 insertions(+), 6 deletions(-) create mode 100644 frontend/src/components/legal/LegalLayout.tsx create mode 100644 frontend/src/components/legal/PrivacyPolicy.tsx create mode 100644 frontend/src/components/legal/Terms.tsx diff --git a/frontend/src/components/Login.tsx b/frontend/src/components/Login.tsx index 31b5774..89845ba 100644 --- a/frontend/src/components/Login.tsx +++ b/frontend/src/components/Login.tsx @@ -35,9 +35,9 @@ export default function Login() { Subfeed

- Your subscriptions, filtered your way. -
- Sign in with your Google account. + A self-hosted, filterable feed of your YouTube subscriptions — sort, tag, and + tune out the noise. Sign in with Google to get started; YouTube access is an + optional, separate step you control.

+ ); } diff --git a/frontend/src/components/legal/LegalLayout.tsx b/frontend/src/components/legal/LegalLayout.tsx new file mode 100644 index 0000000..a6bb34c --- /dev/null +++ b/frontend/src/components/legal/LegalLayout.tsx @@ -0,0 +1,47 @@ +// Shared shell for the public, login-free legal pages (/privacy, /terms). These must be +// reachable unauthenticated — Google's OAuth consent screen links to them and reviewers +// fetch them directly — so they render outside the authenticated App tree (see main.tsx). + +export const CONTACT_EMAIL = "b1fr0stmailops@gmail.com"; +export const LAST_UPDATED = "June 14, 2026"; + +export function LegalLink({ href, children }: { href: string; children: React.ReactNode }) { + return ( + + {children} + + ); +} + +export default function LegalLayout({ + title, + children, +}: { + title: string; + children: React.ReactNode; +}) { + return ( +
+
+ + Subfeed + +
+

{title}

+

Last updated: {LAST_UPDATED}

+
{children}
+
+ +
+
+ ); +} + +export function H2({ children }: { children: React.ReactNode }) { + return

{children}

; +} diff --git a/frontend/src/components/legal/PrivacyPolicy.tsx b/frontend/src/components/legal/PrivacyPolicy.tsx new file mode 100644 index 0000000..32dde1f --- /dev/null +++ b/frontend/src/components/legal/PrivacyPolicy.tsx @@ -0,0 +1,111 @@ +import LegalLayout, { CONTACT_EMAIL, H2, LegalLink } from "./LegalLayout"; + +export default function PrivacyPolicy() { + return ( + +

+ Subfeed is a personal, non-commercial project operated by an individual in Hungary + ("we", "us"). It lets you view and organize your own YouTube subscriptions as a + filterable feed. This policy explains what data Subfeed accesses, how it is used, and + the choices you have. By using Subfeed you agree to this policy. +

+ +

What we access

+

Subfeed only accesses data you explicitly authorize, in two separate steps:

+ +

+ You can use Subfeed with sign-in alone, and grant or decline each YouTube permission + independently. +

+ +

How we use it

+

+ Your data is used exclusively to provide Subfeed's features to you: building and + displaying your personalized subscription feed, and carrying out actions (such as + unsubscribing) that you initiate. We do not use it for advertising, profiling, or any + purpose unrelated to the app. +

+ +

Limited Use disclosure

+

+ Subfeed's use and transfer of information received from Google APIs to any other app + will adhere to the{" "} + + Google API Services User Data Policy + + , including the Limited Use requirements. We do not sell your data, do not transfer it + to third parties (except as needed to operate the app, e.g. the hosting infrastructure + we control), and do not use it for advertising. +

+ +

Storage & security

+

+ Data is stored in a private PostgreSQL database on a server we control, accessible only + to the operator. Google OAuth refresh tokens are encrypted at rest. Access is restricted + to the emails explicitly invited to the app. No system is perfectly secure, but we take + reasonable measures to protect your data. +

+ +

Retention & deletion

+

+ We keep your data while your account is active. You can revoke Subfeed's access to your + Google data at any time at{" "} + + myaccount.google.com/permissions + + ; once revoked, the stored tokens can no longer be used. To have your account and + associated data deleted, email{" "} + + {CONTACT_EMAIL} + + . +

+ +

Cookies

+

+ Subfeed sets a single, signed session cookie to keep you logged in. It is not used for + tracking or advertising, and there are no third-party analytics cookies. +

+ +

YouTube & Google

+

+ Subfeed uses YouTube API Services. By using Subfeed you also agree to the{" "} + YouTube Terms of Service, + and your use of Google data is subject to the{" "} + Google Privacy Policy. +

+ +

Changes

+

+ We may update this policy; material changes will be reflected by the "Last updated" date + above. Continued use after a change constitutes acceptance. +

+ +

Contact

+

+ Questions about this policy or your data:{" "} + + {CONTACT_EMAIL} + + . +

+
+ ); +} diff --git a/frontend/src/components/legal/Terms.tsx b/frontend/src/components/legal/Terms.tsx new file mode 100644 index 0000000..ea7f159 --- /dev/null +++ b/frontend/src/components/legal/Terms.tsx @@ -0,0 +1,67 @@ +import LegalLayout, { CONTACT_EMAIL, H2, LegalLink } from "./LegalLayout"; + +export default function Terms() { + return ( + +

+ Subfeed is a personal, non-commercial project operated by an individual in Hungary. It + provides a filterable view of your own YouTube subscriptions. By accessing or using + Subfeed, you agree to these Terms. If you do not agree, do not use the service. +

+ +

The service

+

+ Subfeed is offered on an invite-only basis, free of charge, and "as is", without + warranties of any kind. It is a hobby project: features may change, and the service may + be slowed, interrupted, or discontinued at any time without notice. +

+ +

Your account & acceptable use

+

+ You sign in with your Google account and may only access Subfeed with an email that has + been invited. You agree to use the service lawfully, not to abuse, disrupt, probe, or + overload it, and not to attempt to access other users' data. We may suspend or revoke + access at our discretion, for example in case of misuse. +

+ +

Third-party services

+

+ Subfeed uses YouTube API Services. By using Subfeed you agree to be bound by the{" "} + YouTube Terms of Service. + Your data is handled as described in our{" "} + Privacy Policy and, for + Google data, the{" "} + Google Privacy Policy. +

+ +

No warranty & limitation of liability

+

+ To the maximum extent permitted by law, the service is provided without warranty, and + the operator is not liable for any indirect, incidental, or consequential damages, or + for any loss of data, arising from your use of Subfeed. Your sole remedy if you are + dissatisfied is to stop using the service and revoke its access to your account. +

+ +

Governing law

+

+ These Terms are governed by the laws of Hungary and applicable European Union law, + without regard to conflict-of-law rules. +

+ +

Changes

+

+ We may update these Terms; the "Last updated" date above reflects the latest version. + Continued use after a change constitutes acceptance. +

+ +

Contact

+

+ Questions:{" "} + + {CONTACT_EMAIL} + + . +

+
+ ); +} diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx index 4ec3461..9f6e91b 100644 --- a/frontend/src/main.tsx +++ b/frontend/src/main.tsx @@ -3,18 +3,28 @@ import { createRoot } from "react-dom/client"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import App from "./App"; import ErrorBoundary from "./components/ErrorBoundary"; +import PrivacyPolicy from "./components/legal/PrivacyPolicy"; +import Terms from "./components/legal/Terms"; import "./index.css"; const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false, staleTime: 30_000, refetchOnWindowFocus: false } }, }); -createRoot(document.getElementById("root")!).render( - +// Public, login-free legal pages. They live outside the authenticated App tree (no /api/me) +// so Google's consent screen and reviewers can fetch them directly. Reached by full-page +// navigation (plain ), so a simple pathname switch is enough — no router needed. +const path = window.location.pathname; +const root = + path === "/privacy" ? : + path === "/terms" ? : ( - + ); + +createRoot(document.getElementById("root")!).render( + {root} );