v1.0.6: fetch timeouts and project pagination

This commit is contained in:
pdmarf
2026-04-10 20:53:19 +01:00
parent d6282d7396
commit 7fe0841024
5 changed files with 100 additions and 63 deletions

View File

@@ -1,4 +1,5 @@
const NOTION_BASE = "https://api.notion.com/v1";
const TIMEOUT_MS = 10_000;
export interface NotionProject {
id: string;
@@ -13,17 +14,17 @@ function headers(token: string) {
};
}
function fetchWithTimeout(url: string, options: RequestInit): Promise<Response> {
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), TIMEOUT_MS);
return fetch(url, { ...options, signal: controller.signal }).finally(() => clearTimeout(timer));
}
const BOOK_COLOR: Record<string, string> = {
green: "📗", blue: "📘", orange: "📙", red: "📕",
yellow: "📒", pink: "📔", purple: "📓",
};
const COLOR_CIRCLE: Record<string, string> = {
green: "🟢", blue: "🔵", red: "🔴", orange: "🟠",
yellow: "🟡", purple: "🟣", pink: "🩷", brown: "🟤",
gray: "⚫", lightgray: "⬜", default: "⬜",
};
const ICON_NAME: Record<string, string> = {
graduate: "🎓", science: "🔬", gears: "⚙️", comment: "💬",
building: "🏢", chart: "📊", code: "💻", document: "📄",
@@ -51,7 +52,7 @@ export interface NotionUser {
}
export async function fetchUsers(token: string): Promise<NotionUser[]> {
const resp = await fetch(`${NOTION_BASE}/users`, {
const resp = await fetchWithTimeout(`${NOTION_BASE}/users`, {
headers: headers(token),
});
if (!resp.ok) throw new Error(`Failed to fetch users: ${resp.status}`);
@@ -63,27 +64,37 @@ export async function fetchUsers(token: string): Promise<NotionUser[]> {
}
export async function fetchProjects(token: string, dbId: string): Promise<NotionProject[]> {
const resp = await fetch(`${NOTION_BASE}/databases/${dbId}/query`, {
method: "POST",
headers: headers(token),
body: JSON.stringify({
const results: NotionProject[] = [];
let cursor: string | undefined;
do {
const body: Record<string, unknown> = {
page_size: 100,
sorts: [{ property: "Project name", direction: "ascending" }],
}),
});
if (!resp.ok) throw new Error(`Notion error ${resp.status}`);
const data = (await resp.json()) as { results: unknown[] };
return data.results.map((page: any) => {
const titleArr: any[] = page.properties?.["Project name"]?.title ?? [];
const title = titleArr.map((t: any) => t.plain_text).join("").trim() || "Untitled";
const emoji = notionIconToEmoji(page);
return {
id: page.id as string,
name: emoji ? `${emoji} ${title}` : title,
};
});
if (cursor) body.start_cursor = cursor;
const resp = await fetchWithTimeout(`${NOTION_BASE}/databases/${dbId}/query`, {
method: "POST",
headers: headers(token),
body: JSON.stringify(body),
});
if (!resp.ok) throw new Error(`Notion error ${resp.status}`);
const data = (await resp.json()) as { results: unknown[]; has_more: boolean; next_cursor: string | null };
for (const page of data.results as any[]) {
const titleArr: any[] = page.properties?.["Project name"]?.title ?? [];
const title = titleArr.map((t: any) => t.plain_text).join("").trim() || "Untitled";
const emoji = notionIconToEmoji(page);
results.push({ id: page.id as string, name: emoji ? `${emoji} ${title}` : title });
}
cursor = data.has_more && data.next_cursor ? data.next_cursor : undefined;
} while (cursor);
return results;
}
export async function startTimer(
@@ -94,9 +105,8 @@ export async function startTimer(
userId: string
): Promise<string> {
const now = new Date().toISOString();
const date = new Date().toLocaleDateString("en-GB");
const resp = await fetch(`${NOTION_BASE}/pages`, {
const resp = await fetchWithTimeout(`${NOTION_BASE}/pages`, {
method: "POST",
headers: headers(token),
body: JSON.stringify({
@@ -121,7 +131,7 @@ export async function startTimer(
export async function stopTimer(token: string, entryId: string): Promise<void> {
const now = new Date().toISOString();
const resp = await fetch(`${NOTION_BASE}/pages/${entryId}`, {
const resp = await fetchWithTimeout(`${NOTION_BASE}/pages/${entryId}`, {
method: "PATCH",
headers: headers(token),
body: JSON.stringify({