Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updated route.ts #459

Closed
wants to merge 2 commits into from
Closed
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
314 changes: 139 additions & 175 deletions src/app/api/content/[...slug]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,223 +21,187 @@ type RouteProps = {
};
};

export function GET(req: Request, { params: { slug } }: RouteProps) {
export async function GET(req: Request, { params: { slug } }: RouteProps) {
// dummy check on the url params
if (!slug || !Array.isArray(slug) || slug.length <= 0) {
return notFound();
}

let { group, locale, href, appendix } = computeDetailsFromSlug(slug);
try {
let { group, locale, href, appendix } = computeDetailsFromSlug(slug);

if (!group) return notFound();
if (!group) return notFound();

// `lessons` are nested under `courses`
if (group == "courses" && appendix.split("/").length == 2) {
group = "lessons";
}

// get the base locale's record to serve as the default content
const baseLocalRecords = getRecordsForGroup(group, {
locale: DEFAULT_LOCALE_EN,
}) as SupportedDocTypes[];

// create a flat listing of all the nav items in order to locate the next, current, and prev records
let flatNavItems = generateFlatNavItemListing(
generateNavItemListing(baseLocalRecords),
);

if (!flatNavItems || flatNavItems.length <= 0) return notFound();

// initialize the NavItem record trackers
let current: NavItem | null = null;
let next: NavItem | null = null;
let prev: NavItem | null = null;

for (let i = 0; i < flatNavItems.length; i++) {
// skip incorrect routes
if (
flatNavItems[i].href != href &&
flatNavItems[i].href != `/${href}` &&
!flatNavItems[i]?.altRoutes?.filter(
route => route.toLocaleLowerCase() == href,
).length
) {
continue;
// `lessons` are nested under `courses`
if (group === "courses" && appendix.split("/").length === 2) {
group = "lessons";
}

// set the current requested record
current = flatNavItems[i];
// get the base locale's record to serve as the default content
const baseLocalRecords = getRecordsForGroup(group, {
locale: DEFAULT_LOCALE_EN,
}) as SupportedDocTypes[];

// get the "previous" record link to display (that is an actual link)
if (i > 0) {
for (let j = i - 1; j >= 0; j--) {
if (Boolean(flatNavItems[j]?.href) && !flatNavItems[j].isSkippedInNav) {
prev = flatNavItems[j];
break;
}
}
}
// get the "next" record link to display (that is an actual link)
if (flatNavItems.length >= i + 1) {
for (let j = i + 1; j < flatNavItems.length; j++) {
if (Boolean(flatNavItems[j]?.href) && !flatNavItems[j].isSkippedInNav) {
next = flatNavItems[j];
break;
}
// create a flat listing of all the nav items to locate the next, current, and prev records
let flatNavItems = generateFlatNavItemListing(
generateNavItemListing(baseLocalRecords),
);

if (!flatNavItems || flatNavItems.length <= 0) return notFound();

// initialize the NavItem record trackers
let current: NavItem | null = null;
let next: NavItem | null = null;
let prev: NavItem | null = null;

// Find the current item based on href
for (let i = 0; i < flatNavItems.length; i++) {
const item = flatNavItems[i];
const matchesHref =
[item.href, `/${item.href}`].includes(href.toLowerCase()) ||
item?.altRoutes?.some(
route => route.toLowerCase() === href.toLowerCase(),
);

if (!matchesHref) continue;

current = item;

// get the "previous" record link to display (that is an actual link)
if (i > 0) {
prev =
flatNavItems
.slice(0, i)
.reverse()
.find(item => item?.href && !item.isSkippedInNav) || null;
}
}

// break out of the loop and stop processing
break;
}
// get the "next" record link to display (that is an actual link)
if (i < flatNavItems.length - 1) {
next =
flatNavItems
.slice(i + 1)
.find(item => item?.href && !item.isSkippedInNav) || null;
}

if (!current) return notFound();
break; // Stop processing after finding the current record
}

// TODO: you wouldn't normally use undefined explicitly
// that's what null is for.
let course: CourseRecord | undefined = undefined;
if (!current) return notFound();

// handle the special cases for lessons
if (group == "lessons" && Boolean(current.slug)) {
try {
// Handle lessons nested under courses
let course: CourseRecord | null = null;
if (group === "lessons" && current.slug) {
const courseSlug = appendix.split("/")[0];

course = (
getRecordsForGroup("courses", {
locale: DEFAULT_LOCALE_EN,
}) as CourseRecord[]
).find(item => item.slug == courseSlug);

if (!course) throw `Course '${courseSlug}' not found`;
course =
(
getRecordsForGroup("courses", {
locale: DEFAULT_LOCALE_EN,
}) as CourseRecord[]
).find(item => item.slug === courseSlug) || null;
if (!course) throw new Error(`Course '${courseSlug}' not found`);

if (!course.lessons) course.lessons = [];

const lessonIndex = course.lessons.findIndex(
item => item == current!.slug,
item => item === current.slug,
);

next =
course.lessons.length > lessonIndex
lessonIndex >= 0 && course.lessons.length > lessonIndex + 1
? flatNavItems.find(item =>
item.path?.endsWith(course!.lessons![lessonIndex + 1]),
item.path?.endsWith(course.lessons![lessonIndex + 1]),
) || null
: null;

prev =
lessonIndex > 0
? flatNavItems.find(item =>
item.path?.endsWith(course!.lessons![lessonIndex - 1]),
item.path?.endsWith(course.lessons![lessonIndex - 1]),
) || null
: null;
} catch (err) {
console.warn(
"[api content]",
"an error occurred while getting the course details for a lesson",
);
console.warn(err);
return notFound();
}
}

// locate full content record for the base locale
let record = baseLocalRecords.find(
(item: SupportedDocTypes) =>
item.href.toLowerCase() == current?.href?.toLowerCase(),
);

if (!record) return notFound();

/**
* with the base locale record and data in hand, we can attempt to
* locate the desired locale's record data
*/
if (locale !== DEFAULT_LOCALE_EN) {
const localeRecords = getRecordsForGroup(group, {
locale,
}) as SupportedDocTypes[];

const localRecord = localeRecords.find(
(item: SupportedDocTypes) =>
item.href.toLowerCase() == current?.href?.toLowerCase(),
// Locate the base locale's record
let record = baseLocalRecords.find(
item => item.href.toLowerCase() === current?.href?.toLowerCase(),
);
if (localRecord) {
record = localRecord;
}

flatNavItems = generateFlatNavItemListing(
generateNavItemListing(localeRecords),
);

// get the locale specific next/prev info
if (!!next) next = flatNavItems.find(item => item.id == next!.id) || next;
if (!!prev) prev = flatNavItems.find(item => item.id == prev!.id) || prev;
}

if (!record) return notFound();

const breadcrumbs: BreadcrumbItem[] = [];
let parentId = current.id.substring(0, current.id.lastIndexOf("-"));
if (!record) return notFound();

// Attempt to find the desired locale's record
if (locale !== DEFAULT_LOCALE_EN) {
const localeRecords = getRecordsForGroup(group, {
locale,
}) as SupportedDocTypes[];
const localRecord = localeRecords.find(
item => item.href.toLowerCase() === current?.href?.toLowerCase(),
);

for (let i = 0; i <= parentId.split("-").length + 2; i++) {
const item = flatNavItems.find(item => item.id == parentId);
if (localRecord) record = localRecord;

if (item) {
breadcrumbs.unshift({
href: item.href,
label: item.label,
});
flatNavItems = generateFlatNavItemListing(
generateNavItemListing(localeRecords),
);
next = next
? flatNavItems.find(item => item.id === next!.id) || next
: null;
prev = prev
? flatNavItems.find(item => item.id === prev!.id) || prev
: null;
}

parentId = parentId.substring(0, parentId.lastIndexOf("-"));
}

// remove the html formatted content (since it is undesired data to send over the wire)
if (!!record?.body?.raw && typeof record.body.raw !== "undefined") {
// @ts-ignore - preprocess the body content and simplify the content returned
record.body = preProcessContent(record.body.raw.trim());
}

// remove the i18n prefixes
record._raw = {
...record._raw,
sourceFilePath: record._raw.sourceFilePath.replace(I18N_LOCALE_REGEX, ""),
sourceFileDir: record._raw.sourceFileDir.replace(I18N_LOCALE_REGEX, ""),
flattenedPath: record._raw.flattenedPath.replace(I18N_LOCALE_REGEX, ""),
};

// force put the course details into the lesson
if (group == "lessons") {
// @ts-expect-error
current.course = course;
}

let author: AuthorRecord | undefined = undefined;

// add the author details into the record
if (!!record?.author) {
const allAuthors = getRecordsForGroup("authors", {
locale: DEFAULT_LOCALE_EN,
}) as AuthorRecord[];
const breadcrumbs: BreadcrumbItem[] = [];
let parentId = current.id.substring(0, current.id.lastIndexOf("-"));
for (let i = 0; i <= parentId.split("-").length + 2; i++) {
const item = flatNavItems.find(item => item.id === parentId);
if (item) {
breadcrumbs.unshift({ href: item.href, label: item.label });
}
parentId = parentId.substring(0, parentId.lastIndexOf("-"));
}

// @ts-ignore
author = allAuthors.find(node => node.slug == record.author);
// @ts-expect-error
delete author._raw;
// Pre-process content if necessary
if (record?.body?.raw) {
record.body = preProcessContent(record.body.raw.trim());
}

if (!!author?.organization) {
// @ts-expect-error - we are forcing in the organization's record on purpose
author.organization = allAuthors.find(
node => node.slug == author!.organization,
);
// @ts-expect-error
delete author.organization._raw;
// Remove i18n prefixes
record._raw = {
...record._raw,
sourceFilePath: record._raw.sourceFilePath.replace(I18N_LOCALE_REGEX, ""),
sourceFileDir: record._raw.sourceFileDir.replace(I18N_LOCALE_REGEX, ""),
flattenedPath: record._raw.flattenedPath.replace(I18N_LOCALE_REGEX, ""),
};

// Force course details into the lesson
if (group === "lessons" && course) {
(current as any).course = course;
}
}

// todo: support sending related content records back to the client
// Get author information
let author: AuthorRecord | null = null;
if (record?.author) {
const allAuthors = getRecordsForGroup("authors", {
locale: DEFAULT_LOCALE_EN,
}) as AuthorRecord[];
author = allAuthors.find(node => node.slug === record.author) || null;
if (author && author.organization) {
author.organization =
allAuthors.find(node => node.slug === author.organization) || null;
}
}

// finally, return the json formatted listing of NavItems (with the next and prev records)
return Response.json(
Object.assign(current, record, { breadcrumbs, next, prev, author }),
);
// Return the json response
return Response.json({
...current,
...record,
breadcrumbs,
next,
prev,
author,
});
} catch (error) {
console.error("Error processing the request:", error);
return notFound();
}
}
Loading