Upgrade OneNote sync with section/page targeting and edit mode
- Add GET /api/onenote/section-pages endpoint for listing pages in a section - Extend /api/onenote/append with syncMode add/edit support - Support editing an existing note block by noteId on a selected page - Keep add mode for appending to selected page or creating new page in section - Add Note + Sync UI controls for sync mode, section/page target, existing noteId, and optional new page title - Wire frontend flow to load sync targets and pages, then sync with selected mode
This commit is contained in:
@@ -173,15 +173,43 @@ app.get('/api/onenote/site-sections', async (c) => {
|
||||
}
|
||||
});
|
||||
|
||||
// GET /api/onenote/section-pages?sectionId=...
|
||||
// Requires: Authorization: Bearer <delegated token>
|
||||
app.get('/api/onenote/section-pages', async (c) => {
|
||||
try {
|
||||
const delegatedToken = getBearerToken(c.req.header('authorization'));
|
||||
if (!delegatedToken) return c.json({ success: false, message: 'Missing delegated token' }, 401);
|
||||
|
||||
const sectionId = String(c.req.query('sectionId') || '').trim();
|
||||
if (!sectionId) return c.json({ success: false, message: 'Missing sectionId' }, 400);
|
||||
|
||||
const siteId = await resolveSiteId(delegatedToken);
|
||||
const resp = await fetch(
|
||||
`https://graph.microsoft.com/v1.0/sites/${siteId}/onenote/sections/${encodeURIComponent(sectionId)}/pages?$top=100&$select=id,title,createdDateTime,lastModifiedDateTime`,
|
||||
{ headers: { Authorization: `Bearer ${delegatedToken}` } },
|
||||
);
|
||||
|
||||
const data = await resp.json().catch(() => ({}));
|
||||
if (!resp.ok) return c.json({ success: false, siteId, sectionId, error: data }, resp.status as any);
|
||||
return c.json({ success: true, siteId, sectionId, pages: data?.value || [] });
|
||||
} catch (e) {
|
||||
return c.json({ success: false, message: (e as Error)?.message }, 500);
|
||||
}
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// POST /api/onenote/append
|
||||
// Requires: x-pb-token (PocketBase session) + Authorization: Bearer <delegated Graph token>
|
||||
// Body: { noteId, title, bodyPlain, noteType?, userEmail?, jobNumber?, sectionId? }
|
||||
// Body: {
|
||||
// noteId, title, bodyPlain, noteType?, userEmail?, jobNumber?,
|
||||
// sectionId?, pageId?, pageTitle?, syncMode?: 'add'|'edit', targetNoteId?
|
||||
// }
|
||||
//
|
||||
// Strategy:
|
||||
// 1. Resolve site ID via GET /sites/{host}:{path}
|
||||
// 2. Try PATCH sites/{siteId}/onenote/pages/{pageId}/content → append to known page
|
||||
// 3. If that fails, POST sites/{siteId}/onenote/sections/{sectionId}/pages → create new page
|
||||
// 2. If syncMode=edit: PATCH replace note block in known page by noteId
|
||||
// 3. If syncMode=add and pageId: PATCH append to known page
|
||||
// 4. If syncMode=add and no pageId: POST create new page in known section
|
||||
// ---------------------------------------------------------------------------
|
||||
app.post('/api/onenote/append', async (c) => {
|
||||
try {
|
||||
@@ -206,6 +234,9 @@ app.post('/api/onenote/append', async (c) => {
|
||||
const noteType = String(body?.noteType || 'personal').trim();
|
||||
const userEmail = String(body?.userEmail || 'unknown').trim();
|
||||
const jobNumber = String(body?.jobNumber || '').trim();
|
||||
const pageTitle = String(body?.pageTitle || title || ONENOTE_TARGET.pageTitle || 'Prism Notes').trim();
|
||||
const syncMode = String(body?.syncMode || 'add').trim().toLowerCase() === 'edit' ? 'edit' : 'add';
|
||||
const targetNoteId = String(body?.targetNoteId || noteId || '').trim();
|
||||
|
||||
if (!noteId) return c.json({ success: false, message: 'Missing noteId' }, 400);
|
||||
if (!bodyPlain) return c.json({ success: false, message: 'Missing bodyPlain' }, 400);
|
||||
@@ -225,7 +256,7 @@ app.post('/api/onenote/append', async (c) => {
|
||||
const pageHtml = [
|
||||
'<!DOCTYPE html>',
|
||||
'<html>',
|
||||
`<head><title>${escapeHtml(title)}</title></head>`,
|
||||
`<head><title>${escapeHtml(pageTitle)}</title></head>`,
|
||||
`<body>${appendHtml}</body>`,
|
||||
'</html>',
|
||||
].join('');
|
||||
@@ -233,6 +264,12 @@ app.post('/api/onenote/append', async (c) => {
|
||||
const sectionId = String(body?.sectionId || ONENOTE_TARGET.sectionId || '').trim();
|
||||
const pageId = String(body?.pageId || ONENOTE_TARGET.pageId || '').trim();
|
||||
if (!sectionId) return c.json({ success: false, message: 'No sectionId — run GET /api/onenote/site-sections first to retrieve valid IDs' }, 400);
|
||||
if (syncMode === 'edit' && !pageId) {
|
||||
return c.json({ success: false, message: 'Edit mode requires pageId' }, 400);
|
||||
}
|
||||
if (syncMode === 'edit' && !targetNoteId) {
|
||||
return c.json({ success: false, message: 'Edit mode requires targetNoteId or noteId' }, 400);
|
||||
}
|
||||
|
||||
const authHeader = { Authorization: `Bearer ${delegatedToken}` };
|
||||
|
||||
@@ -240,6 +277,40 @@ app.post('/api/onenote/append', async (c) => {
|
||||
const siteId = await resolveSiteId(delegatedToken);
|
||||
const base = `https://graph.microsoft.com/v1.0/sites/${siteId}/onenote`;
|
||||
|
||||
if (syncMode === 'edit') {
|
||||
const selectorNoteId = targetNoteId.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
||||
const replaceResp = await fetch(
|
||||
`${base}/pages/${pageId}/content`,
|
||||
{
|
||||
method: 'PATCH',
|
||||
headers: { ...authHeader, 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify([
|
||||
{
|
||||
target: `div[data-prism-notes-note-id=\"${selectorNoteId}\"]`,
|
||||
action: 'replace',
|
||||
content: appendHtml,
|
||||
},
|
||||
]),
|
||||
},
|
||||
);
|
||||
|
||||
if (replaceResp.ok) {
|
||||
console.log('onenote_edit_success', { mode: 'edit', noteId, targetNoteId, pageId, siteId });
|
||||
return c.json({ success: true, mode: 'edited', pageId, noteId: targetNoteId, syncedAt: ts });
|
||||
}
|
||||
|
||||
const replaceErr = await replaceResp.text();
|
||||
console.warn('onenote_edit_failed', { status: replaceResp.status, error: replaceErr, pageId, targetNoteId });
|
||||
return c.json(
|
||||
{
|
||||
success: false,
|
||||
message: `OneNote edit failed (${replaceResp.status})`,
|
||||
details: { edit: replaceErr, pageId, targetNoteId, siteId },
|
||||
},
|
||||
502,
|
||||
);
|
||||
}
|
||||
|
||||
/* ---- 1: try append to known page (if we have a valid pageId) ---- */
|
||||
if (pageId) {
|
||||
const patchResp = await fetch(
|
||||
|
||||
Reference in New Issue
Block a user