Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions client/src/screens/conductor/support/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const SupportCenter = () => {
const july = new Date(now.getFullYear(), 6, 1);
const stdOffset = Math.max(
january.getTimezoneOffset(),
july.getTimezoneOffset()
july.getTimezoneOffset(),
);
const isDST = now.getTimezoneOffset() < stdOffset;

Expand Down Expand Up @@ -61,7 +61,9 @@ const SupportCenter = () => {
if (!disabled) openLink(link);
}}
className={`flex flex-col h-80 w-96 p-4 mx-auto my-4 lg:m-4 border rounded-xl shadow-md items-center cursor-pointer ${
disabled ? "opacity-70 !cursor-not-allowed" : "opacity-100 hover:shadow-xl"
disabled
? "opacity-70 !cursor-not-allowed"
: "opacity-100 hover:shadow-xl"
}`}
aria-disabled={disabled}
>
Expand All @@ -88,6 +90,12 @@ const SupportCenter = () => {
icon="text telephone"
link="/support/contact"
/>
<HomeItem
title="Connections"
text="Connect and converse with fellow instructors and OER authors (Verified Instructors only)."
icon="comments"
link="/support/connections"
/>
<HomeItem
title="Insight"
text="Search Insight for help with all of your LibreTexts apps &
Expand Down
77 changes: 63 additions & 14 deletions server/api/books.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2625,9 +2625,43 @@ export async function syncWithSearchIndex(
req: Request,
res: Response
) {
try {
// Return response immediately to avoid timeout
res.send({
err: false,
msg: "Commons Books search index sync initiated. This process will run in the background.",
});

// Run the actual sync in the background (don't await)
syncBooksInBackground().catch((e) => {
debugError("Background books sync error:", e);
});
} catch (e) {
debugError(e);
// Only send error if response hasn't been sent yet
if (!res.headersSent) {
return res.status(500).send({
err: true,
errMsg: conductorErrors.err6,
});
}
}
}

/**
* Syncs all books to the search index in batches to avoid memory issues
* and timeouts with large datasets. Runs in the background.
* INTERNAL USE ONLY.
*/
async function syncBooksInBackground() {
try {
debugServer("Initiating Commons Books search index synchronization...");
const searchService = await SearchService.create();

const batchSize = 500; // Process 500 books at a time
let skip = 0;
let hasMore = true;
let totalSynced = 0;

/**
* Book data for search index should be in format:
Expand All @@ -2637,7 +2671,7 @@ export async function syncWithSearchIndex(
* projectTags: string[] // array of tag titles associated with the Book's Project
* }
*/
const books = await Book.aggregate([
const aggregationPipeline = [
{
// Add project data to each book (if any)
$lookup: {
Expand Down Expand Up @@ -2700,21 +2734,36 @@ export async function syncWithSearchIndex(
project: 0
}
}
])
];

const syncResult = await searchService.addDocuments("books", books);
while (hasMore) {
const books = await Book.aggregate([
...aggregationPipeline,
{ $skip: skip },
{ $limit: batchSize },
]);

return res.send({
err: false,
msg: "Commons Books search index synchronization completed.",
details: syncResult,
});
} catch (err) {
debugError(err);
return res.status(500).send({
err: true,
errMsg: conductorErrors.err6,
});
if (books.length === 0) {
hasMore = false;
break;
}

await searchService.addDocuments("books", books);
totalSynced += books.length;
debugServer(`Synced batch of ${books.length} books (${totalSynced} total)...`);

skip += batchSize;

// If we got fewer results than batchSize, we're done
if (books.length < batchSize) {
hasMore = false;
}
}

debugServer(`Commons Books search index sync completed. Total synced: ${totalSynced}`);
} catch (e) {
debugError("Error in syncBooksInBackground:", e);
throw e;
}
}

Expand Down
76 changes: 62 additions & 14 deletions server/api/projects.js
Original file line number Diff line number Diff line change
Expand Up @@ -3254,11 +3254,45 @@ async function getTrafficAnalyticsData(req, res, func) {
}

async function syncWithSearchIndex(req, res) {
try {
// Return response immediately to avoid timeout
res.send({
err: false,
msg: "Projects search index sync initiated. This process will run in the background.",
});

// Run the actual sync in the background (don't await)
syncProjectsInBackground().catch((e) => {
debugError("Background projects sync error:", e);
});
} catch (e) {
debugError(e);
// Only send error if response hasn't been sent yet
if (!res.headersSent) {
return res.status(500).send({
err: true,
errMsg: conductorErrors.err6,
});
}
}
}

/**
* Syncs all projects to the search index in batches to avoid memory issues
* and timeouts with large datasets. Runs in the background.
* INTERNAL USE ONLY.
*/
async function syncProjectsInBackground() {
try {
debugServer("Initiating Projects search index sync...");
const searchService = await SearchService.create();

const batchSize = 500; // Process 500 projects at a time
let skip = 0;
let hasMore = true;
let totalSynced = 0;

const projects = await Project.aggregate([
const aggregationPipeline = [
{
$lookup: {
from: "users",
Expand Down Expand Up @@ -3402,22 +3436,36 @@ async function syncWithSearchIndex(req, res) {
instructorAssets: 1,
},
},
]);
];

const syncResult = await searchService.addDocuments("projects", projects);
debugServer("Projects search index sync completed.");
while (hasMore) {
const projects = await Project.aggregate([
...aggregationPipeline,
{ $skip: skip },
{ $limit: batchSize },
]);

return res.send({
err: false,
msg: "Projects search index sync completed.",
syncResult,
});
if (projects.length === 0) {
hasMore = false;
break;
}

await searchService.addDocuments("projects", projects);
totalSynced += projects.length;
debugServer(`Synced batch of ${projects.length} projects (${totalSynced} total)...`);

skip += batchSize;

// If we got fewer results than batchSize, we're done
if (projects.length < batchSize) {
hasMore = false;
}
}

debugServer(`Projects search index sync completed. Total synced: ${totalSynced}`);
} catch (e) {
debugError(e);
return res.status(500).send({
err: true,
errMsg: conductorErrors.err6,
});
debugError("Error in syncProjectsInBackground:", e);
throw e;
}
}

Expand Down
Loading