feat!: remove the shared demo account
The demo account — one shared user that whitelisted emails entered from the login page, reset on a schedule — goes end to end (owner decision, E34): demo.py and the reset job, users.is_demo and demo_whitelist (migration 0098 deletes the demo user, its audit rows, the reset job's override and notices, then drops the column and table; the downgrade restores the empty schema), the /auth/demo entry, the admin whitelist and reset, the Demo tab, the banner, the login page's "Try the demo", and every is_demo branch in routes, userscope, scheduler and the UI. require_human only ever refused the demo account, so its 87 uses become current_user (owner-approved); require_write_scope keeps its own check. The login page's "or" divider now shows only beside the Google button: without the demo entry under it, an instance without Google would have said "or" to nothing. A contract test (removedFeatures) fails on the word anywhere in the backend, frontend, e2e or scripts — seen red on a planted hit. Size ceilings tightened for the modules that shrank; admin.py is back under the limit.
This commit is contained in:
@@ -3,7 +3,7 @@ import { useTranslation } from "react-i18next";
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { qk } from "../lib/queryKeys";
|
||||
import { History, Pause, Play, RefreshCw } from "lucide-react";
|
||||
import { api, type AdminQuotaRow, type Me } from "../lib/api";
|
||||
import { api, type AdminQuotaRow } from "../lib/api";
|
||||
import { formatEta, formatNumber, formatUnit, quotaActionLabel } from "../lib/format";
|
||||
import { notify } from "../lib/notifications";
|
||||
import { notifyYouTubeActionError } from "../lib/youtubeErrors";
|
||||
@@ -61,13 +61,13 @@ export default function Stats() {
|
||||
</PageToolbar>
|
||||
)}
|
||||
|
||||
{tab === "system" && isAdmin ? <SystemStats /> : <Overview me={me} />}
|
||||
{tab === "system" && isAdmin ? <SystemStats /> : <Overview />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Per-user view: sync status + your own API usage + (for real accounts) the manual sync actions.
|
||||
function Overview({ me }: { me: Me }) {
|
||||
// Per-user view: sync status + your own API usage + the manual sync actions.
|
||||
function Overview() {
|
||||
const { t, i18n } = useTranslation();
|
||||
const lang = i18n.language;
|
||||
const qc = useQueryClient();
|
||||
@@ -203,32 +203,30 @@ function Overview({ me }: { me: Me }) {
|
||||
)}
|
||||
</Section>
|
||||
|
||||
{!me.is_demo && (
|
||||
<Section title={t("stats.sync.actions")}>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<Tooltip hint={t("stats.sync.syncSubscriptionsHint")}>
|
||||
<Button
|
||||
variant="glass"
|
||||
icon={<RefreshCw />}
|
||||
loading={syncSubs.isPending}
|
||||
onClick={() => syncSubs.mutate()}
|
||||
>
|
||||
{t("stats.sync.syncSubscriptions")}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Tooltip hint={t("stats.sync.backfillEverythingHint")}>
|
||||
<Button
|
||||
variant="glass"
|
||||
icon={<History />}
|
||||
loading={deepAll.isPending}
|
||||
onClick={() => deepAll.mutate()}
|
||||
>
|
||||
{t("stats.sync.backfillEverything")}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</Section>
|
||||
)}
|
||||
<Section title={t("stats.sync.actions")}>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<Tooltip hint={t("stats.sync.syncSubscriptionsHint")}>
|
||||
<Button
|
||||
variant="glass"
|
||||
icon={<RefreshCw />}
|
||||
loading={syncSubs.isPending}
|
||||
onClick={() => syncSubs.mutate()}
|
||||
>
|
||||
{t("stats.sync.syncSubscriptions")}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Tooltip hint={t("stats.sync.backfillEverythingHint")}>
|
||||
<Button
|
||||
variant="glass"
|
||||
icon={<History />}
|
||||
loading={deepAll.isPending}
|
||||
onClick={() => deepAll.mutate()}
|
||||
>
|
||||
{t("stats.sync.backfillEverything")}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</Section>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user