Files
dynamic-sites-simple/src/pages/editor.astro
2026-04-17 16:08:31 -05:00

135 lines
3.7 KiB
Plaintext

---
import BaseLayout from '../layouts/BaseLayout.astro';
import { loadSiteData } from '../lib/site-data.ts';
const { siteContext } = loadSiteData();
const secret = import.meta.env.EDITOR_SESSION_SECRET || process.env.EDITOR_SESSION_SECRET || 'dev-secret';
const sessionCookie = Astro.cookies.get('editor_session')?.value;
let isAuthed = sessionCookie === secret;
if (!isAuthed && Astro.request.method === 'POST') {
const formData = await Astro.request.formData();
const password = formData.get('password') as string;
const editSecret = import.meta.env.API_EDIT_SECRET || process.env.API_EDIT_SECRET || '';
if (password === editSecret) {
Astro.cookies.set('editor_session', secret, {
httpOnly: true,
sameSite: 'strict',
maxAge: 8 * 60 * 60,
path: '/',
});
isAuthed = true;
}
}
const orchestratorUrl = import.meta.env.PUBLIC_ORCHESTRATOR_URL || process.env.PUBLIC_ORCHESTRATOR_URL || 'http://localhost:3001';
const apiSecret = import.meta.env.API_EDIT_SECRET || process.env.API_EDIT_SECRET || '';
---
<BaseLayout title={`Editor — ${siteContext.businessName}`} primaryColor={siteContext.primaryColor}>
<Fragment slot="logo">{siteContext.businessName}</Fragment>
<Fragment slot="tagline">Content Editor</Fragment>
{!isAuthed ? (
<section class="login-section">
<div class="container">
<div class="login-box">
<h2>Editor Login</h2>
<p>Enter the site edit password to continue.</p>
<form method="POST">
<input type="password" name="password" placeholder="Password" required />
<button type="submit">Log In</button>
</form>
</div>
</div>
</section>
) : (
<section class="editor-section">
<div class="container">
<div id="editor-root"
data-orchestrator-url={orchestratorUrl}
data-api-secret={apiSecret}
></div>
</div>
</section>
)}
<Fragment slot="footer">
&copy; {new Date().getFullYear()} {siteContext.businessName} &middot; Editor
</Fragment>
</BaseLayout>
<style>
.login-section {
display: flex;
align-items: center;
justify-content: center;
min-height: 50vh;
}
.login-box {
max-width: 360px;
padding: 2rem;
background: white;
border: 1px solid var(--color-border);
border-radius: 8px;
text-align: center;
}
.login-box h2 {
font-family: var(--font-display);
font-size: 1.4rem;
color: var(--color-primary-dark);
margin-bottom: 0.5rem;
}
.login-box p {
font-size: 0.9rem;
color: var(--color-text-muted);
margin-bottom: 1.5rem;
}
.login-box input {
display: block;
width: 100%;
padding: 0.6rem 0.8rem;
border: 1px solid var(--color-border);
border-radius: 4px;
font-size: 0.95rem;
margin-bottom: 1rem;
font-family: var(--font-body);
}
.login-box button {
width: 100%;
padding: 0.65rem;
background: var(--color-primary);
color: white;
border: none;
border-radius: 4px;
font-size: 0.95rem;
cursor: pointer;
font-family: var(--font-body);
font-weight: 500;
}
.login-box button:hover {
background: var(--color-primary-dark);
}
.editor-section {
padding: 2rem 0;
}
</style>
{isAuthed && (
<script>
import { createElement } from 'react';
import { createRoot } from 'react-dom/client';
import { VisualEditorIsland } from '../components/editor/VisualEditorIsland';
const el = document.getElementById('editor-root');
if (el) {
const root = createRoot(el);
root.render(createElement(VisualEditorIsland, {
orchestratorUrl: el.dataset.orchestratorUrl || '',
apiSecret: el.dataset.apiSecret || '',
}));
}
</script>
)}