diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml index 9d03667..27feff2 100644 --- a/.gitea/workflows/deploy.yml +++ b/.gitea/workflows/deploy.yml @@ -12,16 +12,13 @@ jobs: - name: Checkout repository uses: actions/checkout@v3 - - name: Set up Node.js - uses: actions/setup-node@v3 - with: - node-version: '20' + - name: Install Docker + uses: docker/setup-buildx-action@v2 - - name: Install dependencies - run: npm install + - name: Build Docker image + run: docker build -t markdownblog . - - name: Build project - run: npm run build + - name: Push Docker image + run: docker push registry.gitea.com/user/markdownblog:latest - - name: Start application - run: npm start -- --port 1337 \ No newline at end of file + - name: Deploy to Gitea \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index bda7c05..895faeb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,6 +14,10 @@ VOLUME ["/app/docker"] EXPOSE 3000 +COPY entrypoint.sh /entrypoint.sh +RUN chmod +x /entrypoint.sh +ENTRYPOINT ["/entrypoint.sh"] + CMD ["npm", "start"] # Building Instructions diff --git a/config.ecosystem.js b/config.ecosystem.js deleted file mode 100644 index 3afcd55..0000000 --- a/config.ecosystem.js +++ /dev/null @@ -1,21 +0,0 @@ -module.exports = { - apps: [ - { - name: "markdownblog", - script: "node_modules/next/dist/bin/next", - args: "start", - instances: 1, // Use 1 unless you have a reverse proxy - autorestart: true, - watch: false, - max_memory_restart: "1G", - env: { - NODE_ENV: "development" - }, - env_production: { - NODE_ENV: "production", - PORT: 3000, - HOST: "0.0.0.0" - } - } - ] -}; diff --git a/docker.sh b/docker.sh new file mode 100755 index 0000000..4ab52b0 --- /dev/null +++ b/docker.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash + +set -e + +IMAGE_NAME="markdownblog" +CONTAINER_NAME="markdownblog" +VOLUME_NAME="markdownblog-posts" +PORT="8080" + +# Stop and remove any containers using the volume +VOLUME_CONTAINERS=$(docker ps -a --filter volume=$VOLUME_NAME -q) +if [ -n "$VOLUME_CONTAINERS" ]; then + echo "Stopping and removing containers using the volume..." + docker rm -f $VOLUME_CONTAINERS +fi + +# Remove the volume for a clean start (optional, comment out if you want to keep posts) +echo "Removing Docker volume (for a clean start)..." +docker volume rm $VOLUME_NAME 2>/dev/null || true + +echo "Building Docker image..." +docker build -t $IMAGE_NAME . + +echo "Running new container with persistent volume..." +docker run -d \ + --name $CONTAINER_NAME \ + -p $PORT:3000 \ + -v $VOLUME_NAME:/app/docker \ + $IMAGE_NAME + +# Copy built-in posts to the volume if it's empty (one-time init) +echo "Copying built-in posts to Docker volume if empty..." +docker exec $CONTAINER_NAME sh -c 'if [ -d /app/posts ] && [ -d /app/docker ] && [ "$(ls -A /app/docker)" = "" ]; then cp -r /app/posts/* /app/docker/; fi' + +echo "Deployment complete!" +echo "App should be available at http://localhost:$PORT" diff --git a/entrypoint.sh b/entrypoint.sh new file mode 100644 index 0000000..0c61c61 --- /dev/null +++ b/entrypoint.sh @@ -0,0 +1,6 @@ +#!/bin/sh +# Copy built-in posts to docker volume if the volume is empty +if [ -d /app/posts ] && [ -d /app/docker ] && [ "$(ls -A /app/docker)" = "" ]; then + cp -r /app/posts/* /app/docker/ +fi +exec "$@" diff --git a/package-lock.json b/package-lock.json index f293768..0806d80 100644 --- a/package-lock.json +++ b/package-lock.json @@ -36,6 +36,7 @@ "@types/dompurify": "^3.0.5", "@types/jsdom": "^21.1.6", "@types/marked": "^5.0.2", + "@types/tar": "^6.1.13", "concurrently": "^8.2.2", "eslint": "^8.57.0", "eslint-config-next": "14.1.0" @@ -1245,6 +1246,27 @@ "@types/node": "*" } }, + "node_modules/@types/tar": { + "version": "6.1.13", + "resolved": "https://registry.npmjs.org/@types/tar/-/tar-6.1.13.tgz", + "integrity": "sha512-IznnlmU5f4WcGTh2ltRu/Ijpmk8wiWXfF0VA4s+HPjHZgvFggk1YaIkbo5krX/zUCzWF8N/l4+W/LNxnvAJ8nw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "minipass": "^4.0.0" + } + }, + "node_modules/@types/tar/node_modules/minipass": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.8.tgz", + "integrity": "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=8" + } + }, "node_modules/@types/tough-cookie": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", diff --git a/package.json b/package.json index 964cb7c..4d3d066 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "@types/dompurify": "^3.0.5", "@types/jsdom": "^21.1.6", "@types/marked": "^5.0.2", + "@types/tar": "^6.1.13", "concurrently": "^8.2.2", "eslint": "^8.57.0", "eslint-config-next": "14.1.0" diff --git a/posts/mdtest.md b/posts/mdtest.md index 294b23c..945a490 100644 --- a/posts/mdtest.md +++ b/posts/mdtest.md @@ -1,156 +1,319 @@ --- -title: "Markdown Testing" -date: "2025-06-17" -tags: ["testing"] -summary: "Testing Markdown Features" ---- -# Markdown Feature Test - -## Table of Contents -- [Headings](#headings) -- [Text Formatting](#text-formatting) -- [Lists](#lists) -- [Links](#links) -- [Images](#images) -- [Code](#code) -- [Blockquotes](#blockquotes) -- [Horizontal Rule](#horizontal-rule) -- [Tables](#tables) -- [Task Lists](#task-lists) -- [HTML in Markdown](#html-in-markdown) - +title: Markdown Demo! +date: '2025-06-19' +tags: + - demo +summary: Demo of Markdown Parsing +author: Rattatwinko's --- -## Headings +# Markdown: Syntax -# H1 -## H2 -### H3 -#### H4 -##### H5 -###### H6 - ---- - -## Text Formatting - -- **Bold** -- *Italic* -- ***Bold and Italic*** -- ~~Strikethrough~~ -- Underline (HTML) -- ==Highlight (non-standard)== - ---- - -## Lists - -### Unordered -- Item 1 - - Subitem 1.1 - - Subitem 1.1.1 -- Item 2 - -### Ordered -1. First item -2. Second item - 1. Subitem - 2. Subitem -3. Third item - ---- - -## Links - -- [Inline Link](https://www.example.com) -- [Reference Link][example] -- - -[example]: https://www.example.com - ---- - -## Images - -![Alt text](https://via.placeholder.com/150 "Optional title") - -GIF (Image works too): -![Image](https://media3.giphy.com/media/v1.Y2lkPTc5MGI3NjExd3k1dmd0MW0yMW94MW5kanBiaHIwd2dxczdoZXZhNTdjaDF4cTJ1YyZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/ietk9bNd6xIsOdXJHo/giphy.gif) - ---- - -## Code - -### Inline Code - -Here is `inline code`. - -### Code Block (fenced) - -```Python - -def hello\_world(): -print("Hello, world!") - -``` - -### Code Blog (test) - -```Python -def hello(): - return true; -``` +* [Overview](#overview) + * [Philosophy](#philosophy) + * [Inline HTML](#html) + * [Automatic Escaping for Special Characters](#autoescape) +* [Block Elements](#block) + * [Paragraphs and Line Breaks](#p) + * [Headers](#header) + * [Blockquotes](#blockquote) + * [Lists](#list) + * [Code Blocks](#precode) + * [Horizontal Rules](#hr) +* [Span Elements](#span) + * [Links](#link) + * [Emphasis](#em) + * [Code](#code) + * [Images](#img) +* [Miscellaneous](#misc) + * [Backslash Escapes](#backslash) + * [Automatic Links](#autolink) -### Code Block (indented) -```Python - def indented_example(): - return True -``` ---- +**Note:** This document is itself written using Markdown; you +can [see the source for it by adding '.text' to the URL](/projects/markdown/syntax.text). -## Blockquotes +---- -> This is a blockquote. +## Overview + +### Philosophy + +Markdown is intended to be as easy-to-read and easy-to-write as is feasible. + +Readability, however, is emphasized above all else. A Markdown-formatted +document should be publishable as-is, as plain text, without looking +like it's been marked up with tags or formatting instructions. While +Markdown's syntax has been influenced by several existing text-to-HTML +filters -- including [Setext](http://docutils.sourceforge.net/mirror/setext.html), [atx](http://www.aaronsw.com/2002/atx/), [Textile](http://textism.com/tools/textile/), [reStructuredText](http://docutils.sourceforge.net/rst.html), +[Grutatext](http://www.triptico.com/software/grutatxt.html), and [EtText](http://ettext.taint.org/doc/) -- the single biggest source of +inspiration for Markdown's syntax is the format of plain text email. + +## Block Elements + +### Paragraphs and Line Breaks + +A paragraph is simply one or more consecutive lines of text, separated +by one or more blank lines. (A blank line is any line that looks like a +blank line -- a line containing nothing but spaces or tabs is considered +blank.) Normal paragraphs should not be indented with spaces or tabs. + +The implication of the "one or more consecutive lines of text" rule is +that Markdown supports "hard-wrapped" text paragraphs. This differs +significantly from most other text-to-HTML formatters (including Movable +Type's "Convert Line Breaks" option) which translate every line break +character in a paragraph into a `
` tag. + +When you *do* want to insert a `
` break tag using Markdown, you +end a line with two or more spaces, then type return. + +### Headers + +Markdown supports two styles of headers, [Setext] [1] and [atx] [2]. + +Optionally, you may "close" atx-style headers. This is purely +cosmetic -- you can use this if you think it looks better. The +closing hashes don't even need to match the number of hashes +used to open the header. (The number of opening hashes +determines the header level.) + + +### Blockquotes + +Markdown uses email-style `>` characters for blockquoting. If you're +familiar with quoting passages of text in an email message, then you +know how to create a blockquote in Markdown. It looks best if you hard +wrap the text and put a `>` before every line: + +> This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet, +> consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus. +> Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus. +> +> Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse +> id sem consectetuer libero luctus adipiscing. + +Markdown allows you to be lazy and only put the `>` before the first +line of a hard-wrapped paragraph: + +> This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet, +consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus. +Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus. + +> Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse +id sem consectetuer libero luctus adipiscing. + +Blockquotes can be nested (i.e. a blockquote-in-a-blockquote) by +adding additional levels of `>`: + +> This is the first level of quoting. > -> > Nested blockquote. +> > This is nested blockquote. +> +> Back to the first level. ---- +Blockquotes can contain other Markdown elements, including headers, lists, +and code blocks: -## Horizontal Rule +> ## This is a header. +> +> 1. This is the first list item. +> 2. This is the second list item. +> +> Here's some example code: +> +> return shell_exec("echo $input | $markdown_script"); ---- +Any decent text editor should make email-style quoting easy. For +example, with BBEdit, you can make a selection and choose Increase +Quote Level from the Text menu. -___ -*** +### Lists ---- +Markdown supports ordered (numbered) and unordered (bulleted) lists. -## Tables +Unordered lists use asterisks, pluses, and hyphens -- interchangably +-- as list markers: -| Syntax | Description | -|--------|-------------| -| Header | Title | -| Cell | Content | +* Red +* Green +* Blue ---- +is equivalent to: -## Task Lists ++ Red ++ Green ++ Blue -- [x] Task 1 -- [ ] Task 2 - - [x] Subtask - - [ ] Subtask +and: ---- +- Red +- Green +- Blue -## HTML in Markdown +Ordered lists use numbers followed by periods: -
-This is raw HTML in Markdown. -
+1. Bird +2. McHale +3. Parish ---- +It's important to note that the actual numbers you use to mark the +list have no effect on the HTML output Markdown produces. The HTML +Markdown produces from the above list is: -_End of Markdown Feature Test_ +If you instead wrote the list in Markdown like this: + +1. Bird +1. McHale +1. Parish + +or even: + +3. Bird +1. McHale +8. Parish + +you'd get the exact same HTML output. The point is, if you want to, +you can use ordinal numbers in your ordered Markdown lists, so that +the numbers in your source match the numbers in your published HTML. +But if you want to be lazy, you don't have to. + +To make lists look nice, you can wrap items with hanging indents: + +* Lorem ipsum dolor sit amet, consectetuer adipiscing elit. + Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi, + viverra nec, fringilla in, laoreet vitae, risus. +* Donec sit amet nisl. Aliquam semper ipsum sit amet velit. + Suspendisse id sem consectetuer libero luctus adipiscing. + +But if you want to be lazy, you don't have to: + +* Lorem ipsum dolor sit amet, consectetuer adipiscing elit. +Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi, +viverra nec, fringilla in, laoreet vitae, risus. +* Donec sit amet nisl. Aliquam semper ipsum sit amet velit. +Suspendisse id sem consectetuer libero luctus adipiscing. + +List items may consist of multiple paragraphs. Each subsequent +paragraph in a list item must be indented by either 4 spaces +or one tab: + +1. This is a list item with two paragraphs. Lorem ipsum dolor + sit amet, consectetuer adipiscing elit. Aliquam hendrerit + mi posuere lectus. + + Vestibulum enim wisi, viverra nec, fringilla in, laoreet + vitae, risus. Donec sit amet nisl. Aliquam semper ipsum + sit amet velit. + +2. Suspendisse id sem consectetuer libero luctus adipiscing. + +It looks nice if you indent every line of the subsequent +paragraphs, but here again, Markdown will allow you to be +lazy: + +* This is a list item with two paragraphs. + + This is the second paragraph in the list item. You're +only required to indent the first line. Lorem ipsum dolor +sit amet, consectetuer adipiscing elit. + +* Another item in the same list. + +To put a blockquote within a list item, the blockquote's `>` +delimiters need to be indented: + +* A list item with a blockquote: + + > This is a blockquote + > inside a list item. + +To put a code block within a list item, the code block needs +to be indented *twice* -- 8 spaces or two tabs: + +* A list item with a code block: + + + +### Code Blocks + +Pre-formatted code blocks are used for writing about programming or +markup source code. Rather than forming normal paragraphs, the lines +of a code block are interpreted literally. Markdown wraps a code block +in both `
` and `` tags.
+
+To produce a code block in Markdown, simply indent every line of the
+block by at least 4 spaces or 1 tab.
+
+This is a normal paragraph:
+
+    This is a code block.
+
+Here is an example of AppleScript:
+
+    tell application "Foo"
+        beep
+    end tell
+
+A code block continues until it reaches a line that is not indented
+(or the end of the article).
+
+Within a code block, ampersands (`&`) and angle brackets (`<` and `>`)
+are automatically converted into HTML entities. This makes it very
+easy to include example HTML source code using Markdown -- just paste
+it and indent it, and Markdown will handle the hassle of encoding the
+ampersands and angle brackets. For example, this:
+
+    
+
+Regular Markdown syntax is not processed within code blocks. E.g.,
+asterisks are just literal asterisks within a code block. This means
+it's also easy to use Markdown to write about Markdown's own syntax.
+
+```
+tell application "Foo"
+    beep
+end tell
+```
+
+## Span Elements
+
+### Links
+
+Markdown supports two style of links: *inline* and *reference*.
+
+In both styles, the link text is delimited by [square brackets].
+
+To create an inline link, use a set of regular parentheses immediately
+after the link text's closing square bracket. Inside the parentheses,
+put the URL where you want the link to point, along with an *optional*
+title for the link, surrounded in quotes. For example:
+
+This is [an example](http://example.com/) inline link.
+
+[This link](http://example.net/) has no title attribute.
+
+### Emphasis
+
+Markdown treats asterisks (`*`) and underscores (`_`) as indicators of
+emphasis. Text wrapped with one `*` or `_` will be wrapped with an
+HTML `` tag; double `*`'s or `_`'s will be wrapped with an HTML
+`` tag. E.g., this input:
+
+*single asterisks*
+
+_single underscores_
+
+**double asterisks**
+
+__double underscores__
+
+### Code
+
+To indicate a span of code, wrap it with backtick quotes (`` ` ``).
+Unlike a pre-formatted code block, a code span indicates code within a
+normal paragraph. For example:
+
+Use the `printf()` function.
diff --git a/posts/welcome.md b/posts/welcome.md
index 62dda9e..6c0f677 100644
--- a/posts/welcome.md
+++ b/posts/welcome.md
@@ -100,7 +100,13 @@ You can pin a post both in the UI and in the backend of the server.
 
 |                   Status                      |                       Task                        |
 |:---------------------------------------------:|:-------------------------------------------------:|
-|DONE|Code Editor in Admin Panel with saving!
+|DONE|Code Editor in Admin Panel with saving!|
+| SEMI | Exporting Tar of 'Posts/' Folder|
+
+### Exporting of Folder:
+
+This for now atleast , only works with Next.JS Production Server `npm install && npm run build && npm start` for reference.
+Docker Support for now is limited. I've gotten Persistence working. ( On Branch PM2 )
 
 ---
 
diff --git a/src/app/admin/page.tsx b/src/app/admin/page.tsx
index 37f59a0..527262e 100644
--- a/src/app/admin/page.tsx
+++ b/src/app/admin/page.tsx
@@ -78,6 +78,7 @@ export default function AdminPage() {
   const [changePwFeedback, setChangePwFeedback] = useState(null);
   const [previewHtml, setPreviewHtml] = useState('');
   const [editingPost, setEditingPost] = useState<{ slug: string, path: string } | null>(null);
+  const [isDocker, setIsDocker] = useState(false);
   const router = useRouter();
   const usernameRef = useRef(null);
   const passwordRef = useRef(null);
@@ -96,6 +97,7 @@ export default function AdminPage() {
   useEffect(() => {
     localStorage.setItem('pinnedPosts', JSON.stringify(pinned));
   }, [pinned]);
+
   useEffect(() => {
     marked.setOptions({
       gfm: true,
@@ -111,6 +113,14 @@ export default function AdminPage() {
     setPreviewHtml(marked.parse(newPost.content || '') as string);
   }, [newPost.content]);
 
+  useEffect(() => {
+    // Check if docker is used
+    fetch('/api/admin/docker')
+      .then(res => res.json())
+      .then(data => setIsDocker(!!data.docker))
+      .catch(() => setIsDocker(false));
+  }, []);
+
   const loadContent = async () => {
     try {
       const response = await fetch('/api/posts');
@@ -480,6 +490,25 @@ export default function AdminPage() {
     }
   };
 
+  function handleExportTarball() {
+    fetch('/api/admin/export')
+      .then(async (res) => {
+        if (!res.ok) throw new Error('Export failed');
+        const blob = await res.blob();
+        const url = window.URL.createObjectURL(blob);
+        const a = document.createElement('a');
+        a.href = url;
+        a.download = 'markdownblog-export.tar.gz';
+        document.body.appendChild(a);
+        a.click();
+        a.remove();
+        window.URL.revokeObjectURL(url);
+      })
+      .catch((err) => {
+        alert('Export failed: ' + err.message);
+      });
+  }
+
   return (
     
{pinFeedback && ( @@ -548,6 +577,19 @@ export default function AdminPage() { > Passwort ändern + {/* Docker warning above export button */} + {isDocker && ( +
+ Warning: Docker is in use. Exporting will export the entire /app root directory (including all files and folders in the container's root). +
+ )} +
diff --git a/src/app/api/admin/delete/route.ts b/src/app/api/admin/delete/route.ts index 9411ca3..8cfe4df 100644 --- a/src/app/api/admin/delete/route.ts +++ b/src/app/api/admin/delete/route.ts @@ -1,6 +1,7 @@ import { NextRequest, NextResponse } from 'next/server'; import fs from 'fs/promises'; import path from 'path'; +import { getPostsDirectory } from '@/lib/postsDirectory'; export async function POST(request: NextRequest) { try { @@ -16,7 +17,7 @@ export async function POST(request: NextRequest) { // Construct the full path to the item const basePath = process.cwd(); - const postsDir = path.join(basePath, 'posts'); + const postsDir = getPostsDirectory(); // Ensure the posts directory exists try { diff --git a/src/app/api/admin/docker/route.ts b/src/app/api/admin/docker/route.ts new file mode 100644 index 0000000..26a0427 --- /dev/null +++ b/src/app/api/admin/docker/route.ts @@ -0,0 +1,10 @@ +import { existsSync } from 'fs'; +import path from 'path'; +import { NextResponse } from 'next/server'; + +export async function GET() { + const rootDir = process.cwd(); + const dockerDir = path.join(rootDir, 'docker'); + const isDocker = existsSync(dockerDir); + return NextResponse.json({ docker: isDocker }); +} \ No newline at end of file diff --git a/src/app/api/admin/export/route.ts b/src/app/api/admin/export/route.ts new file mode 100644 index 0000000..d0be048 --- /dev/null +++ b/src/app/api/admin/export/route.ts @@ -0,0 +1,69 @@ +import tar from 'tar'; +import { NextResponse } from 'next/server'; +import { statSync, createReadStream, existsSync } from 'fs'; +import path from 'path'; + +export async function GET() { + try { + const rootDir = process.cwd(); + const dockerDir = path.join(rootDir, 'docker'); + const postsDir = path.join(rootDir, 'posts'); + let tarballName: string; + let tarballPath: string; + let tarCwd: string; + let tarItems: string[]; + let tarOptions: any = { + gzip: true, + portable: true, + noMtime: true, + }; + + if (existsSync(dockerDir)) { + // Docker is in use: export the entire root directory (excluding node_modules, .next, etc) + tarballName = 'root-export.tar.gz'; + tarballPath = path.join('/tmp', tarballName); + tarCwd = rootDir; + tarItems = ['.']; + tarOptions.file = tarballPath; + tarOptions.cwd = tarCwd; + tarOptions.filter = (filepath: string) => { + // Exclude node_modules, .next, .git, /tmp, and tarball itself + const excludes = [ + 'node_modules', '.next', '.git', 'tmp', 'docker.sock', tarballName + ]; + // Only check top-level folders/files + const rel = filepath.split(path.sep)[0]; + return !excludes.includes(rel); + }; + } else { + // Not docker: export only the posts directory + tarballName = 'posts-export.tar.gz'; + tarballPath = path.join('/tmp', tarballName); + tarCwd = rootDir; + tarItems = ['posts']; + tarOptions.file = tarballPath; + tarOptions.cwd = tarCwd; + } + + // Create tarball + await tar.c( + tarOptions, + tarItems + ); + + // Stream the tarball as a response + const stat = statSync(tarballPath); + const stream = createReadStream(tarballPath); + return new Response(stream as any, { + status: 200, + headers: { + 'Content-Type': 'application/gzip', + 'Content-Disposition': `attachment; filename="${tarballName}"`, + 'Content-Length': stat.size.toString(), + }, + }); + } catch (error) { + console.error('Error exporting tarball:', error); + return NextResponse.json({ error: 'Error exporting tarball' }, { status: 500 }); + } +} \ No newline at end of file diff --git a/src/app/api/admin/folders/details/route.ts b/src/app/api/admin/folders/details/route.ts index 59fca94..392fa91 100644 --- a/src/app/api/admin/folders/details/route.ts +++ b/src/app/api/admin/folders/details/route.ts @@ -1,8 +1,9 @@ import { NextResponse } from 'next/server'; import fs from 'fs'; import path from 'path'; +import { getPostsDirectory } from '@/lib/postsDirectory'; -const postsDirectory = path.join(process.cwd(), 'posts'); +const postsDirectory = getPostsDirectory(); function getFolderStats(folderPath: string) { const fullPath = path.join(postsDirectory, folderPath); diff --git a/src/app/api/admin/folders/route.ts b/src/app/api/admin/folders/route.ts index 7609010..4231c2b 100644 --- a/src/app/api/admin/folders/route.ts +++ b/src/app/api/admin/folders/route.ts @@ -1,8 +1,9 @@ import { NextResponse } from 'next/server'; import fs from 'fs'; import path from 'path'; +import { getPostsDirectory } from '@/lib/postsDirectory'; -const postsDirectory = path.join(process.cwd(), 'posts'); +const postsDirectory = getPostsDirectory(); export async function POST(request: Request) { try { diff --git a/src/app/api/admin/posts/move/route.ts b/src/app/api/admin/posts/move/route.ts index 4e5aed5..6f95ad1 100644 --- a/src/app/api/admin/posts/move/route.ts +++ b/src/app/api/admin/posts/move/route.ts @@ -1,8 +1,9 @@ import { NextResponse } from 'next/server'; import fs from 'fs'; import path from 'path'; +import { getPostsDirectory } from '@/lib/postsDirectory'; -const postsDirectory = path.join(process.cwd(), 'posts'); +const postsDirectory = getPostsDirectory(); export async function POST(request: Request) { try { diff --git a/src/app/api/admin/posts/raw/route.ts b/src/app/api/admin/posts/raw/route.ts index 0b0ea93..82d3070 100644 --- a/src/app/api/admin/posts/raw/route.ts +++ b/src/app/api/admin/posts/raw/route.ts @@ -1,8 +1,9 @@ import { NextRequest, NextResponse } from 'next/server'; import fs from 'fs'; import path from 'path'; +import { getPostsDirectory } from '@/lib/postsDirectory'; -const postsDirectory = path.join(process.cwd(), 'posts'); +const postsDirectory = getPostsDirectory(); export async function GET(request: NextRequest) { const { searchParams } = new URL(request.url); diff --git a/src/app/api/admin/posts/route.ts b/src/app/api/admin/posts/route.ts index 329920b..45be787 100644 --- a/src/app/api/admin/posts/route.ts +++ b/src/app/api/admin/posts/route.ts @@ -2,8 +2,9 @@ import { NextResponse } from 'next/server'; import fs from 'fs'; import path from 'path'; import matter from 'gray-matter'; +import { getPostsDirectory } from '@/lib/postsDirectory'; -const postsDirectory = path.join(process.cwd(), 'posts'); +const postsDirectory = getPostsDirectory(); export async function POST(request: Request) { try { diff --git a/src/app/api/admin/posts/size/route.ts b/src/app/api/admin/posts/size/route.ts index 5ba6734..7349fa5 100644 --- a/src/app/api/admin/posts/size/route.ts +++ b/src/app/api/admin/posts/size/route.ts @@ -1,8 +1,9 @@ import { NextResponse } from 'next/server'; import fs from 'fs'; import path from 'path'; +import { getPostsDirectory } from '@/lib/postsDirectory'; -const postsDirectory = path.join(process.cwd(), 'posts'); +const postsDirectory = getPostsDirectory(); export async function GET(request: Request) { const { searchParams } = new URL(request.url); diff --git a/src/app/api/admin/upload/route.ts b/src/app/api/admin/upload/route.ts index 9b8e11a..983380b 100644 --- a/src/app/api/admin/upload/route.ts +++ b/src/app/api/admin/upload/route.ts @@ -2,8 +2,9 @@ import { NextResponse } from 'next/server'; import fs from 'fs'; import path from 'path'; import matter from 'gray-matter'; +import { getPostsDirectory } from '@/lib/postsDirectory'; -const postsDirectory = path.join(process.cwd(), 'posts'); +const postsDirectory = getPostsDirectory(); export async function POST(request: Request) { try { diff --git a/src/app/api/posts/[slug]/route.ts b/src/app/api/posts/[slug]/route.ts index cb3b5e1..67ebbbd 100644 --- a/src/app/api/posts/[slug]/route.ts +++ b/src/app/api/posts/[slug]/route.ts @@ -8,8 +8,9 @@ import { marked } from 'marked'; import DOMPurify from 'dompurify'; import { JSDOM } from 'jsdom'; import hljs from 'highlight.js'; +import { getPostsDirectory } from '@/lib/postsDirectory'; -const postsDirectory = path.join(process.cwd(), 'posts'); +const postsDirectory = getPostsDirectory(); const renderer = new marked.Renderer(); renderer.code = (code, infostring, escaped) => { diff --git a/src/app/api/posts/route.ts b/src/app/api/posts/route.ts index bf6b3c9..84dd410 100644 --- a/src/app/api/posts/route.ts +++ b/src/app/api/posts/route.ts @@ -8,8 +8,9 @@ import { marked } from 'marked'; import DOMPurify from 'dompurify'; import { JSDOM } from 'jsdom'; import hljs from 'highlight.js'; +import { getPostsDirectory } from '@/lib/postsDirectory'; -const postsDirectory = path.join(process.cwd(), 'posts'); +const postsDirectory = getPostsDirectory(); const pinnedPath = path.join(postsDirectory, 'pinned.json'); let pinnedSlugs: string[] = []; diff --git a/src/lib/markdown.ts b/src/lib/markdown.ts index ff051f7..1a8d169 100644 --- a/src/lib/markdown.ts +++ b/src/lib/markdown.ts @@ -7,6 +7,7 @@ import { JSDOM } from 'jsdom'; import chokidar from 'chokidar'; import type { FSWatcher } from 'chokidar'; import hljs from 'highlight.js'; +import { getPostsDirectory } from './postsDirectory'; export interface Post { slug: string; @@ -19,7 +20,7 @@ export interface Post { author: string; } -const postsDirectory = path.join(process.cwd(), 'posts'); +const postsDirectory = getPostsDirectory(); // Function to get file creation date function getFileCreationDate(filePath: string): Date { diff --git a/src/lib/postsDirectory.ts b/src/lib/postsDirectory.ts new file mode 100644 index 0000000..689d480 --- /dev/null +++ b/src/lib/postsDirectory.ts @@ -0,0 +1,9 @@ +import path from 'path'; +import { existsSync } from 'fs'; + +export function getPostsDirectory() { + const rootDir = process.cwd(); + const dockerDir = path.join(rootDir, 'docker'); + const postsDir = path.join(rootDir, 'posts'); + return existsSync(dockerDir) ? dockerDir : postsDir; +} \ No newline at end of file