feat: admin panel — dashboard, articles list, editor, auth, cover regen, AI generate

This commit is contained in:
Alexey Pavlov
2026-05-31 14:17:58 +03:00
parent e968dbfd1f
commit 5fc85a31d4
15 changed files with 928 additions and 0 deletions
+20
View File
@@ -0,0 +1,20 @@
import { cookies } from 'next/headers';
const SESSION_COOKIE = 'zp_admin_session';
const VALID_TOKEN = process.env.ADMIN_PASSWORD || 'zeropost_admin_2026';
export async function checkAdminAuth() {
const jar = await cookies();
const token = jar.get(SESSION_COOKIE)?.value;
return token === VALID_TOKEN;
}
export async function requireAdminAuth() {
const ok = await checkAdminAuth();
if (!ok) {
const { redirect } = await import('next/navigation');
redirect('/admin/login');
}
}
export { SESSION_COOKIE, VALID_TOKEN };
+36
View File
@@ -71,3 +71,39 @@ export async function generateArticle(data) {
body: JSON.stringify(data),
});
}
// ── Admin API ─────────────────────────────────────────────────────────────────
export async function adminListArticles({ limit = 50, offset = 0 } = {}) {
return call(`/api/articles?limit=${limit}&offset=${offset}`);
}
export async function adminGetArticle(id) {
return call(`/api/articles/id/${id}`);
}
export async function adminUpdateArticle(id, data) {
return call(`/api/articles/${id}`, {
method: 'PATCH',
body: JSON.stringify(data),
});
}
export async function adminDeleteArticle(id) {
return call(`/api/articles/${id}`, { method: 'DELETE' });
}
export async function adminBackfillCovers(limit = 5) {
return call('/api/articles/backfill-covers', {
method: 'POST',
body: JSON.stringify({ limit }),
});
}
export async function adminGenerateArticle(topic, tags = []) {
return call('/api/articles/generate', {
method: 'POST',
body: JSON.stringify({ topic, tags, autoPublish: false }),
});
}