feat(zero): admin UI for Zero notes management
Adds new admin section 'Заметки Зеро' (☕) with:
- manual 'Generate' button with channel/bucket/allow-dup form
- status filter tabs with counters (draft/approved/published/failed/skipped)
- per-note actions: approve / edit inline / regenerate with bucket pick / skip
- status-colored cards with bucket icon, pose, scheduled time MSK
- error display with attempt counter
- tokens & model footer
Files:
app/api/admin/zero/[...path]/route.js catch-all proxy → engine
components/admin/AdminZero.js main component
components/AdminPanel.js +section in sidebar
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Catch-all proxy для /api/admin/zero/* → engine /api/admin/zero/*
|
||||
* Принимает любой метод и любой путь. Auth: session cookie → user.isAdmin.
|
||||
*/
|
||||
import { NextResponse } from 'next/server';
|
||||
import { requireUser } from '@/lib/session';
|
||||
|
||||
const ENGINE_URL = process.env.ENGINE_URL || 'http://127.0.0.1:3030';
|
||||
const ENGINE_SECRET = process.env.ENGINE_SECRET || '';
|
||||
|
||||
async function proxy(req, { params }) {
|
||||
const user = await requireUser();
|
||||
if (!user?.isAdmin) {
|
||||
return NextResponse.json({ error: 'Forbidden' }, { status: 403 });
|
||||
}
|
||||
const tail = (params?.path || []).join('/');
|
||||
const qs = req.url.split('?')[1];
|
||||
const url = `${ENGINE_URL}/api/admin/zero${tail ? '/' + tail : ''}${qs ? '?' + qs : ''}`;
|
||||
|
||||
const headers = {
|
||||
'x-internal-secret': ENGINE_SECRET,
|
||||
'x-user-id': String(user.id),
|
||||
};
|
||||
|
||||
let body;
|
||||
if (req.method !== 'GET' && req.method !== 'HEAD') {
|
||||
const ct = req.headers.get('content-type') || '';
|
||||
if (ct.includes('application/json')) {
|
||||
headers['Content-Type'] = 'application/json';
|
||||
const raw = await req.text();
|
||||
body = raw || undefined;
|
||||
} else {
|
||||
body = await req.text();
|
||||
}
|
||||
}
|
||||
|
||||
const res = await fetch(url, { method: req.method, headers, body, cache: 'no-store' });
|
||||
const data = await res.json().catch(() => ({ error: 'invalid engine response' }));
|
||||
return NextResponse.json(data, { status: res.status });
|
||||
}
|
||||
|
||||
export const GET = proxy;
|
||||
export const POST = proxy;
|
||||
export const PATCH = proxy;
|
||||
export const PUT = proxy;
|
||||
export const DELETE = proxy;
|
||||
Reference in New Issue
Block a user