update
This commit is contained in:
@@ -17,7 +17,9 @@ import nextcloudService from '../services/nextcloud.service';
|
|||||||
import logger from '../utils/logger';
|
import logger from '../utils/logger';
|
||||||
|
|
||||||
const INTERVAL_MS = 15 * 60 * 1000; // 15 minutes
|
const INTERVAL_MS = 15 * 60 * 1000; // 15 minutes
|
||||||
const NEXTCLOUD_INTERVAL_MS = 2 * 60 * 1000; // 2 minutes
|
const NEXTCLOUD_INTERVAL_MS = 5 * 60 * 1000; // 5 minutes — keep reasonable to avoid rate-limiting
|
||||||
|
const NEXTCLOUD_BATCH_DELAY_MS = 300; // ms between user batches to avoid hammering Nextcloud
|
||||||
|
const STARTUP_DELAY_MS = 30 * 1000; // 30 seconds — avoid burst on container restart
|
||||||
const ATEMSCHUTZ_THRESHOLD = 60; // days
|
const ATEMSCHUTZ_THRESHOLD = 60; // days
|
||||||
|
|
||||||
let jobInterval: ReturnType<typeof setInterval> | null = null;
|
let jobInterval: ReturnType<typeof setInterval> | null = null;
|
||||||
@@ -267,6 +269,9 @@ async function generateNextcloudTalkNotifications(): Promise<void> {
|
|||||||
const users = usersResult.rows;
|
const users = usersResult.rows;
|
||||||
|
|
||||||
for (let i = 0; i < users.length; i += NEXTCLOUD_BATCH_SIZE) {
|
for (let i = 0; i < users.length; i += NEXTCLOUD_BATCH_SIZE) {
|
||||||
|
if (i > 0) {
|
||||||
|
await new Promise((r) => setTimeout(r, NEXTCLOUD_BATCH_DELAY_MS));
|
||||||
|
}
|
||||||
const batch = users.slice(i, i + NEXTCLOUD_BATCH_SIZE);
|
const batch = users.slice(i, i + NEXTCLOUD_BATCH_SIZE);
|
||||||
await Promise.allSettled(batch.map((user) => processNextcloudUser(user)));
|
await Promise.allSettled(batch.map((user) => processNextcloudUser(user)));
|
||||||
}
|
}
|
||||||
@@ -318,9 +323,11 @@ export function startNotificationJob(): void {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run both once on startup, then repeat on separate intervals.
|
// Run main job once on startup, then repeat.
|
||||||
runNotificationGeneration();
|
runNotificationGeneration();
|
||||||
runNextcloudNotificationGeneration();
|
|
||||||
|
// Delay initial Nextcloud run to avoid a burst on container restart.
|
||||||
|
setTimeout(() => runNextcloudNotificationGeneration(), STARTUP_DELAY_MS);
|
||||||
|
|
||||||
jobInterval = setInterval(() => {
|
jobInterval = setInterval(() => {
|
||||||
runNotificationGeneration();
|
runNotificationGeneration();
|
||||||
|
|||||||
@@ -86,8 +86,15 @@ function buildHeaders(): Record<string, string> {
|
|||||||
* Fetches all BookStack books and returns a map of book_id → book_slug.
|
* Fetches all BookStack books and returns a map of book_id → book_slug.
|
||||||
* The /api/pages list endpoint does not reliably include book_slug, so we
|
* The /api/pages list endpoint does not reliably include book_slug, so we
|
||||||
* look it up separately and use it when constructing page URLs.
|
* look it up separately and use it when constructing page URLs.
|
||||||
|
* Cached for 5 minutes to avoid hammering the API on every dashboard load.
|
||||||
*/
|
*/
|
||||||
|
let bookSlugMapCache: { map: Map<number, string>; expiresAt: number } | null = null;
|
||||||
|
const BOOK_SLUG_CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes
|
||||||
|
|
||||||
async function getBookSlugMap(): Promise<Map<number, string>> {
|
async function getBookSlugMap(): Promise<Map<number, string>> {
|
||||||
|
if (bookSlugMapCache && Date.now() < bookSlugMapCache.expiresAt) {
|
||||||
|
return bookSlugMapCache.map;
|
||||||
|
}
|
||||||
const { bookstack } = environment;
|
const { bookstack } = environment;
|
||||||
try {
|
try {
|
||||||
const response = await httpClient.get(
|
const response = await httpClient.get(
|
||||||
@@ -95,9 +102,11 @@ async function getBookSlugMap(): Promise<Map<number, string>> {
|
|||||||
{ params: { count: 500 }, headers: buildHeaders() },
|
{ params: { count: 500 }, headers: buildHeaders() },
|
||||||
);
|
);
|
||||||
const books: Array<{ id: number; slug: string }> = response.data?.data ?? [];
|
const books: Array<{ id: number; slug: string }> = response.data?.data ?? [];
|
||||||
return new Map(books.map((b) => [b.id, b.slug]));
|
const map = new Map(books.map((b) => [b.id, b.slug]));
|
||||||
|
bookSlugMapCache = { map, expiresAt: Date.now() + BOOK_SLUG_CACHE_TTL_MS };
|
||||||
|
return map;
|
||||||
} catch {
|
} catch {
|
||||||
return new Map();
|
return bookSlugMapCache?.map ?? new Map();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -435,7 +435,7 @@ function MitgliedDetail() {
|
|||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Edit controls */}
|
{/* Edit controls */}
|
||||||
{canEdit && (
|
{canEdit && (canWrite || !!profile) && (
|
||||||
<Box>
|
<Box>
|
||||||
{editMode ? (
|
{editMode ? (
|
||||||
<Box sx={{ display: 'flex', gap: 1 }}>
|
<Box sx={{ display: 'flex', gap: 1 }}>
|
||||||
@@ -475,7 +475,9 @@ function MitgliedDetail() {
|
|||||||
{!profile && (
|
{!profile && (
|
||||||
<Alert severity="info" sx={{ mt: 2 }}>
|
<Alert severity="info" sx={{ mt: 2 }}>
|
||||||
Für dieses Mitglied wurde noch kein Profil angelegt.
|
Für dieses Mitglied wurde noch kein Profil angelegt.
|
||||||
{canWrite && ' Ein Kommandant kann das Profil unter "Stammdaten" erstellen.'}
|
{canWrite
|
||||||
|
? ' Ein Kommandant kann das Profil unter "Stammdaten" erstellen.'
|
||||||
|
: ' Wende dich an einen Administrator, um dein Profil anlegen zu lassen.'}
|
||||||
</Alert>
|
</Alert>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user