Spaces:
Sleeping
Sleeping
resolve missings chapter
Browse files- src/routes/personalize.ts +26 -25
src/routes/personalize.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
| 1 |
import { Hono } from 'hono';
|
| 2 |
import { eq } from 'drizzle-orm';
|
|
|
|
|
|
|
| 3 |
import { db } from '../db/client.js';
|
| 4 |
import { userProfiles } from '../db/schema.js';
|
| 5 |
import { requireAuth, getAuthContext } from '../middleware/auth.js';
|
|
@@ -90,40 +92,34 @@ function validateProfileCompleteness(profile: ProfileResponse | null): ProfileVa
|
|
| 90 |
// =============================================================================
|
| 91 |
|
| 92 |
/**
|
| 93 |
-
* Get the
|
| 94 |
-
* Uses the deployed Docusaurus site's raw markdown from GitHub
|
| 95 |
*/
|
| 96 |
-
function
|
| 97 |
-
//
|
| 98 |
-
return process.env.
|
| 99 |
-
'https://raw.githubusercontent.com/shuremali02/AI_Spec-Driven_Online_Hackathon_1/main/book-write/docs';
|
| 100 |
}
|
| 101 |
|
| 102 |
/**
|
| 103 |
-
* Fetch chapter content from
|
| 104 |
* Supports both `chapter-01.md` and `chapter-01/index.md` patterns
|
| 105 |
*/
|
| 106 |
async function fetchChapterContent(chapterId: string): Promise<string | null> {
|
| 107 |
-
const
|
| 108 |
|
| 109 |
// Sanitize chapter ID to prevent directory traversal
|
| 110 |
const sanitizedId = chapterId.replace(/\.\./g, '').replace(/^\/+/, '');
|
| 111 |
|
| 112 |
// Try different file patterns
|
| 113 |
const patterns = [
|
| 114 |
-
`${
|
| 115 |
-
`${
|
| 116 |
-
|
| 117 |
-
|
| 118 |
];
|
| 119 |
|
| 120 |
-
for (const
|
| 121 |
try {
|
| 122 |
-
const
|
| 123 |
-
if (!response.ok) {
|
| 124 |
-
continue;
|
| 125 |
-
}
|
| 126 |
-
const content = await response.text();
|
| 127 |
// Strip frontmatter
|
| 128 |
const frontmatterMatch = content.match(/^---\n[\s\S]*?\n---\n/);
|
| 129 |
if (frontmatterMatch) {
|
|
@@ -131,7 +127,7 @@ async function fetchChapterContent(chapterId: string): Promise<string | null> {
|
|
| 131 |
}
|
| 132 |
return content;
|
| 133 |
} catch {
|
| 134 |
-
//
|
| 135 |
continue;
|
| 136 |
}
|
| 137 |
}
|
|
@@ -378,13 +374,18 @@ personalize.post('/personalize', async (c) => {
|
|
| 378 |
}
|
| 379 |
|
| 380 |
// Fetch chapter content (TASK-005)
|
| 381 |
-
|
|
|
|
| 382 |
if (!chapterContent) {
|
| 383 |
-
|
| 384 |
-
|
| 385 |
-
|
| 386 |
-
|
| 387 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 388 |
}
|
| 389 |
|
| 390 |
// Log personalization request
|
|
|
|
| 1 |
import { Hono } from 'hono';
|
| 2 |
import { eq } from 'drizzle-orm';
|
| 3 |
+
import { readFile } from 'fs/promises';
|
| 4 |
+
import { join, resolve } from 'path';
|
| 5 |
import { db } from '../db/client.js';
|
| 6 |
import { userProfiles } from '../db/schema.js';
|
| 7 |
import { requireAuth, getAuthContext } from '../middleware/auth.js';
|
|
|
|
| 92 |
// =============================================================================
|
| 93 |
|
| 94 |
/**
|
| 95 |
+
* Get the path to chapter content directory
|
|
|
|
| 96 |
*/
|
| 97 |
+
function getChapterContentPath(): string {
|
| 98 |
+
// Use environment variable or default to relative path from auth service
|
| 99 |
+
return process.env.CHAPTER_CONTENT_PATH || resolve(process.cwd(), '..', 'book-write', 'docs');
|
|
|
|
| 100 |
}
|
| 101 |
|
| 102 |
/**
|
| 103 |
+
* Fetch chapter content from filesystem
|
| 104 |
* Supports both `chapter-01.md` and `chapter-01/index.md` patterns
|
| 105 |
*/
|
| 106 |
async function fetchChapterContent(chapterId: string): Promise<string | null> {
|
| 107 |
+
const basePath = getChapterContentPath();
|
| 108 |
|
| 109 |
// Sanitize chapter ID to prevent directory traversal
|
| 110 |
const sanitizedId = chapterId.replace(/\.\./g, '').replace(/^\/+/, '');
|
| 111 |
|
| 112 |
// Try different file patterns
|
| 113 |
const patterns = [
|
| 114 |
+
join(basePath, `${sanitizedId}.md`),
|
| 115 |
+
join(basePath, `${sanitizedId}.mdx`),
|
| 116 |
+
join(basePath, sanitizedId, 'index.md'),
|
| 117 |
+
join(basePath, sanitizedId, 'index.mdx'),
|
| 118 |
];
|
| 119 |
|
| 120 |
+
for (const filePath of patterns) {
|
| 121 |
try {
|
| 122 |
+
const content = await readFile(filePath, 'utf-8');
|
|
|
|
|
|
|
|
|
|
|
|
|
| 123 |
// Strip frontmatter
|
| 124 |
const frontmatterMatch = content.match(/^---\n[\s\S]*?\n---\n/);
|
| 125 |
if (frontmatterMatch) {
|
|
|
|
| 127 |
}
|
| 128 |
return content;
|
| 129 |
} catch {
|
| 130 |
+
// File doesn't exist, try next pattern
|
| 131 |
continue;
|
| 132 |
}
|
| 133 |
}
|
|
|
|
| 374 |
}
|
| 375 |
|
| 376 |
// Fetch chapter content (TASK-005)
|
| 377 |
+
// For deployment compatibility, allow frontend to provide content directly
|
| 378 |
+
let chapterContent = body.chapter_content;
|
| 379 |
if (!chapterContent) {
|
| 380 |
+
// Fallback to filesystem fetch if content not provided
|
| 381 |
+
chapterContent = await fetchChapterContent(body.chapter_id);
|
| 382 |
+
if (!chapterContent) {
|
| 383 |
+
return c.json({
|
| 384 |
+
success: false,
|
| 385 |
+
error: 'CHAPTER_NOT_FOUND',
|
| 386 |
+
message: 'Chapter not found and no content provided',
|
| 387 |
+
}, 404);
|
| 388 |
+
}
|
| 389 |
}
|
| 390 |
|
| 391 |
// Log personalization request
|