Wiki Phase 1: categories, drafts/publish, HTML sanitization
Foundation & safety phase of the wiki upgrade (see WIKI_UPGRADE.md). Schema (additive, idempotent via ensureSchema): - new wiki_categories table; wiki_pages gains category_id, excerpt, published, published_at, sort_order, and a FULLTEXT index - migration ALTERs guarded with IF NOT EXISTS for existing databases - seed reworked into 4 sections with the 8 starter pages assigned Security: - new utils/sanitizeHtml.js (sanitize-html allowlist); wiki bodies are sanitized on every save, and the article renders through DOMPurify - strips <script>, event handlers (onerror), and javascript: URLs Backend: - public: published-only list with ?category filter + /wiki/categories - admin: extended page CRUD, PATCH publish toggle, category CRUD; drafts visible to admin, hidden from public - all writes logged to activity_log Frontend: - data-driven public wiki index (sections + real descriptions; removed hardcoded blurbs/Roman numerals) with ?category filtering - article: category breadcrumb + sanitized render - admin: Section/Status columns, draft/publish + section + excerpt in the editor, and a Manage sections modal Verified end-to-end against MariaDB 11: migration clean, XSS neutralized, drafts hidden, client builds, server boots. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -49,7 +49,8 @@ export const api = {
|
||||
status: () => req('/public/status'),
|
||||
posts: (category) => req(`/public/posts/${category}`),
|
||||
post: (category, idOrSlug) => req(`/public/posts/${category}/${idOrSlug}`),
|
||||
wiki: () => req('/public/wiki'),
|
||||
wiki: (category) => req(`/public/wiki${category ? `?category=${encodeURIComponent(category)}` : ''}`),
|
||||
wikiCategories: () => req('/public/wiki/categories'),
|
||||
wikiPage: (slug) => req(`/public/wiki/${slug}`),
|
||||
contact: (payload) => req('/public/contact', { method: 'POST', body: payload }),
|
||||
|
||||
@@ -69,11 +70,18 @@ export const api = {
|
||||
fd.append('image', file)
|
||||
return req('/admin/posts/upload', { method: 'POST', body: fd, raw: true })
|
||||
},
|
||||
listWiki: () => req('/admin/wiki'),
|
||||
listWiki: (params = '') => req(`/admin/wiki${params}`),
|
||||
getWiki: (slug) => req(`/admin/wiki/${slug}`),
|
||||
createWiki: (data) => req('/admin/wiki', { method: 'POST', body: data }),
|
||||
updateWiki: (slug, data) => req(`/admin/wiki/${slug}`, { method: 'PUT', body: data }),
|
||||
publishWiki: (slug, published) =>
|
||||
req(`/admin/wiki/${slug}/publish`, { method: 'PATCH', body: { published } }),
|
||||
deleteWiki: (slug) => req(`/admin/wiki/${slug}`, { method: 'DELETE' }),
|
||||
listWikiCategories: () => req('/admin/wiki/categories'),
|
||||
createWikiCategory: (data) => req('/admin/wiki/categories', { method: 'POST', body: data }),
|
||||
updateWikiCategory: (id, data) =>
|
||||
req(`/admin/wiki/categories/${id}`, { method: 'PUT', body: data }),
|
||||
deleteWikiCategory: (id) => req(`/admin/wiki/categories/${id}`, { method: 'DELETE' }),
|
||||
getSettings: () => req('/admin/settings'),
|
||||
updateSettings: (obj) => req('/admin/settings', { method: 'PUT', body: obj }),
|
||||
activity: (limit = 50) => req(`/admin/activity?limit=${limit}`),
|
||||
|
||||
Reference in New Issue
Block a user