Add modal for export method selection in AdminPage and update export filename based on hosting type
Some checks failed
Deploy / build-and-deploy (push) Failing after 1s
Some checks failed
Deploy / build-and-deploy (push) Failing after 1s
This commit is contained in:
@@ -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 = `
|
||||
<button id="close-btn" style="position: absolute; top: 10px; right: 15px; background: none; border: none; font-size: 24px; cursor: pointer; color: #666; font-weight: bold;">×</button>
|
||||
<h2 style="margin: 0 0 20px 0; font-size: 1.5rem; font-weight: bold;">Export Method</h2>
|
||||
<p style="margin: 0 0 20px 0; color: #666;">How are you hosting this application?</p>
|
||||
<p style="margin: 0 0 20px 0; font-size: 0.9rem; color: #888; font-style: italic;">If you don't know your method of hosting, ask your Systems Administrator</p>
|
||||
<div style="display: flex; gap: 10px; justify-content: center; margin-top: 20px;">
|
||||
<button id="docker-btn" style="padding: 10px 20px; background-color: #059669; color: white; border: none; border-radius: 5px; cursor: pointer; font-weight: bold;">Docker</button>
|
||||
<button id="local-btn" style="padding: 10px 20px; background-color: #2563eb; color: white; border: none; border-radius: 5px; cursor: pointer; font-weight: bold;">Local</button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
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();
|
||||
|
||||
@@ -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
|
||||
|
||||
47
src/app/api/admin/exportlocal/route.ts
Normal file
47
src/app/api/admin/exportlocal/route.ts
Normal file
@@ -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 });
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user