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() {
|
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) => {
|
.then(async (res) => {
|
||||||
if (!res.ok) throw new Error('Export failed');
|
if (!res.ok) throw new Error('Export failed');
|
||||||
const blob = await res.blob();
|
const blob = await res.blob();
|
||||||
const url = window.URL.createObjectURL(blob);
|
const url = window.URL.createObjectURL(blob);
|
||||||
const a = document.createElement('a');
|
const a = document.createElement('a');
|
||||||
a.href = url;
|
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);
|
document.body.appendChild(a);
|
||||||
a.click();
|
a.click();
|
||||||
a.remove();
|
a.remove();
|
||||||
|
|||||||
@@ -3,6 +3,9 @@ import { NextResponse } from 'next/server';
|
|||||||
import { statSync, createReadStream, existsSync } from 'fs';
|
import { statSync, createReadStream, existsSync } from 'fs';
|
||||||
import path from 'path';
|
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() {
|
export async function GET() {
|
||||||
try {
|
try {
|
||||||
const dockerDir = '/app/docker'; // update this to your actual path
|
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