// Change this to your Blazor/hosted app origin
const CORS_ORIGIN = "https://yourapp.pages.dev";
"Access-Control-Allow-Origin": CORS_ORIGIN,
"Access-Control-Allow-Methods": "GET, POST, OPTIONS",
"Access-Control-Allow-Headers": "Content-Type, Authorization",
function withCors(init = {}) {
// Helper like ExtractTextValue
function extractText(page, propertyName) {
const prop = page.properties?.[propertyName];
return (prop.title || []).map(t => t.plain_text || "").join("");
return (prop.rich_text || []).map(t => t.plain_text || "").join("");
return prop.number != null ? String(prop.number) : null;
return prop.email || null;
return prop.phone_number || null;
// Helper like ExtractMultiSelectValue
function extractMultiSelect(page, propertyName) {
const prop = page.properties?.[propertyName];
if (!prop || prop.type !== "multi_select" || !Array.isArray(prop.multi_select)) {
async fetch(request, env, ctx) {
const url = new URL(request.url);
if (request.method === "OPTIONS") {
return new Response(null, withCors());
// ----------------------------------------------------
// GET /api/company-info (unchanged from previous)
// ----------------------------------------------------
if (url.pathname === "/api/company-info" && request.method === "GET") {
const notionDbId = env.COMPANY_DB_ID;
const notionResponse = await fetch(
`https://api.notion.com/v1/databases/${notionDbId}/query`,
"Authorization": `Bearer ${env.NOTION_TOKEN}`,
"Notion-Version": "2022-06-28",
"Content-Type": "application/json",
if (!notionResponse.ok) {
const errorText = await notionResponse.text();
error: "Notion request failed",
status: notionResponse.status,
headers: { "Content-Type": "application/json" },
const data = await notionResponse.json();
if (!data.results || data.results.length === 0) {
return new Response("null", withCors({
headers: { "Content-Type": "application/json" },
const page = data.results[0];
CompanyName: extractText(page, "Company_Name"),
Email: extractText(page, "Email"),
Phone: extractText(page, "Phone"),
RegisteredAddress: extractText(page, "Address"),
UTR: extractText(page, "UTR"),
TON: extractText(page, "TON"),
CRN: extractText(page, "CRN"),
VAT: extractText(page, "VAT"),
JSON.stringify(companyInfo),
headers: { "Content-Type": "application/json" },
JSON.stringify({ error: "Worker error", details: String(err) }),
headers: { "Content-Type": "application/json" },
// ----------------------------------------------------
// GET /api/projects (unchanged from previous)
// ----------------------------------------------------
if (url.pathname === "/api/projects" && request.method === "GET") {
const notionDbId = env.PROJECT_DB_ID;
const notionResponse = await fetch(
`https://api.notion.com/v1/databases/${notionDbId}/query`,
"Authorization": `Bearer ${env.NOTION_TOKEN}`,
"Notion-Version": "2022-06-28",
"Content-Type": "application/json",
select: { does_not_equal: "Cancelled" },
select: { does_not_equal: "On Hold" },
select: { does_not_equal: "In Review" },
if (!notionResponse.ok) {
const errorText = await notionResponse.text();
error: "Notion request failed",
status: notionResponse.status,
headers: { "Content-Type": "application/json" },
const data = await notionResponse.json();
const results = (data.results || []).map(page => ({
Name: extractText(page, "Name"),
FullName: extractText(page, "Project Name"),
ProjectCode: extractText(page, "Project Code"),
ClientCodes: extractMultiSelect(page, "Client_Code"),
headers: { "Content-Type": "application/json" },
JSON.stringify({ error: "Worker error", details: String(err) }),
headers: { "Content-Type": "application/json" },
// ----------------------------------------------------
// NEW: GET /api/clients (equivalent to GetClientsAsync)
// ----------------------------------------------------
if (url.pathname === "/api/clients" && request.method === "GET") {
const notionDbId = env.CLIENTS_DB_ID;
const notionResponse = await fetch(
`https://api.notion.com/v1/databases/${notionDbId}/query`,
"Authorization": `Bearer ${env.NOTION_TOKEN}`,
"Notion-Version": "2022-06-28",
"Content-Type": "application/json",
select: { equals: "Client" },
checkbox: { equals: true },
if (!notionResponse.ok) {
const errorText = await notionResponse.text();
error: "Notion request failed",
status: notionResponse.status,
headers: { "Content-Type": "application/json" },
const data = await notionResponse.json();
const results = (data.results || []).map(page => ({
Name: extractText(page, "Name"),
FullName: extractText(page, "Name Tag"),
Address: extractText(page, "Address"),
TaxId: extractText(page, "Tax_ID"),
ClientCode: extractText(page, "Code"),
headers: { "Content-Type": "application/json" },
JSON.stringify({ error: "Worker error", details: String(err) }),
headers: { "Content-Type": "application/json" },
return new Response("Not found", withCors({ status: 404 }));