Files
zeropost-web/app/zero/page.js
T
Aleksei Pavlov 4c0942d11b fix(zero): defensive fetch — don't crash Server Components on engine errors
- autogen page.js: wrap engineCall in try/catch (return null on any failure)
- /zero page: use Promise.allSettled so one failed source doesn't break page

Root cause was ENGINE_URL=host.docker.internal not resolving in coolify net,
fixed at env level but defending code too so future quirks degrade gracefully.
2026-06-19 11:32:32 +03:00

79 lines
3.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import Header from '@/components/Header';
import Footer from '@/components/Footer';
import ZeroBlock from '@/components/ZeroBlock';
import { listZeroNotes, getZeroCharacter } from '@/lib/engine';
import { Coffee } from 'lucide-react';
export const dynamic = 'force-dynamic';
export const metadata = {
title: 'Заметки от Зеро',
description: 'Короткие посты от AI-персонажа Зеро — мысли программиста о работе, инструментах и забавных багах',
};
export default async function ZeroPage() {
// defensive: один битый источник не должен валить страницу
const results = await Promise.allSettled([
listZeroNotes({ limit: 100 }),
getZeroCharacter(),
]);
const notes = results[0].status === 'fulfilled' ? results[0].value : [];
const character = results[1].status === 'fulfilled' ? results[1].value : null;
return (
<>
<Header />
<main className="pt-10 pb-16">
<div className="container-wide mb-10">
<div
className="inline-flex items-center gap-2 text-xs accent px-3 py-1.5 rounded-full mb-4"
style={{ background: 'rgb(var(--accent) / 0.1)', border: '1px solid rgb(var(--accent) / 0.2)' }}
>
<Coffee className="w-3.5 h-3.5" /> AI-персонаж
</div>
<h1 className="text-3xl sm:text-5xl font-bold ink mb-3 leading-tight">
Заметки от Зеро
</h1>
<p className="mute text-base sm:text-lg max-w-2xl mb-8">
Короткие посты от первого лица в Telegram-канале{' '}
<a href="https://t.me/zeropostru" target="_blank" rel="noreferrer" className="accent hover:underline">@zeropostru</a>.
Программист с многолетним опытом, любит копаться под капотом, постоянно носится с кофе.
</p>
{character?.character?.bio && (
<div className="rounded-xl border border-amber-200 dark:border-amber-900 bg-amber-50/50 dark:bg-amber-950/20 p-5 sm:p-6 max-w-3xl">
<div className="flex items-start gap-4">
{/* eslint-disable-next-line @next/next/no-img-element */}
<img
src="/uploads/zero-coffee.webp"
alt="Зеро"
className="w-16 h-16 rounded-full object-cover bg-amber-100 dark:bg-amber-950/40 ring-2 ring-amber-300 dark:ring-amber-800 shrink-0"
/>
<div className="min-w-0">
<div className="text-sm font-semibold ink mb-2">Кто такой Зеро</div>
<ul className="space-y-1 text-sm mute">
{character.character.bio.map((line, i) => (
<li key={i}> {line}</li>
))}
</ul>
</div>
</div>
</div>
)}
</div>
{notes.length > 0 ? (
<ZeroBlock notes={notes} />
) : (
<div className="container-wide">
<div className="rounded-xl border border-neutral-200 dark:border-neutral-800 p-10 text-center">
<Coffee className="w-8 h-8 text-amber-500 mx-auto mb-3" />
<p className="mute text-sm">Зеро ещё не написал ни одной заметки. Скоро появится.</p>
</div>
</div>
)}
</main>
<Footer />
</>
);
}