Система управления мультиязычным контентом, построенная на современных технологиях с применением принципов SOLID.
Multipult Frontend - это система управления мультиязычным контентом, построенная на современных технологиях с применением принципов SOLID. Frontend реализован на основе SvelteKit с использованием TypeScript, TailwindCSS и Houdini GraphQL.
Проект обеспечивает:
yarn install
Скопируйте .env.example в .env:
cp .env.example .env
Отредактируйте .env под ваше окружение:
VITE_GRAPHQL_ENDPOINT=http://127.0.0.1:8000/graphql/
VITE_APP_ENV=development
VITE_APP_URL=http://localhost:5173
VITE_DEBUG=true
⚠️ ВАЖНО: Frontend требует работающий backend GraphQL сервер!
Убедитесь, что backend запущен на http://127.0.0.1:8000:
# Перейдите в папку backend
cd ../backend
# Запустите backend сервер (Django)
python manage.py runserver 127.0.0.1:8000
Проверьте доступность GraphQL API: http://127.0.0.1:8000/graphql/
Если backend недоступен: См. BACKEND_CONNECTION.md для troubleshooting
npx houdini generate
yarn dev
Приложение доступно по адресу: http://localhost:5173
Подробная структура проекта описана в PROJECT_STRUCTURE.md.
Фильтрация контента по языкам:
Как использовать:
Документация: см. MULTILINGUAL_USAGE.md и MULTILINGUAL_IMPLEMENTATION.md
Методы входа:
Возможности:
Архитектура:
Документация: см. src/lib/auth/README.md и ARCHITECTURE.md
Иерархическая структура для организации контента.
Поля:
id - уникальный идентификаторpath - путь концепцииdepth - глубина вложенностиparentId - родительская концепция (nullable)Операции: CRUD через Houdini mutations
Переводы концепций на разные языки.
Поля:
id, name, description, imagelanguageId - языкconceptId - связанная концепцияОперации: CRUD с фильтрацией через Houdini
Управление поддерживаемыми языками.
Поля:
id - уникальный идентификаторcode - код языка (ISO)name - название языкаКомпоненты защиты:
ProtectedRoute - защита маршрутовRequirePermission - проверка правRequireRole - проверка ролейconfig.tsAppError)ErrorHandler для единообразной обработкиCacheAndNetwork@list для автообновленияimport { config, logger } from '$lib/config';
// Доступ к конфигурации
const endpoint = config.graphqlEndpoint;
const isProduction = config.isProduction;
// Conditional logging (только в dev)
logger.log('Debug info');
logger.error('Error'); // Всегда выводится
import { errorHandler } from '$lib/errors';
// Автоматическая обработка с уведомлением
try {
await someOperation();
} catch (error) {
errorHandler.handle(error); // Покажет toast автоматически
}
// Создание специфичных ошибок
throw errorHandler.createError.validation('Invalid input');
throw errorHandler.createError.authentication('Token expired');
throw errorHandler.createError.notFound('User');
// Async обработка
await errorHandler.handleAsync(async () => {
return await fetchData();
});
import { notificationStore } from '$lib/notifications';
// Простые уведомления
notificationStore.success('Saved!');
notificationStore.error('Failed to load data');
notificationStore.warning('Please check your input');
notificationStore.info('New update available');
// С дополнительными опциями
notificationStore.success('Saved!', {
title: 'Success',
duration: 3000,
action: {
label: 'View',
callback: () => navigate('/item')
}
});
// Управление
notificationStore.dismiss(id);
notificationStore.dismissAll();
<script lang="ts">
import { graphql } from '$houdini';
import { errorHandler } from '$lib/errors';
import { notificationStore } from '$lib/notifications';
// Query с автоматическим кешированием
const GetConcepts = graphql`
query GetConcepts {
concepts {
id
path
depth
parentId
}
}
`;
const { data } = GetConcepts.fetch();
// Mutation с обработкой ошибок
const CreateConcept = graphql`
mutation CreateConcept($input: ConceptInput!) {
createConcept(input: $input) {
id
path
depth
}
}
`;
async function create() {
try {
await CreateConcept.mutate({
input: {
path: '/categories/electronics',
depth: 2,
parentId: 1
}
});
notificationStore.success('Concept created!');
} catch (error) {
errorHandler.handle(error);
}
}
</script>
{#if $data}
{#each $data.concepts as concept}
<div>{concept.path}</div>
{/each}
{/if}
<script lang="ts">
import { useAuth } from '$lib/auth';
import { RequirePermission, RequireRole } from '$lib/auth';
const auth = useAuth();
async function handleLogin() {
await auth.login({
username: '[email protected]',
password: 'password123'
});
}
</script>
<RequirePermission resource="articles" action="create">
<button>Create Article</button>
</RequirePermission>
<RequireRole role="admin">
<div>Admin Panel</div>
</RequireRole>
concepts # Все концепции
concept(conceptId: Int!) # Одна концепция
dictionaries(conceptId, languageId) # Словари
dictionary(dictionaryId) # Один словарь
languages # Все языки
language(languageId) # Один язык
me # Текущий пользователь
myRoles # Роли пользователя
roles # Все роли
role(roleId) # Одна роль
Авторизация:
login(input: UserLoginInput!)
loginWithGoogle(input: GoogleAuthInput!)
loginWithTelegram(input: TelegramAuthInput!)
register(input: UserRegistrationInput!)
refreshToken(input: RefreshTokenInput!)
verifyEmail(input: EmailVerificationInput!)
requestPasswordReset(input: PasswordResetRequestInput!)
resetPassword(input: PasswordResetInput!)
CRUD операции:
# Концепции
createConcept(input: ConceptInput!)
updateConcept(conceptId, input: ConceptUpdateInput!)
deleteConcept(conceptId: Int!)
# Словари
createDictionary(input: DictionaryInput!)
updateDictionary(dictionaryId, input: DictionaryUpdateInput!)
deleteDictionary(dictionaryId: Int!)
# Языки
createLanguage(input: LanguageInput!)
updateLanguage(languageId, input: LanguageUpdateInput!)
deleteLanguage(languageId: Int!)
# Роли
createRole(input: RoleInput!)
updateRole(roleId, input: RoleUpdateInput!)
assignRoleToUser(userId, roleName)
removeRoleFromUser(userId, roleName)
addPermissionToRole(roleId, input: PermissionInput!)
# Development
yarn dev # Запустить dev сервер
yarn build # Production build
yarn preview # Предпросмотр production build
# Houdini (GraphQL)
yarn generate # Генерация TypeScript типов из GraphQL
yarn generate:watch # Watch режим для автоматической генерации
yarn generate:pull # Загрузить свежую схему с сервера
# Code Quality
yarn lint # Проверить код
yarn format # Форматировать код
yarn check # Type checking
yarn check:watch # Type checking в watch режиме
# Testing
yarn test # Все тесты
yarn test:unit # Unit тесты (Vitest)
yarn test:unit -- --run # Unit тесты без watch
yarn test:unit -- --coverage # Unit тесты с coverage
yarn test:e2e # E2E тесты (Playwright)
# Docker
docker-compose up -d # Запустить в production режиме
docker-compose --profile dev up frontend-dev # Dev режим с hot-reload
docker-compose down # Остановить контейнеры
docker-compose logs -f # Посмотреть логи
📖 Полное описание команд: см. SCRIPTS.md 🐳 Docker документация: см. DOCKER.md
Версия: 0.4.0 | Статус: Production-Ready
📋 Что дальше? См. BACKLOG.md для планов развития
📋 Product Backlog: Приоритизированный список задач и планы развития - см. BACKLOG.md
📝 История изменений: Полная история версий и изменений - см. CHANGELOG.md
Симптомы:
Failed to load resource: status 500Решение:
http://127.0.0.1:8000Симптомы:
psycopg2.errors.UndefinedColumn: column user_profiles.avatar_file_id does not existРешение:
Симптомы:
isAuthenticated = falseРешение:
См. полный список в TROUBLESHOOTING.md
Если у вас возникли вопросы:
docs/scrum/changelog/6-IMPROVEMENTS.md и docs/scrum/changelog/11-SETUP.mdВерсия: 0.4.0 | Статус: Production-Ready
Проект готов к production использованию. Миграция на Houdini завершена, comprehensive unit tests покрывают все критические модули, CI/CD настроен. Добавлена полная библиотека UX компонентов с валидацией форм.
Следующие приоритеты развития смотрите в BACKLOG.md.