# 🤝 Руководство по внесению вклада в Ospabhost 8.1 Спасибо за интерес к улучшению проекта! Этот документ описывает процесс внесения изменений. --- ## 📋 Содержание - [Кодекс поведения](#кодекс-поведения) - [С чего начать](#с-чего-начать) - [Процесс разработки](#процесс-разработки) - [Стандарты кода](#стандарты-кода) - [Коммиты](#коммиты) - [Pull Requests](#pull-requests) - [Архитектурные решения](#архитектурные-решения) - [Тестирование](#тестирование) - [Документация](#документация) --- ## 📜 Кодекс поведения ### Наши обязательства - Уважительное отношение ко всем участникам - Конструктивная критика - Фокус на улучшении проекта - Помощь новичкам ### Недопустимое поведение - Оскорбления и агрессия - Троллинг и спам - Дискриминация любого рода - Публикация личной информации без разрешения --- ## 🚀 С чего начать ### Для новичков Ищите issues с метками: - `good first issue` - простые задачи для начинающих - `help wanted` - задачи, где нужна помощь - `documentation` - улучшение документации ### Подготовка окружения 1. **Форк репозитория** Нажмите кнопку "Fork" на GitHub. 2. **Клонирование** ```bash git clone https://github.com/YOUR_USERNAME/ospabhost8.1.git cd ospabhost8.1/ospabhost ``` 3. **Добавление upstream** ```bash git remote add upstream https://github.com/Ospab/ospabhost8.1.git ``` 4. **Установка зависимостей** ```bash # Backend cd backend npm install cp .env.example .env # Настройте .env # Миграции npx prisma migrate dev npx prisma generate npx prisma db seed # Frontend cd ../frontend npm install cp .env.example .env ``` 5. **Запуск** ```bash # Terminal 1: Backend cd backend npm run dev # Terminal 2: Frontend cd frontend npm run dev ``` --- ## 🔄 Процесс разработки ### 1. Синхронизация с upstream ```bash git checkout main git fetch upstream git merge upstream/main git push origin main ``` ### 2. Создание ветки ```bash git checkout -b feature/your-feature-name ``` **Префиксы веток:** - `feature/` - новая функциональность - `fix/` - исправление бага - `refactor/` - рефакторинг кода - `docs/` - изменения в документации - `test/` - добавление тестов - `chore/` - рутинные задачи **Примеры:** ```bash git checkout -b feature/add-user-notifications git checkout -b fix/ticket-assignment-bug git checkout -b docs/update-api-documentation ``` ### 3. Разработка Внесите изменения, следуя [стандартам кода](#стандарты-кода). ### 4. Коммиты ```bash git add . git commit -m "feat: add user notifications" ``` См. [раздел о коммитах](#коммиты) для подробностей. ### 5. Пуш ```bash git push origin feature/your-feature-name ``` ### 6. Pull Request Откройте PR на GitHub, следуя [шаблону](#pull-requests). --- ## 📝 Стандарты кода ### TypeScript/JavaScript #### Общие правила - ✅ Используйте TypeScript везде - ✅ Строгая типизация (`strict: true`) - ❌ Избегайте `any` (используйте `unknown` при необходимости) - ✅ Используйте `const` и `let`, не `var` - ✅ Предпочитайте стрелочные функции - ✅ Асинхронный код через `async/await` #### Именование ```typescript // ✅ Хорошо const userName = 'John'; const getUserById = async (id: number) => { ... }; class UserService { ... } interface UserData { ... } type UserId = number; // ❌ Плохо const username = 'John'; // camelCase для переменных const get_user_by_id = () => { ... }; // не snake_case class userService { ... } // PascalCase для классов ``` #### Функции и методы ```typescript // ✅ Хорошо async function getUserById(id: number): Promise { try { const user = await prisma.user.findUnique({ where: { id } }); return user; } catch (error) { console.error('Error fetching user:', error); throw new Error('Failed to fetch user'); } } // ❌ Плохо function getUserById(id: any) { // any запрещён const user = prisma.user.findUnique({ where: { id } }); // нет await return user; // нет обработки ошибок } ``` #### Обработка ошибок ```typescript // ✅ Хорошо try { const result = await riskyOperation(); return res.json(result); } catch (error) { console.error('[Module] Error:', error); const message = error instanceof Error ? error.message : 'Unknown error'; return res.status(500).json({ error: message }); } // ❌ Плохо try { const result = await riskyOperation(); return res.json(result); } catch (error) { return res.status(500).json({ error: error }); // может быть не Error } ``` #### Express Controllers ```typescript // ✅ Хорошо export async function createServer(req: Request, res: Response) { try { const userId = (req as any).user?.id; if (!userId) { return res.status(401).json({ error: 'Unauthorized' }); } const { osId, tariffId } = req.body; if (!osId || !tariffId) { return res.status(400).json({ error: 'Missing required fields' }); } const server = await serverService.createServer({ userId, osId: Number(osId), tariffId: Number(tariffId), }); return res.json({ server }); } catch (error) { console.error('[Server] Create error:', error); const message = error instanceof Error ? error.message : 'Server creation failed'; return res.status(500).json({ error: message }); } } ``` ### React/Frontend #### Компоненты ```typescript // ✅ Хорошо - функциональный компонент с типизацией interface UserCardProps { user: User; onEdit: (userId: number) => void; } const UserCard: React.FC = ({ user, onEdit }) => { const [isLoading, setIsLoading] = useState(false); const handleEdit = useCallback(() => { onEdit(user.id); }, [user.id, onEdit]); return (

{user.name}

); }; export default UserCard; ``` #### Hooks ```typescript // ✅ Хорошо - кастомный хук с типизацией interface UseUserDataReturn { user: User | null; loading: boolean; error: string | null; refetch: () => Promise; } export const useUserData = (userId: number): UseUserDataReturn => { const [user, setUser] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const fetchUser = useCallback(async () => { try { setLoading(true); const response = await apiClient.get(`/users/${userId}`); setUser(response.data); setError(null); } catch (err) { setError(err instanceof Error ? err.message : 'Unknown error'); } finally { setLoading(false); } }, [userId]); useEffect(() => { fetchUser(); }, [fetchUser]); return { user, loading, error, refetch: fetchUser }; }; ``` ### Prisma #### Схема ```prisma // ✅ Хорошо - явные типы и связи model User { id Int @id @default(autoincrement()) email String @unique username String @unique password String balance Float @default(0) isAdmin Boolean @default(false) operator Boolean @default(false) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt servers Server[] tickets Ticket[] posts Post[] comments Comment[] @@index([email]) @@index([username]) } ``` #### Запросы ```typescript // ✅ Хорошо - типизированные запросы с обработкой async function getServerWithRelations(id: number, userId: number) { const server = await prisma.server.findFirst({ where: { id, userId, // Проверка владельца }, include: { os: true, tariff: true, user: { select: { id: true, username: true, email: true, }, }, }, }); if (!server) { throw new Error('Server not found or access denied'); } return server; } ``` ### SQL/Миграции ```sql -- ✅ Хорошо - явные имена, индексы, значения по умолчанию CREATE TABLE `StoragePlan` ( `id` INTEGER NOT NULL AUTO_INCREMENT, `code` VARCHAR(191) NOT NULL, `name` VARCHAR(191) NOT NULL, `price` DECIMAL(10, 2) NOT NULL DEFAULT 0, `pricePerGb` DECIMAL(10, 2) NULL, `bandwidthPerGb` DECIMAL(10, 2) NULL, `requestsPerGb` INTEGER NULL, `quotaGb` INTEGER NOT NULL DEFAULT 0, `bandwidthGb` INTEGER NOT NULL DEFAULT 0, `requestLimit` VARCHAR(191) NOT NULL DEFAULT '0', `description` TEXT NULL, `order` INTEGER NOT NULL DEFAULT 0, `isActive` BOOLEAN NOT NULL DEFAULT true, `createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), `updatedAt` DATETIME(3) NOT NULL, UNIQUE INDEX `StoragePlan_code_key`(`code`), INDEX `StoragePlan_isActive_idx`(`isActive`), INDEX `StoragePlan_order_idx`(`order`), PRIMARY KEY (`id`) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; ``` --- ## 📝 Коммиты ### Conventional Commits Используем формат: `(): ` **Types:** - `feat` - новая функциональность - `fix` - исправление бага - `docs` - изменения в документации - `style` - форматирование, отступы (не CSS) - `refactor` - рефакторинг без изменения функциональности - `perf` - улучшение производительности - `test` - добавление тестов - `chore` - обновление зависимостей, настройки **Scope (опционально):** - `server` - VPS модуль - `storage` - S3 модуль - `blog` - блог модуль - `ticket` - тикеты - `auth` - авторизация - `admin` - админ панель - `frontend` - фронтенд - `backend` - бэкенд **Примеры:** ```bash # Новая функция git commit -m "feat(storage): add custom tariff pricing with per-GB rates" # Исправление бага git commit -m "fix(ticket): auto-unassign operator on user close" # Документация git commit -m "docs: update API endpoints in README" # Рефакторинг git commit -m "refactor(auth): remove any types from middleware" # Множественные изменения git commit -m "feat(blog): add rich text editor - Add react-quill integration - Implement image upload - Add comment moderation" ``` ### Правила коммитов - ✅ Subject в imperative mood ("add" не "added") - ✅ Первая буква строчная - ❌ Точка в конце не ставится - ✅ Разделение логически независимых изменений - ✅ Body коммита для пояснения "почему", если нужно --- ## 🔍 Pull Requests ### Перед созданием PR - [ ] Код соответствует стандартам - [ ] Нет ошибок компиляции - [ ] Проверено локально - [ ] Добавлена документация (если нужно) - [ ] Обновлён CHANGELOG (если существенные изменения) ### Шаблон PR ```markdown ## 📝 Описание Краткое описание изменений. ## 🎯 Тип изменений - [ ] 🐛 Исправление бага - [ ] ✨ Новая функция - [ ] 📝 Документация - [ ] ♻️ Рефакторинг - [ ] ⚡️ Улучшение производительности - [ ] ✅ Тесты ## 🔗 Связанные issues Closes #123 ## 🧪 Как тестировать 1. Шаг 1 2. Шаг 2 3. Ожидаемый результат ## 📸 Скриншоты (если применимо) Добавьте скриншоты UI изменений. ## ✅ Checklist - [ ] Код следует стандартам проекта - [ ] Проведено самотестирование - [ ] Комментарии добавлены для сложных мест - [ ] Документация обновлена - [ ] Нет warnings при компиляции - [ ] Работает локально ## 📌 Дополнительные заметки Любая дополнительная информация. ``` ### Процесс ревью 1. **Автоматические проверки** - должны пройти успешно 2. **Code review** - минимум 1 аппрув от мейнтейнера 3. **Тестирование** - проверка на dev окружении 4. **Мёрдж** - после одобрения ### Работа с замечаниями ```bash # Внесите изменения git add . git commit -m "fix: address review comments" git push origin feature/your-feature ``` --- ## 🏗️ Архитектурные решения ### Модульность Каждый модуль должен быть независимым: ``` backend/src/modules/example/ ├── example.controller.ts # HTTP handlers ├── example.service.ts # Business logic ├── example.routes.ts # Express routes ├── example.types.ts # TypeScript types └── example.utils.ts # Helper functions ``` ### Разделение ответственности ```typescript // ❌ Плохо - всё в контроллере export async function createUser(req: Request, res: Response) { const { email, password } = req.body; const hashedPassword = await bcrypt.hash(password, 10); const user = await prisma.user.create({ data: { email, password: hashedPassword } }); return res.json(user); } // ✅ Хорошо - разделение на слои // controller export async function createUser(req: Request, res: Response) { try { const userData = req.body; const user = await userService.createUser(userData); return res.json(user); } catch (error) { return res.status(400).json({ error: error.message }); } } // service export async function createUser(data: CreateUserInput) { const hashedPassword = await hashPassword(data.password); return await prisma.user.create({ data: { ...data, password: hashedPassword } }); } ``` ### API дизайн ```typescript // ✅ RESTful маршруты GET /api/servers # Список POST /api/servers # Создать GET /api/servers/:id # Один PUT /api/servers/:id # Обновить DELETE /api/servers/:id # Удалить # Действия над ресурсом POST /api/servers/:id/start POST /api/servers/:id/stop POST /api/servers/:id/snapshot # Вложенные ресурсы GET /api/servers/:id/snapshots DELETE /api/servers/:id/snapshots/:snapshotName ``` ### Обработка ошибок ```typescript // utils/errors.ts export class AppError extends Error { constructor( public message: string, public statusCode: number = 500, public isOperational: boolean = true ) { super(message); } } export class NotFoundError extends AppError { constructor(message: string) { super(message, 404); } } export class UnauthorizedError extends AppError { constructor(message: string = 'Unauthorized') { super(message, 401); } } // Использование if (!server) { throw new NotFoundError('Server not found'); } ``` --- ## 🧪 Тестирование ### Ручное тестирование Перед PR проверьте: **Backend:** ```bash # Компиляция без ошибок npm run build # API endpoints работают curl -X POST http://localhost:5000/api/auth/login \ -H "Content-Type: application/json" \ -d '{"email":"test@test.com","password":"password"}' ``` **Frontend:** ```bash # Сборка без ошибок npm run build # Линтинг без warnings npm run lint # Проверка в браузере npm run dev ``` ### Тестовые сценарии Для новых функций опишите сценарии: ```markdown ## Тестовые сценарии ### Создание сервера 1. Войти как пользователь 2. Открыть `/dashboard/servers` 3. Нажать "Создать сервер" 4. Выбрать OS: Ubuntu 22.04 5. Выбрать тариф: Standard 6. Нажать "Создать" 7. Ожидать: сервер создаётся, статус "creating" 8. Через 2-3 минуты: статус "running" ### Ошибки 1. Без авторизации → 401 Unauthorized 2. Недостаточно средств → 400 Bad Request 3. Несуществующий OS → 404 Not Found ``` --- ## 📚 Документация ### Комментарии в коде ```typescript /** * Создаёт новый VPS сервер в Proxmox * * @param userId - ID пользователя-владельца * @param osId - ID операционной системы * @param tariffId - ID тарифного плана * @returns Объект созданного сервера * @throws {Error} Если недостаточно средств или Proxmox недоступен */ export async function createServer( userId: number, osId: number, tariffId: number ): Promise { // Реализация } ``` ### API документация При добавлении endpoint обновите README: ```markdown #### Серверы (VPS) ```http POST /api/servers/:id/snapshot Authorization: Bearer TOKEN Content-Type: application/json { "name": "backup-before-update" } ``` **Response:** ```json { "snapshot": { "name": "backup-before-update", "createdAt": "2025-11-26T10:00:00Z" } } ``` ``` ### Изменения в БД При добавлении миграций опишите в CHANGELOG: ```markdown ## [Unreleased] ### Added - Поле `pricePerGb` в модель `StoragePlan` для кастомных тарифов - Индекс на `StoragePlan.isActive` для быстрой фильтрации ### Changed - Функция `serializePlan` теперь возвращает поля per-GB ### Migration ```bash npx prisma migrate deploy npx prisma generate ``` ``` --- ## 🎯 Приоритеты разработки ### High Priority 🔴 - Критические баги безопасности - Потеря данных - Падение сервиса - Блокирующие ошибки ### Medium Priority 🟡 - Новые функции из роадмапа - Улучшения UX - Оптимизация производительности - Рефакторинг сложных участков ### Low Priority 🟢 - Косметические исправления - Документация - Code style улучшения - Nice-to-have функции --- ## 🔒 Безопасность ### Уязвимости Если нашли уязвимость безопасности: 1. **НЕ создавайте публичный issue** 2. Напишите на security@ospab.host 3. Опишите подробно проблему 4. Предложите решение (если есть) ### Практики безопасности ```typescript // ✅ Хорошо const hashedPassword = await bcrypt.hash(password, 10); // Проверка владельца ресурса const server = await prisma.server.findFirst({ where: { id: serverId, userId } // Фильтр по userId }); // Санитизация ввода const sanitizedName = name.trim().slice(0, 100); // ❌ Плохо const server = await prisma.server.findUnique({ where: { id: serverId } // Любой может получить любой сервер }); ``` --- ## 🌍 Интернационализация В будущем планируется поддержка нескольких языков. При добавлении текста: ```typescript // ✅ Хорошо - готово к i18n const messages = { server_created: 'Server created successfully', server_error: 'Failed to create server', }; // ❌ Плохо - хардкод return res.json({ message: 'Сервер создан успешно' }); ``` --- ## 📞 Получение помощи ### Где задать вопрос - **GitHub Discussions** - общие вопросы - **GitHub Issues** - баги и фичи - **Telegram** - [@ospab](https://t.me/ospab) - быстрая помощь ### Как задать хороший вопрос ```markdown ## Описание проблемы Краткое описание что не работает. ## Шаги для воспроизведения 1. Шаг 1 2. Шаг 2 3. Результат ## Ожидаемое поведение Что должно было произойти. ## Окружение - OS: Windows 11 - Node.js: 18.17.0 - Browser: Chrome 120 - Backend: running on localhost:5000 ## Логи/ошибки ``` Вставьте логи или скриншоты ошибок ``` ## Что уже пробовали - Перезапустили сервер - Очистили npm cache ``` --- ## 📅 Релизный цикл ### Версионирование Следуем [Semantic Versioning](https://semver.org/): - **MAJOR** (8.x.x) - несовместимые изменения API - **MINOR** (x.1.x) - новая функциональность, обратно совместимая - **PATCH** (x.x.1) - исправления багов ### Ветки - `main` - стабильная production версия - `develop` - разработка следующего релиза - `feature/*` - новые функции - `fix/*` - исправления --- ## ✨ Лучшие практики ### DRY (Don't Repeat Yourself) ```typescript // ❌ Плохо const user1 = await prisma.user.findUnique({ where: { id: 1 } }); const user2 = await prisma.user.findUnique({ where: { id: 2 } }); const user3 = await prisma.user.findUnique({ where: { id: 3 } }); // ✅ Хорошо const userIds = [1, 2, 3]; const users = await prisma.user.findMany({ where: { id: { in: userIds } } }); ``` ### Раннее возвращение ```typescript // ✅ Хорошо function processUser(user: User | null) { if (!user) { return null; } if (!user.isActive) { return null; } // Основная логика return user.name; } // ❌ Плохо function processUser(user: User | null) { if (user) { if (user.isActive) { // Основная логика return user.name; } } return null; } ``` ### Константы вместо магических чисел ```typescript // ✅ Хорошо const MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB const SESSION_TTL_MINUTES = 20; const DEFAULT_PAGE_SIZE = 10; if (fileSize > MAX_FILE_SIZE) { throw new Error('File too large'); } // ❌ Плохо if (fileSize > 10485760) { // Что это за число? throw new Error('File too large'); } ``` --- ## 🎓 Обучающие ресурсы ### TypeScript - [Official Docs](https://www.typescriptlang.org/docs/) - [TypeScript Deep Dive](https://basarat.gitbook.io/typescript/) ### React - [Official Docs](https://react.dev/) - [React TypeScript Cheatsheet](https://react-typescript-cheatsheet.netlify.app/) ### Prisma - [Official Docs](https://www.prisma.io/docs/) - [Prisma Best Practices](https://www.prisma.io/docs/guides/performance-and-optimization) ### Express - [Official Docs](https://expressjs.com/) - [Express Best Practices](https://expressjs.com/en/advanced/best-practice-performance.html) --- ## 🏆 Признание вкладчиков Все участники будут упомянуты в: - CHANGELOG.md - Contributors страница на сайте - Release notes Спасибо за вклад в развитие Ospabhost! 🚀 --- **Последнее обновление:** 26 ноября 2025 **Версия:** 1.0.0