feat(PROJ-53): Konfigurierbare Listenanzahl pro Seite

- users.list_page_size (Default 25), PATCH /api/auth/preferences,
  Whitelist 25/50/100/200, Wert in login/me-Response
- Settings-UI mit Select, /search nutzt gespeicherte Seitengröße
- /api/search page_size serverseitig auf max. 500 gecappt

fix(PROJ-46): login_attempts-Migration nutzte s.db statt s.pool
(Backend kompilierte nicht)

feat(PROJ-50): DSGVO-Löschersuchen Backend (dsgvo_requests, Handler,
cc_addr/bcc_addr Indexerweiterung) — noch nicht QA'd/deployed
This commit is contained in:
sysops
2026-06-14 22:25:02 +02:00
parent b73ef55a65
commit 472ba6a087
25 changed files with 1078 additions and 111 deletions
+9 -7
View File
@@ -44,7 +44,7 @@ import { Label } from "@/components/ui/label";
import { Switch } from "@/components/ui/switch";
import { Bookmark, BookmarkPlus, Trash2 } from "lucide-react";
const PAGE_SIZE = 25;
const DEFAULT_PAGE_SIZE = 25;
function formatBytes(bytes: number): string {
if (bytes < 1024) return `${bytes} B`;
@@ -86,6 +86,8 @@ export default function SearchPage() {
const { user, loading: authLoading } = useAuth();
const router = useRouter();
const pageSize = user?.list_page_size ?? DEFAULT_PAGE_SIZE;
const [query, setQuery] = useState("");
const [fromFilter, setFromFilter] = useState("");
const [toFilter, setToFilter] = useState("");
@@ -143,7 +145,7 @@ export default function SearchPage() {
sort: sort !== "date_desc" ? sort : undefined,
has_attachment: hasAttachment,
page: p,
page_size: PAGE_SIZE,
page_size: pageSize,
});
setResults(res.hits || []);
setTotal(res.total);
@@ -156,7 +158,7 @@ export default function SearchPage() {
setSearching(false);
}
},
[query, fromFilter, toFilter, dateFrom, dateTo, sort, hasAttachment]
[query, fromFilter, toFilter, dateFrom, dateTo, sort, hasAttachment, pageSize]
);
// Superadmin has no mail access — redirect to admin dashboard
@@ -170,7 +172,7 @@ export default function SearchPage() {
useEffect(() => {
if (!user || user.role === "superadmin") return;
setSearching(true);
searchEmails({ page: 1, page_size: PAGE_SIZE })
searchEmails({ page: 1, page_size: pageSize })
.then((res) => {
setResults(res.hits || []);
setTotal(res.total);
@@ -182,7 +184,7 @@ export default function SearchPage() {
setTotal(0);
})
.finally(() => setSearching(false));
}, [user]);
}, [user, pageSize]);
function handleSubmit(e: React.FormEvent) {
e.preventDefault();
@@ -339,7 +341,7 @@ export default function SearchPage() {
date_to: s.query.date_to || undefined,
has_attachment: s.query.has_attachment === "true" ? true : undefined,
page: 1,
page_size: PAGE_SIZE,
page_size: pageSize,
})
.then((res) => {
setResults(res.hits || []);
@@ -364,7 +366,7 @@ export default function SearchPage() {
}
}
const totalPages = Math.ceil(total / PAGE_SIZE);
const totalPages = Math.ceil(total / pageSize);
const allSelected = results.length > 0 && results.every((h) => selected.has(h.id));
return (