fucking shit working properly now. prod works.
TODO; Fix asset loading :3
This commit is contained in:
12
src/app/admin/MonacoEditor.tsx
Normal file
12
src/app/admin/MonacoEditor.tsx
Normal file
@@ -0,0 +1,12 @@
|
||||
import Editor from "@monaco-editor/react";
|
||||
|
||||
export default function MonacoEditorWrapper(props: any) {
|
||||
return (
|
||||
<Editor
|
||||
height="600px"
|
||||
defaultLanguage="markdown"
|
||||
defaultValue={props.defaultValue || ""}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -1,9 +1,15 @@
|
||||
'use client';
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
/*********************************************
|
||||
* This is the main admin page for the blog.
|
||||
*
|
||||
* Written Jun 19 2025
|
||||
* Rewritten fucking 15 times cause of the
|
||||
* fucking
|
||||
* typescript linter.
|
||||
*
|
||||
* If any Issues about "Window" (For Monaco) pop up. Its not my fucking fault
|
||||
**********************************************/
|
||||
|
||||
import { useState, useEffect, useCallback, useRef } from 'react';
|
||||
@@ -12,17 +18,21 @@ import Link from 'next/link';
|
||||
import { marked } from 'marked';
|
||||
import hljs from 'highlight.js';
|
||||
import matter from 'gray-matter';
|
||||
import dynamic from 'next/dynamic';
|
||||
import dynamicImport from 'next/dynamic';
|
||||
import { Theme } from 'emoji-picker-react';
|
||||
import '../highlight-github.css';
|
||||
import MonacoEditor from '@monaco-editor/react';
|
||||
import { initVimMode, VimMode } from 'monaco-vim';
|
||||
const MonacoEditor = dynamicImport(() => import('./MonacoEditor'), { ssr: false });
|
||||
// Import monaco-vim only on client side
|
||||
let initVimMode: any = null;
|
||||
let VimMode: any = null;
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
const monacoVim = require('monaco-vim');
|
||||
initVimMode = monacoVim.initVimMode;
|
||||
VimMode = monacoVim.VimMode;
|
||||
}
|
||||
import '@fontsource/jetbrains-mono';
|
||||
|
||||
// @ts-ignore-next-line
|
||||
// eslint-disable-next-line
|
||||
// If you want, you can move this to a global.d.ts file
|
||||
// declare module 'monaco-vim';
|
||||
|
||||
interface Post {
|
||||
slug: string;
|
||||
@@ -55,7 +65,7 @@ interface Post {
|
||||
|
||||
type Node = Post | Folder;
|
||||
|
||||
const EmojiPicker = dynamic(() => import('emoji-picker-react'), { ssr: false });
|
||||
const EmojiPicker = dynamicImport(() => import('emoji-picker-react'), { ssr: false });
|
||||
|
||||
// Patch marked renderer to always add 'hljs' class to code blocks
|
||||
const renderer = new marked.Renderer();
|
||||
@@ -89,12 +99,7 @@ export default function AdminPage() {
|
||||
});
|
||||
const [showManageContent, setShowManageContent] = useState(false);
|
||||
const [managePath, setManagePath] = useState<string[]>([]);
|
||||
const [pinned, setPinned] = useState<string[]>(() => {
|
||||
if (typeof window !== 'undefined') {
|
||||
return JSON.parse(localStorage.getItem('pinnedPosts') || '[]');
|
||||
}
|
||||
return [];
|
||||
});
|
||||
const [pinned, setPinned] = useState<string[]>([]);
|
||||
const [pinFeedback, setPinFeedback] = useState<string | null>(null);
|
||||
const [showChangePassword, setShowChangePassword] = useState(false);
|
||||
const [changePwOld, setChangePwOld] = useState('');
|
||||
@@ -104,12 +109,7 @@ export default function AdminPage() {
|
||||
const [previewHtml, setPreviewHtml] = useState('');
|
||||
const [editingPost, setEditingPost] = useState<{ slug: string, path: string } | null>(null);
|
||||
const [isDocker, setIsDocker] = useState<boolean>(false);
|
||||
const [rememberExportChoice, setRememberExportChoice] = useState<boolean>(() => {
|
||||
if (typeof window !== 'undefined') {
|
||||
return localStorage.getItem('rememberExportChoice') === 'true';
|
||||
}
|
||||
return false;
|
||||
});
|
||||
const [rememberExportChoice, setRememberExportChoice] = useState<boolean>(false);
|
||||
const [lastExportChoice, setLastExportChoice] = useState<string | null>(null);
|
||||
const [emojiPickerOpen, setEmojiPickerOpen] = useState<string | null>(null);
|
||||
const [emojiPickerAnchor, setEmojiPickerAnchor] = useState<HTMLElement | null>(null);
|
||||
@@ -536,15 +536,7 @@ export default function AdminPage() {
|
||||
};
|
||||
|
||||
function handleExportTarball() {
|
||||
// Check if we should use the remembered choice
|
||||
if (rememberExportChoice && lastExportChoice) {
|
||||
if (lastExportChoice === 'docker') {
|
||||
exportFromEndpoint('/api/admin/export');
|
||||
} else if (lastExportChoice === 'local') {
|
||||
exportFromEndpoint('/api/admin/exportlocal');
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (typeof window === 'undefined') return;
|
||||
|
||||
// Create popup modal
|
||||
const modal = document.createElement('div');
|
||||
@@ -637,6 +629,7 @@ export default function AdminPage() {
|
||||
}
|
||||
|
||||
function exportFromEndpoint(endpoint: string) {
|
||||
if (typeof window === 'undefined') return;
|
||||
fetch(endpoint)
|
||||
.then(async (res) => {
|
||||
if (!res.ok) throw new Error('Export failed');
|
||||
@@ -660,6 +653,15 @@ export default function AdminPage() {
|
||||
setLastExportChoice(null);
|
||||
};
|
||||
|
||||
// Hydrate pinned, rememberExportChoice, lastExportChoice from localStorage on client only
|
||||
useEffect(() => {
|
||||
if (typeof window !== 'undefined') {
|
||||
setPinned(JSON.parse(localStorage.getItem('pinnedPosts') || '[]'));
|
||||
setRememberExportChoice(localStorage.getItem('rememberExportChoice') === 'true');
|
||||
setLastExportChoice(localStorage.getItem('lastExportChoice'));
|
||||
}
|
||||
}, []);
|
||||
|
||||
// Simple and reliable emoji update handler
|
||||
const handleSetFolderEmoji = async (folderPath: string, emoji: string) => {
|
||||
try {
|
||||
@@ -772,7 +774,7 @@ export default function AdminPage() {
|
||||
|
||||
// Attach/detach Vim mode when vimMode changes
|
||||
useEffect(() => {
|
||||
if (vimMode && monacoRef.current) {
|
||||
if (vimMode && monacoRef.current && initVimMode) {
|
||||
// @ts-ignore
|
||||
vimInstanceRef.current = initVimMode(monacoRef.current, vimStatusRef.current);
|
||||
} else if (vimInstanceRef.current) {
|
||||
@@ -917,7 +919,7 @@ export default function AdminPage() {
|
||||
<path d="M75.8578 99.1263C73.4721 100.274 70.6219 99.7885 68.75 97.9166C71.0564 100.223 75 98.5895 75 95.3278V4.67213C75 1.41039 71.0564 -0.223106 68.75 2.08329C70.6219 0.211402 73.4721 -0.273666 75.8578 0.873633L96.4587 10.7807C98.6234 11.8217 100 14.0112 100 16.4132V83.5871C100 85.9891 98.6234 88.1786 96.4586 89.2196L75.8578 99.1263Z" fill="#1F9CF0"/>
|
||||
</g>
|
||||
<g style={{ mixBlendMode: 'overlay' }} opacity="0.25">
|
||||
<path fillRule="evenodd" clipRule="evenodd" d="M70.8511 99.3171C72.4261 99.9306 74.2221 99.8913 75.8117 99.1264L96.4 89.2197C98.5634 88.1787 99.9392 85.9892 99.9392 83.5871V16.4133C99.9392 14.0112 98.5635 11.8217 96.4001 10.7807L75.8117 0.873695C73.7255 -0.13019 71.2838 0.115699 69.4527 1.44688C69.1912 1.63705 68.942 1.84937 68.7082 2.08335L29.2943 38.0414L12.1264 25.0096C10.5283 23.7964 8.29285 23.8959 6.80855 25.246L1.30225 30.2548C-0.513334 31.9064 -0.515415 34.7627 1.29775 36.4169L16.1863 50L1.29775 63.5832C-0.515415 65.2374 -0.513334 68.0937 1.30225 69.7452L6.80855 74.754C8.29285 76.1042 10.5283 76.2036 12.1264 74.9905L29.2943 61.9586L68.7082 97.9167C69.3317 98.5405 70.0638 99.0104 70.8511 99.3171ZM74.9544 27.2989L45.0483 50L74.9544 72.7012V27.2989Z" fill="url(#paint0_linear)"/>
|
||||
<path fillRule="evenodd" clipRule="evenodd" d="M70.8511 99.3171C72.4261 99.9306 74.2221 99.8913 75.8117 99.1264L96.4 89.2197C98.5634 88.1787 99.9392 85.9892 99.9392 83.5871V16.4133C99.9392 14.0112 98.5635 11.8217 96.4001 10.7807L75.8117 0.873695C73.7255 -0.13019 71.2838 0.115699 69.4527 1.44688C69.1912 1.63705 68.942 1.84937 68.7082 2.08335L29.2943 38.0414L12.1264 25.0096C10.5283 23.7964 8.29285 23.8959 6.80855 25.246L1.30225 30.2548C-0.513334 31.9064 -0.515415 34.7627 1.29775 36.4169L16.1863 50L1.29775 63.5832C-0.515415 65.2374 -0.513334 68.0937 1.29775 69.7452L6.80855 74.754C8.29285 76.1042 10.5283 76.2036 12.1264 74.9905L29.2943 61.9586L68.7082 97.9167C69.3317 98.5405 70.0638 99.0104 70.8511 99.3171ZM74.9544 27.2989L45.0483 50L74.9544 72.7012V27.2989Z" fill="url(#paint0_linear)"/>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
@@ -1182,7 +1184,7 @@ export default function AdminPage() {
|
||||
height="100%"
|
||||
defaultLanguage="markdown"
|
||||
value={newPost.content}
|
||||
onChange={(value) => setNewPost({ ...newPost, content: value || '' })}
|
||||
onChange={(value?: string) => setNewPost({ ...newPost, content: value || '' })}
|
||||
options={{
|
||||
minimap: { enabled: false },
|
||||
wordWrap: 'on',
|
||||
@@ -1193,7 +1195,7 @@ export default function AdminPage() {
|
||||
automaticLayout: true,
|
||||
fontFamily: 'JetBrains Mono, monospace',
|
||||
}}
|
||||
onMount={(editor) => {
|
||||
onMount={(editor: any) => {
|
||||
monacoRef.current = editor;
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -5,8 +5,7 @@ import fs from 'fs';
|
||||
import path from 'path';
|
||||
import matter from 'gray-matter';
|
||||
import { marked } from 'marked';
|
||||
import DOMPurify from 'dompurify';
|
||||
import { JSDOM } from 'jsdom';
|
||||
import createDOMPurify from 'isomorphic-dompurify';
|
||||
import hljs from 'highlight.js';
|
||||
import { getPostsDirectory } from '@/lib/postsDirectory';
|
||||
|
||||
@@ -106,10 +105,8 @@ async function getPostByPath(filePath: string, relPath: string, pinnedData: { pi
|
||||
|
||||
let processedContent = '';
|
||||
try {
|
||||
const rawHtml = marked.parse(content);
|
||||
const window = new JSDOM('').window;
|
||||
const purify = DOMPurify(window);
|
||||
processedContent = purify.sanitize(rawHtml as string, {
|
||||
const rawHtml = marked.parse(content) as string;
|
||||
processedContent = createDOMPurify.sanitize(rawHtml, {
|
||||
ALLOWED_TAGS: [
|
||||
'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
|
||||
'p', 'a', 'ul', 'ol', 'li', 'blockquote',
|
||||
@@ -123,7 +120,7 @@ async function getPostByPath(filePath: string, relPath: string, pinnedData: { pi
|
||||
'src', 'alt', 'title', 'width', 'height',
|
||||
'frameborder', 'allowfullscreen'
|
||||
],
|
||||
ALLOWED_URI_REGEXP: /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i
|
||||
ALLOWED_URI_REGEXP: /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp):|[^a-z]|[a-z+\.\-]+(?:[^a-z+\.\-:]|$))/i
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(`Error processing markdown for ${relPath}:`, err);
|
||||
|
||||
Reference in New Issue
Block a user