mirror of
https://github.com/jjbrunton/Sauci.git
synced 2026-10-31 18:19:43 +00:00
fix(web): add account deletion page (#16)
Add the public deletion resource and repair the monorepo web container build contract.
This commit is contained in:
11 files changed
+162
-14
No files matched your search
@@ -1,5 +1,7 @@
|
||||
node_modules
|
||||
**/node_modules
|
||||
.next
|
||||
**/.next
|
||||
.git
|
||||
.gitignore
|
||||
*.md
|
||||
@@ -23,11 +25,15 @@ apps/mcp
|
||||
apps/mobile
|
||||
apps/supabase
|
||||
apps/web
|
||||
!apps/web
|
||||
!apps/web/**
|
||||
e2e
|
||||
evidence
|
||||
etc
|
||||
infra
|
||||
scripts
|
||||
!scripts
|
||||
!scripts/fix-hoisting.js
|
||||
terraform
|
||||
.agents
|
||||
.claude
|
||||
|
||||
+10
-12
@@ -12,15 +12,12 @@ ENV NEXT_PUBLIC_POSTHOG_KEY=$NEXT_PUBLIC_POSTHOG_KEY
|
||||
ENV NEXT_PUBLIC_POSTHOG_HOST=$NEXT_PUBLIC_POSTHOG_HOST
|
||||
ENV NEXT_PUBLIC_API_URL=$NEXT_PUBLIC_API_URL
|
||||
|
||||
# Install dependencies
|
||||
COPY package*.json ./
|
||||
# Install dependencies from the committed monorepo lockfile.
|
||||
COPY . .
|
||||
RUN npm ci
|
||||
|
||||
# Copy source code
|
||||
COPY . .
|
||||
|
||||
# Build the application
|
||||
RUN npm run build
|
||||
# Build the web workspace from the repository root.
|
||||
RUN npm run build -w @sauci/web
|
||||
|
||||
# Production stage
|
||||
FROM node:20-alpine AS runner
|
||||
@@ -37,10 +34,11 @@ RUN apk add --no-cache curl
|
||||
RUN addgroup --system --gid 1001 nodejs
|
||||
RUN adduser --system --uid 1001 nextjs
|
||||
|
||||
# Copy built assets from builder
|
||||
COPY --from=builder /app/public ./public
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
|
||||
# Next preserves the workspace path in standalone output. Keep that layout so
|
||||
# the generated server can resolve its .next directory and public assets.
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/apps/web/.next/standalone ./
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/apps/web/public ./apps/web/public
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/apps/web/.next/static ./apps/web/.next/static
|
||||
|
||||
USER nextjs
|
||||
|
||||
@@ -49,4 +47,4 @@ EXPOSE 3000
|
||||
ENV PORT=3000
|
||||
ENV HOSTNAME="0.0.0.0"
|
||||
|
||||
CMD ["node", "server.js"]
|
||||
CMD ["node", "apps/web/server.js"]
|
||||
@@ -0,0 +1,19 @@
|
||||
import { readFile } from 'node:fs/promises'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
const pagePath = new URL('./page.tsx', import.meta.url)
|
||||
|
||||
describe('/delete-account public deletion resource', () => {
|
||||
it('provides an accessible request route and the required deletion details', async () => {
|
||||
const page = await readFile(pagePath, 'utf8')
|
||||
|
||||
expect(page).toContain('Delete your Sauci account')
|
||||
expect(page).toContain('mailto:privacy@sauci.app')
|
||||
expect(page).toContain('without reinstalling the app')
|
||||
expect(page).toContain('Open Sauci and go to your Profile.')
|
||||
expect(page).toContain('Choose Delete Account and follow the confirmation steps.')
|
||||
expect(page).toContain('within 30 days')
|
||||
expect(page).toContain('required for legal purposes')
|
||||
expect(page).toContain('relationship data, question responses, match data, chat messages and photos')
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,89 @@
|
||||
import Link from 'next/link'
|
||||
import { ArrowLeft } from 'lucide-react'
|
||||
import type { Metadata } from 'next'
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Delete your Sauci account',
|
||||
description: 'How to delete your Sauci account and associated data.',
|
||||
}
|
||||
|
||||
export default function DeleteAccountPage() {
|
||||
return (
|
||||
<main className="min-h-screen bg-gradient-to-b from-background-light to-background">
|
||||
<header className="py-6 px-6 border-b border-white/5">
|
||||
<div className="max-w-4xl mx-auto flex items-center gap-4">
|
||||
<Link href="/" className="flex items-center gap-2 text-white/60 hover:text-white transition-colors">
|
||||
<ArrowLeft className="w-5 h-5" />
|
||||
<span>Back to Home</span>
|
||||
</Link>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div className="max-w-4xl mx-auto px-6 py-12">
|
||||
<h1 className="text-4xl font-bold mb-4">Delete your Sauci account</h1>
|
||||
<p className="text-white/70 leading-relaxed text-lg">
|
||||
You can request deletion of your Sauci account and associated data without reinstalling the app.
|
||||
</p>
|
||||
|
||||
<div className="glass p-6 my-8">
|
||||
<h2 className="text-2xl font-semibold text-white mb-3">Request deletion by email</h2>
|
||||
<p className="text-white/70 leading-relaxed mb-4">
|
||||
Email us from the address associated with your Sauci account and ask us to delete your account.
|
||||
</p>
|
||||
<a
|
||||
href="mailto:privacy@sauci.app?subject=Sauci%20account%20deletion%20request"
|
||||
className="inline-flex rounded-lg bg-primary px-5 py-3 font-semibold text-white hover:opacity-90 transition-opacity"
|
||||
>
|
||||
Request account deletion
|
||||
</a>
|
||||
<p className="text-white/60 mt-4">
|
||||
Or email <a href="mailto:privacy@sauci.app" className="text-primary hover:underline">privacy@sauci.app</a>.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="prose prose-invert prose-lg max-w-none space-y-8">
|
||||
<section>
|
||||
<h2 className="text-2xl font-semibold text-white mb-4">Delete your account in the Sauci app</h2>
|
||||
<ol className="list-decimal pl-6 text-white/70 space-y-2">
|
||||
<li>Open Sauci and go to your Profile.</li>
|
||||
<li>Select Account.</li>
|
||||
<li>Choose Delete Account and follow the confirmation steps.</li>
|
||||
</ol>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 className="text-2xl font-semibold text-white mb-4">What deletion covers</h2>
|
||||
<p className="text-white/70 leading-relaxed">
|
||||
Deleting an account permanently removes the account and profile, relationship data, question responses, match data, chat messages and photos, encryption keys, and other cloud-stored data associated with that account.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 className="text-2xl font-semibold text-white mb-4">Timing and limited retention</h2>
|
||||
<p className="text-white/70 leading-relaxed">
|
||||
We will delete or anonymize your personal information within 30 days. We may retain information where required for legal purposes.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 className="text-2xl font-semibold text-white mb-4">Need help?</h2>
|
||||
<p className="text-white/70 leading-relaxed">
|
||||
For questions about deletion or your data, contact <a href="mailto:privacy@sauci.app" className="text-primary hover:underline">privacy@sauci.app</a>.
|
||||
</p>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer className="py-8 px-6 border-t border-white/5">
|
||||
<div className="max-w-4xl mx-auto flex flex-col sm:flex-row items-center justify-between gap-4">
|
||||
<span className="text-xl font-bold gradient-text">Sauci</span>
|
||||
<div className="flex gap-6 text-sm text-white/40">
|
||||
<Link href="/privacy" className="hover:text-white transition-colors">Privacy</Link>
|
||||
<Link href="/terms" className="hover:text-white transition-colors">Terms</Link>
|
||||
<Link href="/" className="hover:text-white transition-colors">Home</Link>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
@@ -132,6 +132,9 @@ export default function PrivacyPolicy() {
|
||||
<p className="text-white/70 leading-relaxed mt-4">
|
||||
To exercise these rights, please contact us at <a href="mailto:privacy@sauci.app" className="text-primary hover:underline">privacy@sauci.app</a>.
|
||||
</p>
|
||||
<p className="text-white/70 leading-relaxed mt-4">
|
||||
For account deletion instructions and an email deletion request, visit our <Link href="/delete-account" className="text-primary hover:underline">account deletion page</Link>.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
@@ -176,6 +179,7 @@ export default function PrivacyPolicy() {
|
||||
<span className="text-xl font-bold gradient-text">Sauci</span>
|
||||
<div className="flex gap-6 text-sm text-white/40">
|
||||
<Link href="/privacy" className="hover:text-white transition-colors">Privacy</Link>
|
||||
<Link href="/delete-account" className="hover:text-white transition-colors">Delete Account</Link>
|
||||
<Link href="/terms" className="hover:text-white transition-colors">Terms</Link>
|
||||
<Link href="/" className="hover:text-white transition-colors">Home</Link>
|
||||
</div>
|
||||
|
||||
@@ -175,6 +175,7 @@ export default function RedeemPage() {
|
||||
<span className="text-xl font-bold gradient-text">Sauci</span>
|
||||
<div className="flex gap-6 text-sm text-white/40">
|
||||
<Link href="/privacy" className="hover:text-white transition-colors">Privacy</Link>
|
||||
<Link href="/delete-account" className="hover:text-white transition-colors">Delete Account</Link>
|
||||
<Link href="/terms" className="hover:text-white transition-colors">Terms</Link>
|
||||
<Link href="/" className="hover:text-white transition-colors">Home</Link>
|
||||
</div>
|
||||
|
||||
@@ -237,6 +237,7 @@ export default function TermsAndConditions() {
|
||||
<span className="text-xl font-bold gradient-text">Sauci</span>
|
||||
<div className="flex gap-6 text-sm text-white/40">
|
||||
<Link href="/privacy" className="hover:text-white transition-colors">Privacy</Link>
|
||||
<Link href="/delete-account" className="hover:text-white transition-colors">Delete Account</Link>
|
||||
<Link href="/terms" className="hover:text-white transition-colors">Terms</Link>
|
||||
<Link href="/" className="hover:text-white transition-colors">Home</Link>
|
||||
</div>
|
||||
|
||||
@@ -27,6 +27,9 @@ export default function Footer() {
|
||||
<Link href="/privacy" className="text-white/60 hover:text-white transition-colors text-sm">
|
||||
Privacy Policy
|
||||
</Link>
|
||||
<Link href="/delete-account" className="text-white/60 hover:text-white transition-colors text-sm">
|
||||
Delete Account
|
||||
</Link>
|
||||
<Link href="/terms" className="text-white/60 hover:text-white transition-colors text-sm">
|
||||
Terms & Conditions
|
||||
</Link>
|
||||
|
||||
@@ -3,8 +3,8 @@ version: '3.8'
|
||||
services:
|
||||
web:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
context: ../..
|
||||
dockerfile: apps/web/Dockerfile
|
||||
args:
|
||||
- NEXT_PUBLIC_POSTHOG_KEY=${NEXT_PUBLIC_POSTHOG_KEY}
|
||||
- NEXT_PUBLIC_POSTHOG_HOST=${NEXT_PUBLIC_POSTHOG_HOST:-https://eu.i.posthog.com}
|
||||
|
||||
@@ -11,6 +11,7 @@ historical notes. Agents should load only the section relevant to their task.
|
||||
- [Testing strategy](testing/strategy.md)
|
||||
- [Database migration safety](operations/database-migrations.md)
|
||||
- [Backend deployment and branching](operations/backend-deployments.md)
|
||||
- [Website deployment](operations/web-deployments.md)
|
||||
- [Agent routing and effort](agents/routing.md)
|
||||
- [Learning promotion](agents/learnings.md)
|
||||
- [Authentication](authentication.md)
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
# Website deployment
|
||||
|
||||
Dokploy deploys the public Sauci website from the `main` branch using
|
||||
`apps/web/docker-compose.prod.yml`. The Compose file builds from the repository
|
||||
root so the Docker build can use the committed workspace lockfile. Its Dockerfile
|
||||
builds `@sauci/web` and runs the Next standalone server from its workspace path.
|
||||
|
||||
## Required configuration
|
||||
|
||||
`NEXT_PUBLIC_API_URL` is required at build time and must be the intended public
|
||||
standalone API URL. `NEXT_PUBLIC_POSTHOG_KEY` is optional. When it is set,
|
||||
`NEXT_PUBLIC_POSTHOG_HOST` defaults to `https://eu.i.posthog.com` unless Dokploy
|
||||
provides a different value. These public values are inlined into the Next build;
|
||||
do not place server credentials in them.
|
||||
|
||||
## Promotion and acceptance
|
||||
|
||||
Promote the verified website commit to `main` before allowing Dokploy to deploy
|
||||
it. After the deployment reports success, verify valid TLS and HTTP 200 for:
|
||||
|
||||
- `https://sauci.app/`
|
||||
- `https://sauci.app/delete-account`
|
||||
|
||||
The second check is required for the Google Play account-deletion resource. A
|
||||
successful Dokploy deployment alone is not sufficient evidence that either URL
|
||||
is publicly reachable.
|
||||
Reference in new issue
Block a user