Files
ospab.host/CONTRIBUTING.md
2025-11-23 14:35:16 +03:00

14 KiB
Raw Blame History

Contributing to Ospab Host 8.1

Спасибо за интерес к проекту! Мы рады любому вкладу в развитие платформы.

Оглавление

  1. Кодекс поведения
  2. Как внести вклад
  3. Процесс разработки
  4. Стандарты кода
  5. Коммиты и Pull Requests
  6. Тестирование
  7. Документация

Кодекс поведения

Наши обязательства

Мы стремимся создать открытое и дружелюбное сообщество. Мы обязуемся:

  • Использовать уважительный и профессиональный язык
  • Уважать различные точки зрения и опыт
  • Принимать конструктивную критику
  • Фокусироваться на лучшем решении для сообщества
  • Проявлять эмпатию к другим участникам

Неприемлемое поведение

  • Оскорбительные комментарии
  • Домогательства в любой форме
  • Публикация личной информации без разрешения
  • Троллинг и провокации
  • Другое неэтичное поведение

Как внести вклад

Сообщение об ошибках

Перед созданием issue убедитесь:

  1. Ошибка воспроизводится на последней версии
  2. Похожего issue еще нет
  3. У вас есть вся необходимая информация

Шаблон сообщения об ошибке:

## Описание
Краткое описание ошибки

## Шаги воспроизведения
1. Перейти на...
2. Нажать на...
3. Увидеть ошибку...

## Ожидаемое поведение
Что должно произойти

## Фактическое поведение
Что произошло на самом деле

## Окружение
- OS: [e.g. Ubuntu 22.04]
- Node.js: [e.g. 18.19.0]
- Browser: [e.g. Chrome 120]
- Version: [e.g. 8.1.0]

## Скриншоты
Если применимо

## Дополнительная информация
Логи, stack traces и т.д.

Предложение улучшений

Шаблон feature request:

## Проблема
Какую проблему решает это улучшение?

## Предлагаемое решение
Подробное описание решения

## Альтернативы
Рассмотренные альтернативные решения

## Дополнительный контекст
Скриншоты, примеры, ссылки

Pull Requests

  1. Fork репозитория
  2. Создайте feature ветку (git checkout -b feature/AmazingFeature)
  3. Зафиксируйте изменения (git commit -m 'Add some AmazingFeature')
  4. Push в ветку (git push origin feature/AmazingFeature)
  5. Откройте Pull Request

Процесс разработки

Настройка окружения

  1. Установка зависимостей
# Клонируйте репозиторий
git clone https://github.com/Ospab/ospabhost8.1.git
cd ospabhost8.1/ospabhost

# Backend
cd backend
npm install
npx prisma generate

# Frontend
cd ../frontend
npm install
  1. Настройка окружения
# Backend .env
cd backend
cp .env.example .env
# Заполните необходимые переменные
  1. Запуск в режиме разработки
# Terminal 1 - Backend
cd backend
npm run dev

# Terminal 2 - Frontend
cd frontend
npm run dev

Структура веток

  • main - стабильная production ветка
  • develop - активная разработка
  • feature/* - новые функции
  • bugfix/* - исправление ошибок
  • hotfix/* - срочные исправления для production

Git Flow

main
  └─ develop
       ├─ feature/new-feature
       ├─ bugfix/fix-something
       └─ hotfix/urgent-fix

Стандарты кода

TypeScript/JavaScript

Основные правила:

  • Используйте TypeScript для типобезопасности
  • Избегайте any, используйте конкретные типы
  • Функции должны быть чистыми где возможно
  • Один компонент/функция = одна ответственность
  • Максимальная длина файла - 300 строк

Именование:

// Константы - UPPER_SNAKE_CASE
const MAX_RETRIES = 3;
const API_BASE_URL = 'https://api.example.com';

// Переменные и функции - camelCase
const userData = getUserData();
function calculateTotal(items) { }

// Классы и компоненты - PascalCase
class UserService { }
const LoginPage = () => { };

// Приватные поля - с underscore
class Example {
  private _internalState: string;
}

// Boolean переменные - is/has/should префиксы
const isLoading = true;
const hasPermission = false;
const shouldUpdate = true;

Комментарии:

// Плохо - очевидное
const price = 100; // Устанавливаем цену

// Хорошо - объясняем "почему"
// Используем кеш для снижения нагрузки на API
const cachedData = getFromCache();

/**
 * Вычисляет финальную цену с учетом скидок и налогов
 * @param basePrice Базовая цена товара
 * @param discountPercent Процент скидки (0-100)
 * @returns Финальная цена
 */
function calculateFinalPrice(basePrice: number, discountPercent: number): number {
  // Реализация
}

React компоненты

Структура компонента:

import React, { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';

// 1. Типы
interface Props {
  userId: number;
  onUpdate?: () => void;
}

// 2. Константы
const DEFAULT_TIMEOUT = 5000;

// 3. Компонент
const UserProfile: React.FC<Props> = ({ userId, onUpdate }) => {
  // 3.1 Hooks
  const navigate = useNavigate();
  const [loading, setLoading] = useState(false);
  
  // 3.2 Effects
  useEffect(() => {
    fetchUserData();
  }, [userId]);
  
  // 3.3 Handlers
  const handleUpdate = async () => {
    // Логика
  };
  
  // 3.4 Render helpers
  if (loading) {
    return <div>Loading...</div>;
  }
  
  // 3.5 Main render
  return (
    <div className="user-profile">
      {/* JSX */}
    </div>
  );
};

// 4. Export
export default UserProfile;

Хуки правила:

  • Используйте хуки только на верхнем уровне
  • Создавайте custom hooks для повторяющейся логики
  • Мемоизируйте тяжелые вычисления (useMemo)
  • Оптимизируйте callbacks (useCallback)

CSS/Tailwind

Tailwind классы:

// Плохо - слишком длинный inline
<div className="flex items-center justify-between px-4 py-2 bg-blue-500 text-white rounded-lg shadow-md hover:bg-blue-600 transition-colors duration-200">

// Хорошо - группировка или extracted component
const buttonClasses = "flex items-center justify-between px-4 py-2 bg-blue-500 text-white rounded-lg shadow-md hover:bg-blue-600 transition-colors duration-200";
<div className={buttonClasses}>

// Еще лучше - отдельный компонент
<Button variant="primary" size="md">

Backend API

Структура endpoints:

// Плохо
app.get('/get-users', ...)
app.post('/create-user', ...)

// Хорошо - RESTful
app.get('/api/users', ...)
app.post('/api/users', ...)
app.get('/api/users/:id', ...)
app.put('/api/users/:id', ...)
app.delete('/api/users/:id', ...)

Обработка ошибок:

export async function getUserProfile(req: Request, res: Response) {
  try {
    const userId = req.user?.id;
    
    if (!userId) {
      return res.status(401).json({ error: 'Не авторизован' });
    }
    
    const user = await prisma.user.findUnique({
      where: { id: userId }
    });
    
    if (!user) {
      return res.status(404).json({ error: 'Пользователь не найден' });
    }
    
    res.json({ user });
  } catch (error) {
    console.error('Ошибка получения профиля:', error);
    res.status(500).json({ error: 'Внутренняя ошибка сервера' });
  }
}

Коммиты и Pull Requests

Commit сообщения

Используйте Conventional Commits:

<type>(<scope>): <subject>

<body>

<footer>

Типы:

  • feat: новая функция
  • fix: исправление ошибки
  • docs: изменения в документации
  • style: форматирование кода
  • refactor: рефакторинг без изменения функционала
  • perf: улучшение производительности
  • test: добавление тестов
  • chore: обновление зависимостей, конфигурации

Примеры:

# Хорошие коммиты
feat(auth): add QR code authentication
fix(server): resolve memory leak in WebSocket
docs(api): update API endpoint documentation
refactor(dashboard): extract sessions component
perf(backend): optimize database queries

# Плохие коммиты
update files
fix bug
changes
wip

Pull Request

Чеклист перед PR:

  • Код соответствует style guide
  • Все тесты проходят
  • Добавлена документация
  • Нет console.log в production коде
  • Обновлен CHANGELOG (если применимо)
  • Screenshots для UI изменений
  • Проверено на разных браузерах

Шаблон PR:

## Описание
Краткое описание изменений

## Тип изменений
- [ ] Bug fix
- [ ] New feature
- [ ] Breaking change
- [ ] Documentation update

## Связанные issues
Closes #123

## Тестирование
Как тестировались изменения

## Screenshots
Если применимо

## Checklist
- [ ] Код следует style guide
- [ ] Self-review выполнен
- [ ] Комментарии добавлены где нужно
- [ ] Документация обновлена
- [ ] Нет новых warnings
- [ ] Тесты добавлены

Тестирование

Unit тесты

// user.service.test.ts
describe('UserService', () => {
  describe('createUser', () => {
    it('should create user with valid data', async () => {
      const userData = { email: 'test@example.com', password: 'Password123' };
      const user = await UserService.createUser(userData);
      
      expect(user.email).toBe(userData.email);
      expect(user.password).not.toBe(userData.password); // hashed
    });
    
    it('should throw error for duplicate email', async () => {
      const userData = { email: 'existing@example.com', password: 'Pass123' };
      
      await expect(UserService.createUser(userData)).rejects.toThrow('Email уже используется');
    });
  });
});

Integration тесты

// auth.integration.test.ts
describe('POST /api/auth/login', () => {
  it('should return token for valid credentials', async () => {
    const response = await request(app)
      .post('/api/auth/login')
      .send({ email: 'user@test.com', password: 'password' })
      .expect(200);
    
    expect(response.body).toHaveProperty('token');
    expect(response.body).toHaveProperty('user');
  });
});

Документация

Код документация

/**
 * Создает новую сессию для пользователя
 * 
 * @param userId - ID пользователя
 * @param req - Express request объект (для получения IP и User-Agent)
 * @param expiresInDays - Количество дней до истечения сессии (по умолчанию 30)
 * 
 * @returns Объект с токеном и информацией о сессии
 * 
 * @throws {Error} Если превышен лимит активных сессий (10)
 * 
 * @example
 * ```typescript
 * const { token, session } = await createSession(user.id, req);
 * res.json({ token });
 * ```
 */
export async function createSession(
  userId: number,
  req: Request,
  expiresInDays: number = 30
): Promise<{ token: string; session: Session }> {
  // Реализация
}

README обновления

При добавлении новых функций обновите:

  1. Раздел "Features" с описанием
  2. API documentation если добавлены endpoints
  3. Configuration guide если нужны новые env переменные
  4. Troubleshooting если есть известные проблемы

Вопросы?

  • Создайте issue с вопросом
  • Напишите в Telegram: @ospab_support
  • Email: support@ospab.host

Спасибо за ваш вклад в Ospab Host!