Add public Hesychia account-deletion page for Play Console (#103).
Unit Tests / test (pull_request) Successful in 11s
Unit Tests / test (pull_request) Successful in 11s
Expose /account-deletion/ without auth (steps, soft-delete retention copy, support path) and document the Play listing URL in Android checklists.
This commit is contained in:
+11
-3
@@ -132,12 +132,20 @@ Out of band ops (not automated here):
|
|||||||
|
|
||||||
1. Google Play developer account
|
1. Google Play developer account
|
||||||
2. Privacy policy URL (product site / legal)
|
2. Privacy policy URL (product site / legal)
|
||||||
3. Data safety form (JWT auth, chat content, analytics if Tianji runs in WebView)
|
3. **Account deletion URL** (Data safety / Account deletion) → public page:
|
||||||
4. Screenshots for phone (+ tablet if targeting)
|
- Canonical: `https://hesychia.ai/account-deletion/`
|
||||||
5. Upload `.aab` to **internal testing** track before production
|
- Legacy host until DNS cutover: `https://chat.aimloperations.com/account-deletion/`
|
||||||
|
- Capacitor HashRouter: `/#/account-deletion/`
|
||||||
|
4. Data safety form (JWT auth, chat content, analytics if Tianji runs in WebView)
|
||||||
|
5. Screenshots for phone (+ tablet if targeting)
|
||||||
|
6. Upload `.aab` to **internal testing** track before production
|
||||||
|
|
||||||
|
In-app delete remains Account → **Delete my account** (`DELETE /api/user/`). The public URL
|
||||||
|
above is for Play reviewers and users who never open the app (#103).
|
||||||
|
|
||||||
## Related issues
|
## Related issues
|
||||||
|
|
||||||
- [#20](https://git.aimloperations.com/ai_ml_operations/chat_web_app/issues/20) — this Android wrap
|
- [#20](https://git.aimloperations.com/ai_ml_operations/chat_web_app/issues/20) — this Android wrap
|
||||||
- [#21](https://git.aimloperations.com/ai_ml_operations/chat_web_app/issues/21) — iOS (reuses this scaffolding)
|
- [#21](https://git.aimloperations.com/ai_ml_operations/chat_web_app/issues/21) — iOS (reuses this scaffolding)
|
||||||
- [#22](https://git.aimloperations.com/ai_ml_operations/chat_web_app/issues/22) / [#23](https://git.aimloperations.com/ai_ml_operations/chat_web_app/issues/23) / [#24](https://git.aimloperations.com/ai_ml_operations/chat_web_app/issues/24) — auth / WS / routing
|
- [#22](https://git.aimloperations.com/ai_ml_operations/chat_web_app/issues/22) / [#23](https://git.aimloperations.com/ai_ml_operations/chat_web_app/issues/23) / [#24](https://git.aimloperations.com/ai_ml_operations/chat_web_app/issues/24) — auth / WS / routing
|
||||||
|
- [#103](https://git.aimloperations.com/ai_ml_operations/chat_web_app/issues/103) — Play public account-deletion URL
|
||||||
|
|||||||
@@ -107,9 +107,15 @@ Without `keystore.properties`, release builds are unsigned; debug builds do not
|
|||||||
|
|
||||||
1. [Play Console](https://play.google.com/console) → app **Hesychia** (`ai.hesychia.chat`).
|
1. [Play Console](https://play.google.com/console) → app **Hesychia** (`ai.hesychia.chat`).
|
||||||
2. Complete store listing: privacy policy URL, data-safety form, screenshots.
|
2. Complete store listing: privacy policy URL, data-safety form, screenshots.
|
||||||
3. **Testing → Internal testing** → create release → upload `app-release.aab`.
|
3. Set **Account deletion URL** to the public instructions page (no sign-in required):
|
||||||
4. Add testers, roll out internal track, verify on devices.
|
- `https://hesychia.ai/account-deletion/` (preferred product host)
|
||||||
5. Promote to closed / open / production only after internal QA passes.
|
- or `https://chat.aimloperations.com/account-deletion/` until hesychia.ai cutover
|
||||||
|
4. **Testing → Internal testing** → create release → upload `app-release.aab`.
|
||||||
|
5. Add testers, roll out internal track, verify on devices.
|
||||||
|
6. Promote to closed / open / production only after internal QA passes.
|
||||||
|
|
||||||
|
See also [`../ANDROID.md`](../ANDROID.md) Play checklist and issue
|
||||||
|
[#103](https://git.aimloperations.com/ai_ml_operations/chat_web_app/issues/103).
|
||||||
|
|
||||||
Each new upload needs a higher `versionCode`.
|
Each new upload needs a higher `versionCode`.
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import NotFound from './llm-fe/pages/NotFound/NotFound';
|
|||||||
import { AuthContext } from './llm-fe/contexts/AuthContext';
|
import { AuthContext } from './llm-fe/contexts/AuthContext';
|
||||||
import TermsOfService from './llm-fe/pages/TermsOfService/TermsOfService';
|
import TermsOfService from './llm-fe/pages/TermsOfService/TermsOfService';
|
||||||
import PublicTermsOfServicePage from './llm-fe/pages/TermsOfService/PublicTermsOfServicePage';
|
import PublicTermsOfServicePage from './llm-fe/pages/TermsOfService/PublicTermsOfServicePage';
|
||||||
|
import AccountDeletionPage from './llm-fe/pages/AccountDeletion/AccountDeletionPage';
|
||||||
import SetPassword from './llm-fe/components/SetPassword/SetPassword';
|
import SetPassword from './llm-fe/components/SetPassword/SetPassword';
|
||||||
import AsyncDashboard2 from './llm-fe/pages/AsyncDashboard2/AsyncDashboard2';
|
import AsyncDashboard2 from './llm-fe/pages/AsyncDashboard2/AsyncDashboard2';
|
||||||
import FeedbackPage2 from './llm-fe/pages/FeedbackPage2/FeedbackPage2';
|
import FeedbackPage2 from './llm-fe/pages/FeedbackPage2/FeedbackPage2';
|
||||||
@@ -71,6 +72,8 @@ class App extends Component {
|
|||||||
{/* Public legal pages (also used for post-login ToS acknowledge) */}
|
{/* Public legal pages (also used for post-login ToS acknowledge) */}
|
||||||
<Route path={"/terms_of_service/"} Component={TermsOfService} />
|
<Route path={"/terms_of_service/"} Component={TermsOfService} />
|
||||||
<Route path={"/legal/terms-of-service"} Component={PublicTermsOfServicePage} />
|
<Route path={"/legal/terms-of-service"} Component={PublicTermsOfServicePage} />
|
||||||
|
{/* Play Console account-deletion URL (#103) — must stay public */}
|
||||||
|
<Route path={"/account-deletion/"} Component={AccountDeletionPage} />
|
||||||
|
|
||||||
<Route element={<ProtectedRoutes />}>
|
<Route element={<ProtectedRoutes />}>
|
||||||
<Route path={"/"} index={true} Component={AsyncDashboard2} />
|
<Route path={"/"} index={true} Component={AsyncDashboard2} />
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { MemoryRouter } from 'react-router-dom';
|
||||||
|
import { render, screen } from '@testing-library/react';
|
||||||
|
import AccountDeletionPage from './AccountDeletionPage';
|
||||||
|
|
||||||
|
describe('AccountDeletionPage', () => {
|
||||||
|
it('renders Hesychia deletion steps and retention copy without auth', () => {
|
||||||
|
render(
|
||||||
|
<MemoryRouter>
|
||||||
|
<AccountDeletionPage />
|
||||||
|
</MemoryRouter>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
screen.getByRole('heading', { name: /Hesychia — Request account deletion/i }),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
expect(screen.getByText(/ai\.hesychia\.chat/i)).toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
screen.getByRole('heading', { name: /How to delete your account/i }),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
expect(screen.getByText(/Delete my account/i)).toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
screen.getByRole('heading', { name: /Deactivated immediately/i }),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
screen.getByRole('heading', { name: /Data retained after soft-delete/i }),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
expect(screen.getByRole('heading', { name: /Retention period/i })).toBeInTheDocument();
|
||||||
|
expect(screen.getByText(/no automatic hard-purge schedule/i)).toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
screen.getByRole('heading', { name: /Cannot access your account/i }),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
expect(screen.getByText(/30 days/i)).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Box, Button, Card, CardContent, Divider, Stack, Typography } from '@mui/material';
|
||||||
|
import { Link as RouterLink } from 'react-router-dom';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Public Play / App Store account-deletion instructions (#103).
|
||||||
|
* No auth required — reviewers and signed-out users must be able to read this page.
|
||||||
|
*/
|
||||||
|
const AccountDeletionPage = (): JSX.Element => (
|
||||||
|
<Box sx={{ py: 4, px: 2, minHeight: '100vh', bgcolor: 'background.default' }}>
|
||||||
|
<Card sx={{ maxWidth: 900, mx: 'auto', width: '100%' }}>
|
||||||
|
<CardContent>
|
||||||
|
<Typography variant="h3" component="h1" gutterBottom>
|
||||||
|
Hesychia — Request account deletion
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="subtitle1" color="text.secondary" gutterBottom>
|
||||||
|
Hesychia (ai.hesychia.chat) · hesychia.ai · AI ML Operations, LLC
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body1" color="text.secondary">
|
||||||
|
Use the steps below to request deletion of your Hesychia account. This page does not
|
||||||
|
require sign-in to read.
|
||||||
|
</Typography>
|
||||||
|
</CardContent>
|
||||||
|
|
||||||
|
<Divider />
|
||||||
|
|
||||||
|
<CardContent>
|
||||||
|
<Stack spacing={3}>
|
||||||
|
<Box>
|
||||||
|
<Typography variant="h5" component="h2" gutterBottom>
|
||||||
|
How to delete your account
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body1" component="ol" sx={{ pl: 2.5, m: 0 }}>
|
||||||
|
<li>
|
||||||
|
Open <strong>Hesychia</strong> in the mobile app or at{' '}
|
||||||
|
<a href="https://hesychia.ai" rel="noopener noreferrer">
|
||||||
|
hesychia.ai
|
||||||
|
</a>
|
||||||
|
.
|
||||||
|
</li>
|
||||||
|
<li>Sign in with the account you want deleted.</li>
|
||||||
|
<li>
|
||||||
|
Go to <strong>Account</strong>.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Choose <strong>Delete my account</strong>, then confirm by typing your account
|
||||||
|
email.
|
||||||
|
</li>
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Box>
|
||||||
|
<Typography variant="h5" component="h2" gutterBottom>
|
||||||
|
What happens when you delete
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<Typography variant="h6" component="h3" gutterBottom sx={{ mt: 2 }}>
|
||||||
|
Deactivated immediately
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body1" component="ul" sx={{ pl: 2.5, m: 0 }}>
|
||||||
|
<li>Your account is marked deleted and set inactive.</li>
|
||||||
|
<li>You lose access to Hesychia and cannot sign in again with that account.</li>
|
||||||
|
<li>Your conversations are hidden from the product.</li>
|
||||||
|
<li>Active sessions are invalidated (refresh tokens are blacklisted).</li>
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<Typography variant="h6" component="h3" gutterBottom sx={{ mt: 2 }}>
|
||||||
|
Data retained after soft-delete
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body1" paragraph>
|
||||||
|
Hesychia currently uses a <strong>soft-delete</strong> process. After you delete your
|
||||||
|
account, personal data associated with the account (including profile details,
|
||||||
|
conversation content, uploaded documents / RAG vectors, and authentication event
|
||||||
|
records) may remain in our systems for administration, security, and audit purposes.
|
||||||
|
This is not a full erasure of all stored data.
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<Typography variant="h6" component="h3" gutterBottom>
|
||||||
|
Retention period
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body1" paragraph sx={{ mb: 0 }}>
|
||||||
|
There is currently <strong>no automatic hard-purge schedule</strong>. Soft-deleted
|
||||||
|
data is retained until a future purge process is implemented or until we process a
|
||||||
|
separate retention / erasure request through the contact path below. We will not claim
|
||||||
|
complete erasure while soft-delete-only remains in effect.
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Box>
|
||||||
|
<Typography variant="h5" component="h2" gutterBottom>
|
||||||
|
Cannot access your account?
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body1" paragraph>
|
||||||
|
If you cannot sign in to complete in-app deletion, contact AI ML Operations, LLC via{' '}
|
||||||
|
<a href="https://www.aimloperations.com" rel="noopener noreferrer">
|
||||||
|
aimloperations.com
|
||||||
|
</a>{' '}
|
||||||
|
and include the email address used for your Hesychia account. We aim to respond to
|
||||||
|
account-deletion requests within <strong>30 days</strong>.
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body2" color="text.secondary">
|
||||||
|
Staff accounts cannot self-delete in the app; use the contact path above.
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
|
</CardContent>
|
||||||
|
|
||||||
|
<Divider />
|
||||||
|
|
||||||
|
<CardContent>
|
||||||
|
<Typography variant="body2" color="text.secondary">
|
||||||
|
Already have an account?{' '}
|
||||||
|
<Button component={RouterLink} to="/signin/" size="small">
|
||||||
|
Sign in
|
||||||
|
</Button>{' '}
|
||||||
|
·{' '}
|
||||||
|
<Button component={RouterLink} to="/account/" size="small">
|
||||||
|
Account
|
||||||
|
</Button>{' '}
|
||||||
|
·{' '}
|
||||||
|
<Button component={RouterLink} to="/terms_of_service/" size="small">
|
||||||
|
Terms of Service
|
||||||
|
</Button>
|
||||||
|
</Typography>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default AccountDeletionPage;
|
||||||
Reference in New Issue
Block a user