diff --git a/src/app/admin/page.tsx b/src/app/admin/page.tsx index 0d8b6fb..91bf6fd 100644 --- a/src/app/admin/page.tsx +++ b/src/app/admin/page.tsx @@ -491,14 +491,80 @@ export default function AdminPage() { }; function handleExportTarball() { - fetch('/api/admin/export') + // Create popup modal + const modal = document.createElement('div'); + modal.style.position = 'fixed'; + modal.style.top = '0'; + modal.style.left = '0'; + modal.style.width = '100%'; + modal.style.height = '100%'; + modal.style.backgroundColor = 'rgba(0, 0, 0, 0.5)'; + modal.style.display = 'flex'; + modal.style.alignItems = 'center'; + modal.style.justifyContent = 'center'; + modal.style.zIndex = '1000'; + + const modalContent = document.createElement('div'); + modalContent.style.backgroundColor = 'white'; + modalContent.style.padding = '30px'; + modalContent.style.borderRadius = '8px'; + modalContent.style.maxWidth = '500px'; + modalContent.style.width = '90%'; + modalContent.style.textAlign = 'center'; + modalContent.style.position = 'relative'; + + modalContent.innerHTML = ` + +

Export Method

+

How are you hosting this application?

+

If you don't know your method of hosting, ask your Systems Administrator

+
+ + +
+ `; + + modal.appendChild(modalContent); + document.body.appendChild(modal); + + // Add event listeners + const dockerBtn = modal.querySelector('#docker-btn'); + const localBtn = modal.querySelector('#local-btn'); + const closeBtn = modal.querySelector('#close-btn'); + + const closeModal = () => { + document.body.removeChild(modal); + }; + + dockerBtn?.addEventListener('click', () => { + closeModal(); + exportFromEndpoint('/api/admin/export'); + }); + + localBtn?.addEventListener('click', () => { + closeModal(); + exportFromEndpoint('/api/admin/exportlocal'); + }); + + closeBtn?.addEventListener('click', closeModal); + + // Close modal when clicking outside + modal.addEventListener('click', (e) => { + if (e.target === modal) { + closeModal(); + } + }); + } + + function exportFromEndpoint(endpoint: string) { + fetch(endpoint) .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'; + a.download = endpoint.includes('local') ? 'local-export.tar.gz' : 'docker-export.tar.gz'; document.body.appendChild(a); a.click(); a.remove(); diff --git a/src/app/api/admin/export/route.ts b/src/app/api/admin/export/route.ts index 7c2bb61..f028e5d 100644 --- a/src/app/api/admin/export/route.ts +++ b/src/app/api/admin/export/route.ts @@ -3,6 +3,9 @@ import { NextResponse } from 'next/server'; import { statSync, createReadStream, existsSync } from 'fs'; import path from 'path'; +// This is the route for exporting posts when using the docker production server +// If you try this on the local server, it will fail because the posts directory is not on the local server + export async function GET() { try { const dockerDir = '/app/docker'; // update this to your actual path diff --git a/src/app/api/admin/exportlocal/route.ts b/src/app/api/admin/exportlocal/route.ts new file mode 100644 index 0000000..fa760f7 --- /dev/null +++ b/src/app/api/admin/exportlocal/route.ts @@ -0,0 +1,47 @@ +import tar from 'tar'; +import { NextResponse } from 'next/server'; +import { statSync, createReadStream, existsSync } from 'fs'; +import path from 'path'; +import { gzip } from 'zlib'; + + +// This is the route for exporting posts when using the local production server +// If you try this on the docker server, it will fail because the posts directory is not on the docker server + +export async function GET() { + try { + const localDir = 'posts'; + const tarballName = 'local-export.tar.gz'; + const tarballPath = path.join('/tmp', tarballName); + + if (!existsSync(localDir)) { + return NextResponse.json({ error: `${localDir} directory does not exist` }, { status: 400 }); + } + + await tar.c( + { + gzip: true, + file: tarballPath, + cwd: path.dirname(localDir), + portable: true, + noMtime: true, + }, + [path.basename(localDir)] + ); + + 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 local:', error); + return NextResponse.json({ error: 'Error exporting local' }, { status: 500 }); + } +}