Prepare production deployment

This commit is contained in:
2026-07-20 04:14:24 +05:00
parent 55583e2101
commit 2e3550a4dc
46 changed files with 1497 additions and 882 deletions
+99 -282
View File
@@ -1,316 +1,133 @@
# Flamy Trade — Frontend
# Flamy Trade
Публичный лендинг и дашборд ML-прогнозов для крипторынка. Построен на **SvelteKit 5** (Runes API), **Tailwind v4**, **TypeScript** и **lightweight-charts v5**.
Публичный frontend для `trade.flamy.studio`: лендинг, блог и демонстрационный дашборд ML-прогнозов по крипторынку.
---
Проект построен на SvelteKit, Svelte 5 Runes, Tailwind CSS v4, TypeScript и `lightweight-charts`.
## Стек
## Требования
| Слой | Технология |
|---|---|
| Фреймворк | SvelteKit 5 (runes, SSR) |
| Язык | TypeScript (strict) |
| Стили | Tailwind CSS v4 (Vite plugin, `@theme`) |
| Графики | lightweight-charts v5 (TradingView) |
| Сервер | `@sveltejs/adapter-node` (Node.js) |
| Пакетный менеджер | pnpm |
```text
Node.js: >=24.0.0 <25
pnpm: >=11.15.1 <12
```
---
В Windows PowerShell запускайте pnpm через `pnpm.cmd`, если выполнение `.ps1`-скриптов отключено.
## Быстрый старт
**Требования:** Node.js ≥ 20, pnpm ≥ 9
```bash
# 1. Установить зависимости
pnpm install
# 2. Запустить dev-сервер
pnpm dev
# 3. Открыть в браузере
# http://localhost:5173
```
Остальные команды:
Локальный адрес по умолчанию:
```text
http://localhost:5173
```
## Команды
```bash
pnpm build # Сборка для продакшена (папка build/)
pnpm preview # Превью продакшен-сборки
pnpm check # Svelte + TypeScript проверка типов
pnpm lint # ESLint + Prettier проверка
pnpm format # Автоформатирование
pnpm format # автоформатирование
pnpm format:check # проверка форматирования
pnpm lint # ESLint
pnpm check # Svelte + TypeScript
pnpm build # production build
pnpm preview # preview production build
pnpm audit # аудит зависимостей
```
---
## Структура проекта
```
src/
├── lib/
│ ├── components/
│ │ ├── Chart.svelte # lightweight-charts обёртка
│ │ ├── home/
│ │ │ ├── DashboardMockup.svelte # 3D-макет на главной
│ │ │ └── DashboardStatus.svelte
│ │ ├── layout/
│ │ │ ├── Header.svelte # Фиксированная шапка + мобильное меню
│ │ │ └── Footer.svelte
│ │ └── ui/
│ │ ├── Button.svelte # Универсальная кнопка/ссылка
│ │ └── Logo.svelte
│ ├── blog/
│ │ └── posts.ts # Статьи блога (статические данные)
│ ├── stores/
│ │ └── chartStore.ts # Типы + генератор фейковых данных
│ ├── utils.ts # cn() — утилита для className
│ └── index.ts
├── routes/
│ ├── +layout.svelte # Корневой layout: Header, Footer, шрифты
│ ├── +page.svelte # Главная страница (секции)
│ ├── +error.svelte # Страница ошибки
│ ├── (sections)/ # Секции главной страницы
│ │ ├── Hero.svelte
│ │ ├── Ticker.svelte
│ │ ├── About.svelte
│ │ ├── Features.svelte
│ │ ├── Stats.svelte
│ │ ├── BlogPreview.svelte
│ │ └── Cta.svelte
│ ├── about/
│ │ ├── +page.svelte
│ │ └── _data.ts
│ ├── blog/
│ │ ├── +page.svelte # Список статей
│ │ └── [slug]/
│ │ ├── +page.ts # Загрузка статьи по slug
│ │ └── +page.svelte # Статья
│ └── dashboard/
│ └── +page.svelte # Интерактивный дашборд с графиком
└── routes/layout.css # Tailwind @theme + глобальные стили
```
---
## Дизайн-система
### Цвета (`layout.css`)
```css
--color-primary: #fe4b07 /* Акцент — оранжевый */
--color-primary-h:#c83e06 /* Hover-состояние */
--color-bg: #09080a /* Фон страницы */
--color-bg-e: #0f0e10 /* Elevated (шапка, карточки) */
--color-bg-c: #141318 /* Card background */
--color-bg-h: #1a191e /* Hover / бордеры */
--color-title: #f0ede6 /* Основной текст */
--color-desc: #8a887f /* Вторичный текст */
```
В Tailwind используются как `bg-primary`, `text-desc`, `border-bg-h` и т.д.
### Шрифты
| Переменная | Семейство | Применение |
|---|---|---|
| `font-display` | Unbounded | Заголовки, кнопки, тикеры |
| `font-sans` | DM Sans | Основной текст |
| `font-mono` | JetBrains Mono | Цены, метки, метаданные |
Подключаются через Google Fonts в `+layout.svelte`.
### Компонент `Button.svelte`
```svelte
<!-- Первичная (ссылка) -->
<Button href="/dashboard">Открыть дашборд</Button>
<!-- Вторичная (кнопка) -->
<Button variant="secondary" onclick={handler}>Действие</Button>
```
Props: `href?`, `target?`, `variant?: 'primary' | 'secondary'`, `class?`
---
## Дашборд и Charts
### Архитектура данных
```
chartStore.ts → generateChartData(symbolId, timeframeId)
└── возвращает ChartData
├── candles: CandleBar[] // 160 свечей
├── prediction: PredPoint[] // 19 прогнозных точек
├── signal: Signal // direction, confidence, entry/tp/sl
├── currentPrice: number
└── change24h(Pct): number
```
`generateChartData`**заглушка с детерминированными фейковыми данными**. При подключении реального бекенда её нужно заменить на API-запрос, сохранив возвращаемые типы:
```typescript
// chartStore.ts — заменить эту функцию на реальный запрос:
export async function fetchChartData(symbolId: string, tfId: TimeframeId): Promise<ChartData> {
const res = await fetch(`/api/chart/${symbolId}?tf=${tfId}`);
return res.json();
}
```
### Компонент `Chart.svelte`
```svelte
<Chart data={chartData} decimals={2} height={520} />
```
| Prop | Тип | Описание |
|---|---|---|
| `data` | `ChartData` | Свечи + прогноз + сигнал |
| `decimals` | `number` | Кол-во знаков после запятой для цены |
| `height` | `number` | Высота canvas в пикселях (default: 520) |
Что рендерится:
- **CandlestickSeries** — исторические зелёные/красные свечи
- **LineSeries** — пунктирная оранжевая линия ML-прогноза (19 свечей вперёд)
- **PriceLine** × 3 — горизонтальные линии Entry / TP / SL с подписями на шкале
- **OHLC тултип** — кастомный HTML-оверлей при наведении (показывает O/H/L/C и ML-значение)
- **ResizeObserver** — автоматически подстраивает ширину и высоту при изменении окна
### Таймфреймы и символы
Определены в `chartStore.ts`:
```typescript
SYMBOLS // 8 монет: BTC ETH SOL BNB XRP ADA DOGE LINK
TIMEFRAMES // 6 таймфреймов: 1m 5m 15m 1h 4h 1d
```
---
## Контракт API (для бекенда)
Дашборд ожидает от бекенда следующую форму ответа:
```typescript
type ChartData = {
candles: Array<{
time: number; // Unix timestamp (UTC seconds)
open: number;
high: number;
low: number;
close: number;
}>;
prediction: Array<{
time: number; // Unix timestamp будущих свечей
value: number; // Прогнозная цена (mid/close)
}>;
signal: {
direction: 'long' | 'short';
confidence: number; // 0100
entry: number;
tp: number;
sl: number;
};
currentPrice: number;
change24h: number;
change24hPct: number;
};
```
Эндпоинт (предполагаемый):
```
GET /api/chart/:symbol?tf=5m
```
---
## Блог
Статьи хранятся в `src/lib/blog/posts.ts` как статический массив `Post[]`.
```typescript
type Post = {
id: number;
slug: string; // URL: /blog/:slug
date: string; // ISO 8601
title: string;
description: string;
tags: string[];
content: string; // HTML-строка
};
```
Для подключения CMS — заменить `posts` на API-запрос в `+page.ts` / `+page.server.ts`.
---
`pnpm outdated` может показывать `@types/node` 26.x и TypeScript 7.x. Это не ошибка текущего стека: проект закреплён на Node 24 LTS, а `@sveltejs/kit` и `typescript-eslint` на текущих версиях требуют TypeScript `<6.1`.
## Конфигурация
### SvelteKit (`svelte.config.js`)
Основные изменяемые значения вынесены в `.env.example`.
- Адаптер: `adapter-node` (деплой как Node.js-сервер)
- Runes: принудительно включены для всех файлов вне `node_modules`
### Tailwind v4 (`layout.css`)
Tailwind подключается через Vite-плагин (`@tailwindcss/vite`), конфиг CSS-первый — все кастомные токены в блоке `@theme` в `src/routes/layout.css`.
**Добавить новый токен:**
```css
/* layout.css */
@theme {
--color-accent: #your-color;
}
```
После этого доступен как `bg-accent`, `text-accent` и т.д.
---
## Деплой
Сборка производит Node.js-сервер:
Публичные значения сайта:
```bash
pnpm build
node build/index.js
PUBLIC_SITE_URL=https://trade.flamy.studio
PUBLIC_SITE_NAME=Flamy Trade
PUBLIC_SITE_DESCRIPTION=Публичная витрина ML-прогнозов для крипторынка.
```
Переменные окружения:
```
PORT=3000 # порт (default: 3000)
HOST=0.0.0.0 # хост
ORIGIN=https://... # обязательно при deploy за проксей
Runtime-переменные SvelteKit adapter-node:
```bash
HOST=0.0.0.0
PORT=3000
ORIGIN=https://trade.flamy.studio
BODY_SIZE_LIMIT=1M
SHUTDOWN_TIMEOUT=15
```
Docker (минимальный `Dockerfile`):
```dockerfile
FROM node:22-alpine
WORKDIR /app
COPY build/ ./build/
COPY package.json .
RUN npm install --omit=dev --ignore-scripts
CMD ["node", "build/index.js"]
Docker Compose:
```bash
APP_HOST_IP=10.20.0.20
APP_PUBLISHED_PORT=18082
APP_IMAGE_TAG=<git-sha>
```
---
## Структура
## Соглашения по коду
```text
src/lib/blog/posts.ts # статические статьи блога
src/lib/config/site.ts # единая конфигурация сайта и canonical URL
src/lib/stores/chartStore.ts # типы графика и демонстрационный provider
src/lib/components/Chart.svelte # lightweight-charts компонент
src/routes/healthz/+server.ts # health endpoint
src/routes/sitemap.xml/+server.ts # sitemap
docker/nginx/default.conf # project nginx
docs/deployment.md # runbook развёртывания
```
- **Svelte 5 Runes** везде: `$state`, `$derived`, `$effect`, `$props` — никакого legacy API
- **Нет `export let`** — только деструктуризация `$props()`
- **Tailwind-first** — inline-стили только там, где Tailwind не справляется (динамические значения, chart dimensions)
- **cn()** из `$lib/utils` для условного объединения классов
- **Нет комментариев** если смысл очевиден из имён; комментарий — только для неочевидного инварианта
- **SSR-safe** — всё что требует `window`/`document` — только в `onMount`
- **Cleanup** — каждый `onMount` возвращает функцию очистки (removeEventListener, disconnect, remove)
## Данные дашборда
---
Текущий provider в `chartStore.ts` генерирует демонстрационные данные. Он не является реальным торговым API и не меняет контракт будущего backend.
## Известные ограничения
Ожидаемая форма `ChartData`:
- Данные в дашборде — **фейковые** (детерминированный генератор). Требует подключения реального API бекенда через замену `generateChartData` в `chartStore.ts`
- Прогнозная линия — условная визуализация; реальная модель возвращает `prediction[]` из бекенда
- Блог — статические данные в коде; для production рекомендуется CMS или headless API
```typescript
type ChartData = {
candles: Array<{ time: number; open: number; high: number; low: number; close: number }>;
prediction: Array<{ time: number; value: number }>;
signal: {
direction: 'long' | 'short';
confidence: number;
entry: number;
tp: number;
sl: number;
};
currentPrice: number;
change24h: number;
change24hPct: number;
};
```
## Блог
Статьи хранятся как типизированные блоки, а не как HTML-строки. Это позволяет рендерить контент без `{@html}` и без отдельной санитаризации.
## Production
Production-сборка рассчитана на схему:
```text
Internet -> Caddy -> 10.20.0.20:18082 -> nginx -> app:3000
```
Подробный порядок развёртывания, Caddy block, smoke-check и rollback описаны в [docs/deployment.md](docs/deployment.md).
## Что не входит в текущий этап
Пока не подключаются:
- ML-runtime;
- реальный API;
- S3;
- база данных;
- Redis;
- Telegram egress;
- публикация ML API через Caddy.