initial commit

This commit is contained in:
root
2026-05-13 14:20:41 +00:00
commit 6e178d2012
6022 changed files with 399872 additions and 0 deletions
+296
View File
@@ -0,0 +1,296 @@
'use client';
import { useEffect, useState } from 'react';
import { useRouter } from 'next/navigation';
interface User {
id: number;
email: string;
displayName: string;
role: string;
status: string;
balance: number;
createdAt: string;
sessionId: string;
}
export default function DashboardPage() {
const router = useRouter();
const [user, setUser] = useState<User | null>(null);
const [loading, setLoading] = useState(true);
const [uniqueizerLoading, setUniqueizerLoading] = useState(false);
useEffect(() => {
async function loadUser() {
try {
const response = await fetch('/api/auth/me', { credentials: 'same-origin' });
if (!response.ok) {
router.push('/');
return;
}
const data = await response.json();
setUser(data.user);
} catch (err) {
router.push('/');
} finally {
setLoading(false);
}
}
loadUser();
}, [router]);
async function handleLogout() {
try {
await fetch('/api/auth/logout', {
method: 'POST',
credentials: 'same-origin',
});
router.push('/');
} catch (err) {
console.error('Logout error:', err);
router.push('/');
}
}
async function getCsrfToken(): Promise<string> {
const response = await fetch('/api/auth/csrf', { credentials: 'same-origin' });
const data = await response.json();
return data.csrfToken || '';
}
async function startUniqueizer() {
setUniqueizerLoading(true);
try {
const csrfToken = await getCsrfToken();
const response = await fetch('/api/scenario/uniqueizer/start', {
method: 'POST',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json',
'x-csrf-token': csrfToken,
},
});
if (response.ok) {
const data = await response.json();
// Передаём generationUuid в URL
router.push(`/uniqueizer?generationUuid=${data.generationUuid}`);
} else {
const data = await response.json();
alert('Ошибка запуска сценария: ' + (data.message || response.statusText));
}
} catch (err: unknown) {
alert('Ошибка: ' + (err instanceof Error ? err.message : String(err)));
} finally {
setUniqueizerLoading(false);
}
}
if (loading) {
return <div style={styles.loading}>Загрузка...</div>;
}
return (
<div style={styles.page}>
<div style={styles.container}>
<div style={styles.header}>
<h1 style={styles.headerTitle}>Uno Click</h1>
<button onClick={handleLogout} style={styles.logoutBtn}>
Выйти
</button>
</div>
<div style={styles.welcome}>
<h2 style={styles.welcomeTitle}>
Добро пожаловать, {user?.displayName || user?.email}!
</h2>
<p style={styles.welcomeText}>Выберите сценарий для запуска</p>
</div>
<div style={styles.scenariosGrid}>
{/* Nano Banana */}
<div style={styles.scenarioCard}>
<div style={{ ...styles.scenarioIcon, ...styles.scenarioIconBlue }}>
🎨
</div>
<div style={styles.scenarioName}>Nano Banana</div>
<div style={styles.scenarioDesc}>
Генерация изображений по текстовому описанию. Введите промпт и получите уникальную иллюстрацию.
</div>
<a href="/prompt?scenario=nano-banana" style={styles.btnPrimary}>
Запустить
</a>
</div>
{/* Demo Scenario */}
<div style={styles.scenarioCard}>
<div style={{ ...styles.scenarioIcon, ...styles.scenarioIconGreen }}>
🧪
</div>
<div style={styles.scenarioName}>Demo Scenario</div>
<div style={styles.scenarioDesc}>
Тестовый сценарий для проверки функциональности. Многоступенчатая генерация с подтверждением.
</div>
<a href="/prompt?scenario=demo-scenario" style={styles.btnSecondary}>
Запустить
</a>
</div>
{/* Уникализация */}
<div style={styles.scenarioCard}>
<div style={{ ...styles.scenarioIcon, ...styles.scenarioIconBlue }}>
</div>
<div style={styles.scenarioName}>Уникализация</div>
<div style={styles.scenarioDesc}>
Запуск сценария уникализации контента.
</div>
<button
onClick={startUniqueizer}
disabled={uniqueizerLoading}
style={{
...styles.btnPrimary,
...(uniqueizerLoading ? styles.btnDisabled : {}),
}}
>
{uniqueizerLoading ? 'Запуск...' : 'Запустить'}
</button>
</div>
</div>
</div>
</div>
);
}
const styles: Record<string, React.CSSProperties> = {
page: {
minHeight: '100vh',
padding: '40px 20px',
background: '#f5f5f5',
},
container: {
maxWidth: '800px',
margin: '0 auto',
},
header: {
background: 'white',
padding: '20px 30px',
borderRadius: '8px',
boxShadow: '0 2px 10px rgba(0,0,0,0.1)',
marginBottom: '20px',
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
},
headerTitle: {
fontSize: '20px',
fontWeight: 600,
color: '#333',
},
logoutBtn: {
background: 'none',
border: '1px solid #ddd',
padding: '8px 16px',
borderRadius: '6px',
cursor: 'pointer',
fontSize: '14px',
color: '#666',
},
welcome: {
background: 'white',
padding: '20px 30px',
borderRadius: '8px',
boxShadow: '0 2px 10px rgba(0,0,0,0.1)',
marginBottom: '20px',
},
welcomeTitle: {
fontSize: '18px',
fontWeight: 600,
color: '#333',
marginBottom: '8px',
},
welcomeText: {
color: '#666',
fontSize: '14px',
},
scenariosGrid: {
display: 'grid',
gridTemplateColumns: 'repeat(auto-fill, minmax(300px, 1fr))',
gap: '20px',
},
scenarioCard: {
background: 'white',
borderRadius: '8px',
boxShadow: '0 2px 10px rgba(0,0,0,0.1)',
padding: '24px',
transition: 'box-shadow 0.2s',
},
scenarioIcon: {
width: '48px',
height: '48px',
borderRadius: '10px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontSize: '24px',
marginBottom: '16px',
},
scenarioIconBlue: {
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
},
scenarioIconGreen: {
background: 'linear-gradient(135deg, #11998e 0%, #38ef7d 100%)',
},
scenarioName: {
fontSize: '16px',
fontWeight: 600,
color: '#333',
marginBottom: '8px',
},
scenarioDesc: {
fontSize: '14px',
color: '#666',
marginBottom: '20px',
lineHeight: 1.5,
},
btnPrimary: {
width: '100%',
padding: '12px',
background: '#007bff',
color: 'white',
border: 'none',
borderRadius: '6px',
fontSize: '14px',
fontWeight: 500,
cursor: 'pointer',
textAlign: 'center',
textDecoration: 'none',
display: 'inline-block',
},
btnSecondary: {
width: '100%',
padding: '12px',
background: '#f5f5f5',
color: '#333',
border: '1px solid #ddd',
borderRadius: '6px',
fontSize: '14px',
fontWeight: 500,
cursor: 'pointer',
textAlign: 'center',
textDecoration: 'none',
display: 'inline-block',
},
btnDisabled: {
background: '#ccc',
cursor: 'not-allowed',
},
loading: {
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
minHeight: '100vh',
fontSize: '18px',
color: '#666',
},
};