diff --git a/CHANGELOG.md b/CHANGELOG.md index 4cc1396..5a51ca5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,43 @@ # πŸ“ Π˜ΡΡ‚ΠΎΡ€ΠΈΡ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠΉ MC Panel +## ВСрсия 2.1 - Π›ΠΈΡ‡Π½Ρ‹ΠΉ ΠΊΠ°Π±ΠΈΠ½Π΅Ρ‚ (14.01.2026) + +### ✨ НовыС возмоТности + +#### πŸ‘€ Π›ΠΈΡ‡Π½Ρ‹ΠΉ ΠΊΠ°Π±ΠΈΠ½Π΅Ρ‚ +- Кнопка "Π›ΠΈΡ‡Π½Ρ‹ΠΉ ΠΊΠ°Π±ΠΈΠ½Π΅Ρ‚" Π² header рядом с "Π’ΠΈΠΊΠ΅Ρ‚Ρ‹" +- Π’Ρ€ΠΈ Π²ΠΊΠ»Π°Π΄ΠΊΠΈ: ΠžΠ±Π·ΠΎΡ€, Имя ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ, ΠŸΠ°Ρ€ΠΎΠ»ΡŒ +- Бтатистика профиля (сСрвСры, Ρ‚ΠΈΠΊΠ΅Ρ‚Ρ‹, Ρ€ΠΎΠ»ΡŒ) +- Бписок своих сСрвСров +- ИзмСнСниС ΠΈΠΌΠ΅Π½ΠΈ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ с ΠΏΠΎΠ΄Ρ‚Π²Π΅Ρ€ΠΆΠ΄Π΅Π½ΠΈΠ΅ΠΌ ΠΏΠ°Ρ€ΠΎΠ»Π΅ΠΌ +- ИзмСнСниС пароля с ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠΎΠΉ +- Показ/скрытиС ΠΏΠ°Ρ€ΠΎΠ»Π΅ΠΉ Π² Ρ„ΠΎΡ€ΠΌΠ°Ρ… + +#### πŸ” Π‘Π΅Π·ΠΎΠΏΠ°ΡΠ½ΠΎΡΡ‚ΡŒ +- ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° ΡƒΠ½ΠΈΠΊΠ°Π»ΡŒΠ½ΠΎΡΡ‚ΠΈ ΠΈΠΌΠ΅Π½ΠΈ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ +- АвтоматичСскоС ΠΎΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΈΠ΅ Π²Π»Π°Π΄Π΅Π»ΡŒΡ†Π΅Π² сСрвСров ΠΏΡ€ΠΈ смСнС ΠΈΠΌΠ΅Π½ΠΈ +- АвтоматичСскоС ΠΎΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΈΠ΅ доступов ΠΊ сСрвСрам +- Новый JWT Ρ‚ΠΎΠΊΠ΅Π½ ΠΏΡ€ΠΈ смСнС ΠΈΠΌΠ΅Π½ΠΈ +- Π₯Π΅ΡˆΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ ΠΏΠ°Ρ€ΠΎΠ»Π΅ΠΉ (bcrypt) + +#### πŸ“Š Бтатистика профиля +- ΠžΠ±Ρ‰Π΅Π΅ количСство сСрвСров +- Мои сСрвСры (Π²Π»Π°Π΄Π΅Π»Π΅Ρ†) +- ДоступныС сСрвСры +- Бтатистика ΠΏΠΎ Ρ‚ΠΈΠΊΠ΅Ρ‚Π°ΠΌ (всСго, Π½Π° рассмотрСнии, Π² Ρ€Π°Π±ΠΎΡ‚Π΅, Π·Π°ΠΊΡ€Ρ‹Ρ‚ΠΎ) +- Π˜Π½Ρ„ΠΎΡ€ΠΌΠ°Ρ†ΠΈΡ ΠΎ Ρ€ΠΎΠ»ΠΈ + +### πŸ“ НовыС Ρ„Π°ΠΉΠ»Ρ‹ +- `frontend/src/components/Profile.jsx` - ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚ Π»ΠΈΡ‡Π½ΠΎΠ³ΠΎ ΠΊΠ°Π±ΠΈΠ½Π΅Ρ‚Π° +- `PROFILE_SYSTEM.md` - докумСнтация Π»ΠΈΡ‡Π½ΠΎΠ³ΠΎ ΠΊΠ°Π±ΠΈΠ½Π΅Ρ‚Π° + +### πŸ”§ API Endpoints +- `PUT /api/profile/username` - ΠΈΠ·ΠΌΠ΅Π½ΠΈΡ‚ΡŒ имя ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ +- `PUT /api/profile/password` - ΠΈΠ·ΠΌΠ΅Π½ΠΈΡ‚ΡŒ ΠΏΠ°Ρ€ΠΎΠ»ΡŒ +- `GET /api/profile/stats` - ΠΏΠΎΠ»ΡƒΡ‡ΠΈΡ‚ΡŒ статистику профиля + +--- + ## ВСрсия 2.0 - БистСма Ρ‚ΠΈΠΊΠ΅Ρ‚ΠΎΠ² (14.01.2026) ### ✨ НовыС возмоТности diff --git a/PROFILE_SYSTEM.md b/PROFILE_SYSTEM.md new file mode 100644 index 0000000..148cf10 --- /dev/null +++ b/PROFILE_SYSTEM.md @@ -0,0 +1,228 @@ +# πŸ‘€ БистСма Π»ΠΈΡ‡Π½ΠΎΠ³ΠΎ ΠΊΠ°Π±ΠΈΠ½Π΅Ρ‚Π° + +## Π§Ρ‚ΠΎ Π΄ΠΎΠ±Π°Π²Π»Π΅Π½ΠΎ + +### βœ… НовыС возмоТности + +1. **Π›ΠΈΡ‡Π½Ρ‹ΠΉ ΠΊΠ°Π±ΠΈΠ½Π΅Ρ‚** - полноцСнная систСма управлСния ΠΏΡ€ΠΎΡ„ΠΈΠ»Π΅ΠΌ +2. **Π’Ρ€ΠΈ Π²ΠΊΠ»Π°Π΄ΠΊΠΈ**: + - πŸ“Š **ΠžΠ±Π·ΠΎΡ€** - статистика ΠΈ информация ΠΎ ΠΏΡ€ΠΎΡ„ΠΈΠ»Π΅ + - πŸ‘€ **Имя ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ** - ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠ΅ ΠΈΠΌΠ΅Π½ΠΈ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ + - πŸ”’ **ΠŸΠ°Ρ€ΠΎΠ»ΡŒ** - ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠ΅ пароля + +3. **Кнопка "Π›ΠΈΡ‡Π½Ρ‹ΠΉ ΠΊΠ°Π±ΠΈΠ½Π΅Ρ‚"** Π² header рядом с ΠΊΠ½ΠΎΠΏΠΊΠΎΠΉ "Π’ΠΈΠΊΠ΅Ρ‚Ρ‹" + +### πŸ“Š Π’ΠΊΠ»Π°Π΄ΠΊΠ° "ΠžΠ±Π·ΠΎΡ€" + +#### Π˜Π½Ρ„ΠΎΡ€ΠΌΠ°Ρ†ΠΈΡ ΠΎ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Π΅ +- Имя ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ +- Роль (Администратор, Π’Π΅Ρ…. ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΊΠ°, ΠŸΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒ) +- Π¦Π²Π΅Ρ‚Π½ΠΎΠΉ Π±Π΅ΠΉΠ΄ΠΆ Ρ€ΠΎΠ»ΠΈ + +#### Бтатистика +- **Π‘Π΅Ρ€Π²Π΅Ρ€Ρ‹**: + - ВсСго сСрвСров + - Мои сСрвСры (Π²Π»Π°Π΄Π΅Π»Π΅Ρ†) + - ДоступныС сСрвСры + +- **Π’ΠΈΠΊΠ΅Ρ‚Ρ‹**: + - ВсСго Ρ‚ΠΈΠΊΠ΅Ρ‚ΠΎΠ² + - На рассмотрСнии + - Π’ Ρ€Π°Π±ΠΎΡ‚Π΅ + - Π—Π°ΠΊΡ€Ρ‹Ρ‚ΠΎ + +- **Роль**: + - НазваниС Ρ€ΠΎΠ»ΠΈ + - ОписаниС ΠΏΡ€Π°Π² + +#### Бписок сСрвСров +- ΠžΡ‚ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅ всСх сСрвСров ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ +- НазваниС ΠΈ ID сСрвСра +- ΠšΡ€Π°ΡΠΈΠ²Ρ‹Π΅ ΠΊΠ°Ρ€Ρ‚ΠΎΡ‡ΠΊΠΈ + +### πŸ‘€ Π’ΠΊΠ»Π°Π΄ΠΊΠ° "Имя ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ" + +#### ВозмоТности +- ΠŸΡ€ΠΎΡΠΌΠΎΡ‚Ρ€ Ρ‚Π΅ΠΊΡƒΡ‰Π΅Π³ΠΎ ΠΈΠΌΠ΅Π½ΠΈ +- Π’Π²ΠΎΠ΄ Π½ΠΎΠ²ΠΎΠ³ΠΎ ΠΈΠΌΠ΅Π½ΠΈ (ΠΌΠΈΠ½ΠΈΠΌΡƒΠΌ 3 символа) +- ΠŸΠΎΠ΄Ρ‚Π²Π΅Ρ€ΠΆΠ΄Π΅Π½ΠΈΠ΅ ΠΏΠ°Ρ€ΠΎΠ»Π΅ΠΌ +- АвтоматичСский ΠΏΠ΅Ρ€Π΅Π»ΠΎΠ³ΠΈΠ½ с Π½ΠΎΠ²Ρ‹ΠΌ ΠΈΠΌΠ΅Π½Π΅ΠΌ + +#### Π‘Π΅Π·ΠΎΠΏΠ°ΡΠ½ΠΎΡΡ‚ΡŒ +- ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° ΡƒΠ½ΠΈΠΊΠ°Π»ΡŒΠ½ΠΎΡΡ‚ΠΈ ΠΈΠΌΠ΅Π½ΠΈ +- ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° пароля +- ОбновлСниС Π²Π»Π°Π΄Π΅Π»ΡŒΡ†Π΅Π² сСрвСров +- ОбновлСниС доступов ΠΊ сСрвСрам +- Новый JWT Ρ‚ΠΎΠΊΠ΅Π½ + +### πŸ”’ Π’ΠΊΠ»Π°Π΄ΠΊΠ° "ΠŸΠ°Ρ€ΠΎΠ»ΡŒ" + +#### ВозмоТности +- Π’Π²ΠΎΠ΄ Ρ‚Π΅ΠΊΡƒΡ‰Π΅Π³ΠΎ пароля +- Π’Π²ΠΎΠ΄ Π½ΠΎΠ²ΠΎΠ³ΠΎ пароля (ΠΌΠΈΠ½ΠΈΠΌΡƒΠΌ 6 символов) +- ΠŸΠΎΠ΄Ρ‚Π²Π΅Ρ€ΠΆΠ΄Π΅Π½ΠΈΠ΅ Π½ΠΎΠ²ΠΎΠ³ΠΎ пароля +- Показ/скрытиС ΠΏΠ°Ρ€ΠΎΠ»Π΅ΠΉ + +#### Π‘Π΅Π·ΠΎΠΏΠ°ΡΠ½ΠΎΡΡ‚ΡŒ +- ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° Ρ‚Π΅ΠΊΡƒΡ‰Π΅Π³ΠΎ пароля +- ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° совпадСния Π½ΠΎΠ²Ρ‹Ρ… ΠΏΠ°Ρ€ΠΎΠ»Π΅ΠΉ +- Π₯Π΅ΡˆΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ пароля (bcrypt) + +## πŸš€ Как ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ + +### ΠžΡ‚ΠΊΡ€Ρ‹Ρ‚ΠΈΠ΅ Π»ΠΈΡ‡Π½ΠΎΠ³ΠΎ ΠΊΠ°Π±ΠΈΠ½Π΅Ρ‚Π° +1. НаТмитС ΠΊΠ½ΠΎΠΏΠΊΡƒ "Π›ΠΈΡ‡Π½Ρ‹ΠΉ ΠΊΠ°Π±ΠΈΠ½Π΅Ρ‚" Π² header +2. ΠžΡ‚ΠΊΡ€ΠΎΠ΅Ρ‚ΡΡ страница с трСмя Π²ΠΊΠ»Π°Π΄ΠΊΠ°ΠΌΠΈ + +### ΠŸΡ€ΠΎΡΠΌΠΎΡ‚Ρ€ статистики +1. ΠžΡ‚ΠΊΡ€ΠΎΠΉΡ‚Π΅ Π²ΠΊΠ»Π°Π΄ΠΊΡƒ "ΠžΠ±Π·ΠΎΡ€" +2. ΠŸΠΎΡΠΌΠΎΡ‚Ρ€ΠΈΡ‚Π΅ ΠΈΠ½Ρ„ΠΎΡ€ΠΌΠ°Ρ†ΠΈΡŽ ΠΎ ΠΏΡ€ΠΎΡ„ΠΈΠ»Π΅ +3. ΠŸΠΎΡΠΌΠΎΡ‚Ρ€ΠΈΡ‚Π΅ статистику ΠΏΠΎ сСрвСрам ΠΈ Ρ‚ΠΈΠΊΠ΅Ρ‚Π°ΠΌ +4. ΠŸΠΎΡΠΌΠΎΡ‚Ρ€ΠΈΡ‚Π΅ список своих сСрвСров + +### ИзмСнСниС ΠΈΠΌΠ΅Π½ΠΈ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ +1. ΠžΡ‚ΠΊΡ€ΠΎΠΉΡ‚Π΅ Π²ΠΊΠ»Π°Π΄ΠΊΡƒ "Имя ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ" +2. Π’Π²Π΅Π΄ΠΈΡ‚Π΅ Π½ΠΎΠ²ΠΎΠ΅ имя (ΠΌΠΈΠ½ΠΈΠΌΡƒΠΌ 3 символа) +3. Π’Π²Π΅Π΄ΠΈΡ‚Π΅ Ρ‚Π΅ΠΊΡƒΡ‰ΠΈΠΉ ΠΏΠ°Ρ€ΠΎΠ»ΡŒ для подтвСрТдСния +4. НаТмитС "Π˜Π·ΠΌΠ΅Π½ΠΈΡ‚ΡŒ имя ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ" +5. Π’Ρ‹ Π±ΡƒΠ΄Π΅Ρ‚Π΅ автоматичСски ΠΏΠ΅Ρ€Π΅Π»ΠΎΠ³ΠΈΠ½Π΅Π½Ρ‹ + +⚠️ **Π’Π°ΠΆΠ½ΠΎ**: ПослС измСнСния ΠΈΠΌΠ΅Π½ΠΈ: +- ΠžΠ±Π½ΠΎΠ²ΡΡ‚ΡΡ всС сСрвСры, Π³Π΄Π΅ Π²Ρ‹ Π²Π»Π°Π΄Π΅Π»Π΅Ρ† +- ΠžΠ±Π½ΠΎΠ²ΡΡ‚ΡΡ всС доступы ΠΊ сСрвСрам +- Π’Ρ‹ ΠΏΠΎΠ»ΡƒΡ‡ΠΈΡ‚Π΅ Π½ΠΎΠ²Ρ‹ΠΉ Ρ‚ΠΎΠΊΠ΅Π½ Π°Π²Ρ‚ΠΎΡ€ΠΈΠ·Π°Ρ†ΠΈΠΈ + +### ИзмСнСниС пароля +1. ΠžΡ‚ΠΊΡ€ΠΎΠΉΡ‚Π΅ Π²ΠΊΠ»Π°Π΄ΠΊΡƒ "ΠŸΠ°Ρ€ΠΎΠ»ΡŒ" +2. Π’Π²Π΅Π΄ΠΈΡ‚Π΅ Ρ‚Π΅ΠΊΡƒΡ‰ΠΈΠΉ ΠΏΠ°Ρ€ΠΎΠ»ΡŒ +3. Π’Π²Π΅Π΄ΠΈΡ‚Π΅ Π½ΠΎΠ²Ρ‹ΠΉ ΠΏΠ°Ρ€ΠΎΠ»ΡŒ (ΠΌΠΈΠ½ΠΈΠΌΡƒΠΌ 6 символов) +4. ΠŸΠΎΠ΄Ρ‚Π²Π΅Ρ€Π΄ΠΈΡ‚Π΅ Π½ΠΎΠ²Ρ‹ΠΉ ΠΏΠ°Ρ€ΠΎΠ»ΡŒ +5. НаТмитС "Π˜Π·ΠΌΠ΅Π½ΠΈΡ‚ΡŒ ΠΏΠ°Ρ€ΠΎΠ»ΡŒ" + +⚠️ **Π’Π°ΠΆΠ½ΠΎ**: ПослС измСнСния пароля ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠΉΡ‚Π΅ Π½ΠΎΠ²Ρ‹ΠΉ ΠΏΠ°Ρ€ΠΎΠ»ΡŒ для Π²Ρ…ΠΎΠ΄Π°. + +## πŸ“ НовыС Ρ„Π°ΠΉΠ»Ρ‹ + +### Backend +- Π”ΠΎΠ±Π°Π²Π»Π΅Π½Ρ‹ endpoints Π² `backend/main.py`: + - `PUT /api/profile/username` - ΠΈΠ·ΠΌΠ΅Π½ΠΈΡ‚ΡŒ имя ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ + - `PUT /api/profile/password` - ΠΈΠ·ΠΌΠ΅Π½ΠΈΡ‚ΡŒ ΠΏΠ°Ρ€ΠΎΠ»ΡŒ + - `GET /api/profile/stats` - ΠΏΠΎΠ»ΡƒΡ‡ΠΈΡ‚ΡŒ статистику профиля + +### Frontend +- `frontend/src/components/Profile.jsx` - ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚ Π»ΠΈΡ‡Π½ΠΎΠ³ΠΎ ΠΊΠ°Π±ΠΈΠ½Π΅Ρ‚Π° + +## 🎨 Π˜Π½Ρ‚Π΅Ρ€Ρ„Π΅ΠΉΡ + +### Π’ΠΊΠ»Π°Π΄ΠΊΠΈ +- Π‘ΠΎΠ²Ρ€Π΅ΠΌΠ΅Π½Π½Ρ‹ΠΉ Π΄ΠΈΠ·Π°ΠΉΠ½ с Π²ΠΊΠ»Π°Π΄ΠΊΠ°ΠΌΠΈ +- ΠŸΠ»Π°Π²Π½Ρ‹Π΅ ΠΏΠ΅Ρ€Π΅Ρ…ΠΎΠ΄Ρ‹ ΠΌΠ΅ΠΆΠ΄Ρƒ Π²ΠΊΠ»Π°Π΄ΠΊΠ°ΠΌΠΈ +- Адаптивный Π΄ΠΈΠ·Π°ΠΉΠ½ + +### ΠšΠ°Ρ€Ρ‚ΠΎΡ‡ΠΊΠΈ статистики +- Π¦Π²Π΅Ρ‚Π½Ρ‹Π΅ ΠΈΠΊΠΎΠ½ΠΊΠΈ +- ЧисловыС ΠΏΠΎΠΊΠ°Π·Π°Ρ‚Π΅Π»ΠΈ +- Π”Π΅Ρ‚Π°Π»ΡŒΠ½Π°Ρ информация + +### Π€ΠΎΡ€ΠΌΡ‹ +- Валидация ΠΏΠΎΠ»Π΅ΠΉ +- Показ/скрытиС ΠΏΠ°Ρ€ΠΎΠ»Π΅ΠΉ +- ΠŸΡ€Π΅Π΄ΡƒΠΏΡ€Π΅ΠΆΠ΄Π΅Π½ΠΈΡ ΠΎ послСдствиях +- Кнопки с ΠΈΠ½Π΄ΠΈΠΊΠ°Ρ†ΠΈΠ΅ΠΉ Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠΈ + +## πŸ”§ ВСхничСскиС Π΄Π΅Ρ‚Π°Π»ΠΈ + +### API Endpoints + +#### PUT /api/profile/username +Π˜Π·ΠΌΠ΅Π½ΠΈΡ‚ΡŒ имя ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ + +**Request:** +```json +{ + "new_username": "NewUsername", + "password": "current_password" +} +``` + +**Response:** +```json +{ + "message": "Имя ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΎ", + "access_token": "new_jwt_token", + "username": "NewUsername" +} +``` + +#### PUT /api/profile/password +Π˜Π·ΠΌΠ΅Π½ΠΈΡ‚ΡŒ ΠΏΠ°Ρ€ΠΎΠ»ΡŒ + +**Request:** +```json +{ + "old_password": "old_password", + "new_password": "new_password" +} +``` + +**Response:** +```json +{ + "message": "ΠŸΠ°Ρ€ΠΎΠ»ΡŒ ΠΈΠ·ΠΌΠ΅Π½Ρ‘Π½" +} +``` + +#### GET /api/profile/stats +ΠŸΠΎΠ»ΡƒΡ‡ΠΈΡ‚ΡŒ статистику профиля + +**Response:** +```json +{ + "username": "username", + "role": "user", + "owned_servers": [ + { + "name": "server1", + "displayName": "My Server" + } + ], + "accessible_servers": [], + "tickets": { + "total": 5, + "pending": 2, + "in_progress": 1, + "closed": 2 + }, + "total_servers": 1 +} +``` + +### Π‘Π΅Π·ΠΎΠΏΠ°ΡΠ½ΠΎΡΡ‚ΡŒ + +#### ИзмСнСниС ΠΈΠΌΠ΅Π½ΠΈ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ +1. ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° пароля +2. ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° ΡƒΠ½ΠΈΠΊΠ°Π»ΡŒΠ½ΠΎΡΡ‚ΠΈ Π½ΠΎΠ²ΠΎΠ³ΠΎ ΠΈΠΌΠ΅Π½ΠΈ +3. ОбновлСниС Π²Π»Π°Π΄Π΅Π»ΡŒΡ†Π΅Π² сСрвСров +4. ОбновлСниС доступов ΠΊ сСрвСрам +5. Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ Π½ΠΎΠ²ΠΎΠ³ΠΎ JWT Ρ‚ΠΎΠΊΠ΅Π½Π° + +#### ИзмСнСниС пароля +1. ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° Ρ‚Π΅ΠΊΡƒΡ‰Π΅Π³ΠΎ пароля +2. ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° Π΄Π»ΠΈΠ½Ρ‹ Π½ΠΎΠ²ΠΎΠ³ΠΎ пароля (ΠΌΠΈΠ½ΠΈΠΌΡƒΠΌ 6 символов) +3. Π₯Π΅ΡˆΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ Π½ΠΎΠ²ΠΎΠ³ΠΎ пароля (bcrypt) + +## βœ… Π“ΠΎΡ‚ΠΎΠ²ΠΎ! + +БистСма Π»ΠΈΡ‡Π½ΠΎΠ³ΠΎ ΠΊΠ°Π±ΠΈΠ½Π΅Ρ‚Π° ΠΏΠΎΠ»Π½ΠΎΡΡ‚ΡŒΡŽ ΠΈΠ½Ρ‚Π΅Π³Ρ€ΠΈΡ€ΠΎΠ²Π°Π½Π° Π² MC Panel. ΠŸΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΠΈ ΠΌΠΎΠ³ΡƒΡ‚: +- ΠŸΡ€ΠΎΡΠΌΠ°Ρ‚Ρ€ΠΈΠ²Π°Ρ‚ΡŒ статистику своСго профиля +- Π˜Π·ΠΌΠ΅Π½ΡΡ‚ΡŒ имя ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ +- Π˜Π·ΠΌΠ΅Π½ΡΡ‚ΡŒ ΠΏΠ°Ρ€ΠΎΠ»ΡŒ +- Π’ΠΈΠ΄Π΅Ρ‚ΡŒ свои сСрвСры ΠΈ Ρ‚ΠΈΠΊΠ΅Ρ‚Ρ‹ + +### Доступ ΠΊ Π»ΠΈΡ‡Π½ΠΎΠΌΡƒ ΠΊΠ°Π±ΠΈΠ½Π΅Ρ‚Ρƒ +Кнопка "Π›ΠΈΡ‡Π½Ρ‹ΠΉ ΠΊΠ°Π±ΠΈΠ½Π΅Ρ‚" доступна всСм ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡΠΌ Π² header рядом с ΠΊΠ½ΠΎΠΏΠΊΠΎΠΉ "Π’ΠΈΠΊΠ΅Ρ‚Ρ‹". + +### Π£Ρ‡Ρ‘Ρ‚Π½Ρ‹Π΅ Π΄Π°Π½Π½Ρ‹Π΅ ΠΏΠΎ ΡƒΠΌΠΎΠ»Ρ‡Π°Π½ΠΈΡŽ +- **Π›ΠΎΠ³ΠΈΠ½**: Sofa12345 +- **ΠŸΠ°Ρ€ΠΎΠ»ΡŒ**: arkonsad123 +- **Роль**: admin diff --git a/backend/main.py b/backend/main.py index 02183c9..c112e96 100644 --- a/backend/main.py +++ b/backend/main.py @@ -266,7 +266,7 @@ async def update_user_role(username: str, data: dict, user: dict = Depends(get_c raise HTTPException(404, "ΠŸΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒ Π½Π΅ Π½Π°ΠΉΠ΄Π΅Π½") new_role = data.get("role") - if new_role not in ["admin", "user"]: + if new_role not in ["admin", "user", "support"]: raise HTTPException(400, "НСвСрная Ρ€ΠΎΠ»ΡŒ") users[username]["role"] = new_role @@ -274,6 +274,130 @@ async def update_user_role(username: str, data: dict, user: dict = Depends(get_c return {"message": "Роль ΠΎΠ±Π½ΠΎΠ²Π»Π΅Π½Π°"} +# API для Π»ΠΈΡ‡Π½ΠΎΠ³ΠΎ ΠΊΠ°Π±ΠΈΠ½Π΅Ρ‚Π° +@app.put("/api/profile/username") +async def update_username(data: dict, user: dict = Depends(get_current_user)): + """Π˜Π·ΠΌΠ΅Π½ΠΈΡ‚ΡŒ имя ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ""" + new_username = data.get("new_username", "").strip() + password = data.get("password", "") + + if not new_username: + raise HTTPException(400, "Имя ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ Π½Π΅ ΠΌΠΎΠΆΠ΅Ρ‚ Π±Ρ‹Ρ‚ΡŒ пустым") + + if len(new_username) < 3: + raise HTTPException(400, "Имя ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ Π΄ΠΎΠ»ΠΆΠ½ΠΎ Π±Ρ‹Ρ‚ΡŒ Π½Π΅ ΠΌΠ΅Π½Π΅Π΅ 3 символов") + + users = load_users() + + # ΠŸΡ€ΠΎΠ²Π΅Ρ€ΡΠ΅ΠΌ ΠΏΠ°Ρ€ΠΎΠ»ΡŒ + if not verify_password(password, users[user["username"]]["password"]): + raise HTTPException(400, "НСвСрный ΠΏΠ°Ρ€ΠΎΠ»ΡŒ") + + # ΠŸΡ€ΠΎΠ²Π΅Ρ€ΡΠ΅ΠΌ, Π½Π΅ занято Π»ΠΈ Π½ΠΎΠ²ΠΎΠ΅ имя + if new_username in users and new_username != user["username"]: + raise HTTPException(400, "Π­Ρ‚ΠΎ имя ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ ΡƒΠΆΠ΅ занято") + + # БохраняСм Π΄Π°Π½Π½Ρ‹Π΅ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ + old_username = user["username"] + user_data = users[old_username] + + # УдаляСм ΡΡ‚Π°Ρ€ΡƒΡŽ запись ΠΈ создаём Π½ΠΎΠ²ΡƒΡŽ + del users[old_username] + user_data["username"] = new_username + users[new_username] = user_data + + # ОбновляСм Π²Π»Π°Π΄Π΅Π»ΡŒΡ†Π΅Π² сСрвСров + for server_dir in SERVERS_DIR.iterdir(): + if server_dir.is_dir(): + config = load_server_config(server_dir.name) + if config.get("owner") == old_username: + config["owner"] = new_username + save_server_config(server_dir.name, config) + + # ОбновляСм доступы ΠΊ сСрвСрам Ρƒ Π΄Ρ€ΡƒΠ³ΠΈΡ… ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Π΅ΠΉ + for username, user_info in users.items(): + if "servers" in user_info and old_username in user_info.get("servers", []): + user_info["servers"] = [new_username if s == old_username else s for s in user_info["servers"]] + + save_users(users) + + # Π‘ΠΎΠ·Π΄Π°Ρ‘ΠΌ Π½ΠΎΠ²Ρ‹ΠΉ Ρ‚ΠΎΠΊΠ΅Π½ + new_token = create_access_token({"sub": new_username, "role": user_data["role"]}) + + return { + "message": "Имя ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΎ", + "access_token": new_token, + "username": new_username + } + +@app.put("/api/profile/password") +async def update_password(data: dict, user: dict = Depends(get_current_user)): + """Π˜Π·ΠΌΠ΅Π½ΠΈΡ‚ΡŒ ΠΏΠ°Ρ€ΠΎΠ»ΡŒ""" + old_password = data.get("old_password", "") + new_password = data.get("new_password", "") + + if not old_password or not new_password: + raise HTTPException(400, "Π—Π°ΠΏΠΎΠ»Π½ΠΈΡ‚Π΅ всС поля") + + if len(new_password) < 6: + raise HTTPException(400, "Новый ΠΏΠ°Ρ€ΠΎΠ»ΡŒ Π΄ΠΎΠ»ΠΆΠ΅Π½ Π±Ρ‹Ρ‚ΡŒ Π½Π΅ ΠΌΠ΅Π½Π΅Π΅ 6 символов") + + users = load_users() + + # ΠŸΡ€ΠΎΠ²Π΅Ρ€ΡΠ΅ΠΌ старый ΠΏΠ°Ρ€ΠΎΠ»ΡŒ + if not verify_password(old_password, users[user["username"]]["password"]): + raise HTTPException(400, "НСвСрный старый ΠΏΠ°Ρ€ΠΎΠ»ΡŒ") + + # УстанавливаСм Π½ΠΎΠ²Ρ‹ΠΉ ΠΏΠ°Ρ€ΠΎΠ»ΡŒ + users[user["username"]]["password"] = get_password_hash(new_password) + save_users(users) + + return {"message": "ΠŸΠ°Ρ€ΠΎΠ»ΡŒ ΠΈΠ·ΠΌΠ΅Π½Ρ‘Π½"} + +@app.get("/api/profile/stats") +async def get_profile_stats(user: dict = Depends(get_current_user)): + """ΠŸΠΎΠ»ΡƒΡ‡ΠΈΡ‚ΡŒ статистику профиля""" + users = load_users() + user_data = users.get(user["username"], {}) + + # ΠŸΠΎΠ΄ΡΡ‡ΠΈΡ‚Ρ‹Π²Π°Π΅ΠΌ сСрвСры ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ + owned_servers = [] + accessible_servers = [] + + for server_dir in SERVERS_DIR.iterdir(): + if server_dir.is_dir(): + config = load_server_config(server_dir.name) + if config.get("owner") == user["username"]: + owned_servers.append({ + "name": server_dir.name, + "displayName": config.get("displayName", server_dir.name) + }) + elif user["username"] in user_data.get("servers", []) or user["role"] == "admin": + accessible_servers.append({ + "name": server_dir.name, + "displayName": config.get("displayName", server_dir.name) + }) + + # ΠŸΠΎΠ΄ΡΡ‡ΠΈΡ‚Ρ‹Π²Π°Π΅ΠΌ Ρ‚ΠΈΠΊΠ΅Ρ‚Ρ‹ + tickets = load_tickets() + user_tickets = [t for t in tickets.values() if t["author"] == user["username"]] + + tickets_stats = { + "total": len(user_tickets), + "pending": len([t for t in user_tickets if t["status"] == "pending"]), + "in_progress": len([t for t in user_tickets if t["status"] == "in_progress"]), + "closed": len([t for t in user_tickets if t["status"] == "closed"]) + } + + return { + "username": user["username"], + "role": user["role"], + "owned_servers": owned_servers, + "accessible_servers": accessible_servers, + "tickets": tickets_stats, + "total_servers": len(owned_servers) + len(accessible_servers) + } + # API для сСрвСров @app.get("/api/servers") async def get_servers(user: dict = Depends(get_current_user)): diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index aa2fd33..39dd9a3 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -1,5 +1,5 @@ import { useState, useEffect } from 'react'; -import { Server, Play, Square, Terminal, FolderOpen, HardDrive, Settings, Plus, Users as UsersIcon, LogOut, Menu, X, MessageSquare } from 'lucide-react'; +import { Server, Play, Square, Terminal, FolderOpen, HardDrive, Settings, Plus, Users as UsersIcon, LogOut, Menu, X, MessageSquare, UserCircle } from 'lucide-react'; import Console from './components/Console'; import FileManager from './components/FileManager'; import Stats from './components/Stats'; @@ -7,6 +7,7 @@ import ServerSettings from './components/ServerSettings'; import CreateServerModal from './components/CreateServerModal'; import Users from './components/Users'; import Tickets from './components/Tickets'; +import Profile from './components/Profile'; import Auth from './components/Auth'; import ErrorBoundary from './components/ErrorBoundary'; import ThemeSelector from './components/ThemeSelector'; @@ -23,6 +24,7 @@ function App() { const [showCreateModal, setShowCreateModal] = useState(false); const [showUsers, setShowUsers] = useState(false); const [showTickets, setShowTickets] = useState(false); + const [showProfile, setShowProfile] = useState(false); const [connectionError, setConnectionError] = useState(false); const [theme, setTheme] = useState(localStorage.getItem('theme') || 'dark'); const [sidebarOpen, setSidebarOpen] = useState(true); @@ -97,6 +99,12 @@ function App() { localStorage.setItem('theme', newTheme); }; + const handleUsernameChange = (newToken, newUsername) => { + setToken(newToken); + setUser({ ...user, username: newUsername }); + loadServers(); + }; + const startServer = async (serverName) => { try { const response = await axios.post( @@ -235,6 +243,56 @@ function App() { ); } + if (showProfile) { + return ( +
+
+
+
+
+
+
+ +
+
+

MC Panel

+

Π£ΠΏΡ€Π°Π²Π»Π΅Π½ΠΈΠ΅ сСрвСрами

+
+
+
+
+
+ + {user?.username} + + + {user?.role === 'admin' ? 'Админ' : user?.role === 'support' ? 'ΠŸΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΊΠ°' : 'ΠŸΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒ'} + +
+ + + +
+
+
+
+ +
+ ); + } + return (
{/* Header */} @@ -273,6 +331,13 @@ function App() {
+ + + + + + {/* Overview Tab */} + {activeTab === 'overview' && ( +
+ {/* User Info Card */} +
+
+
+ +
+
+

{stats?.username}

+
+ + {getRoleName(stats?.role)} +
+
+
+
+ + {/* Stats Grid */} +
+ {/* Servers */} +
+
+
+ +
+
+

ВсСго сСрвСров

+

{stats?.total_servers || 0}

+
+
+
+
+ Мои сСрвСры: + {stats?.owned_servers?.length || 0} +
+
+ ДоступныС: + {stats?.accessible_servers?.length || 0} +
+
+
+ + {/* Tickets */} +
+
+
+ +
+
+

Мои Ρ‚ΠΈΠΊΠ΅Ρ‚Ρ‹

+

{stats?.tickets?.total || 0}

+
+
+
+
+ На рассмотрСнии: + {stats?.tickets?.pending || 0} +
+
+ Π’ Ρ€Π°Π±ΠΎΡ‚Π΅: + {stats?.tickets?.in_progress || 0} +
+
+ Π—Π°ΠΊΡ€Ρ‹Ρ‚ΠΎ: + {stats?.tickets?.closed || 0} +
+
+
+ + {/* Role Info */} +
+
+
+ +
+
+

Π’Π°ΡˆΠ° Ρ€ΠΎΠ»ΡŒ

+

{getRoleName(stats?.role)}

+
+
+
+ {stats?.role === 'admin' && 'ΠŸΠΎΠ»Π½Ρ‹ΠΉ доступ ΠΊΠΎ всСм функциям ΠΏΠ°Π½Π΅Π»ΠΈ'} + {stats?.role === 'support' && 'Доступ ΠΊ систСмС Ρ‚ΠΈΠΊΠ΅Ρ‚ΠΎΠ² ΠΈ ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΊΠ΅'} + {stats?.role === 'user' && 'Доступ ΠΊ своим сСрвСрам ΠΈ Ρ‚ΠΈΠΊΠ΅Ρ‚Π°ΠΌ'} +
+
+
+ + {/* Servers List */} + {stats?.owned_servers?.length > 0 && ( +
+

Мои сСрвСры

+
+ {stats.owned_servers.map((server) => ( +
+
+ +
+

{server.displayName}

+

{server.name}

+
+
+
+ ))} +
+
+ )} +
+ )} + + {/* Username Tab */} + {activeTab === 'username' && ( +
+

Π˜Π·ΠΌΠ΅Π½ΠΈΡ‚ΡŒ имя ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ

+
+
+ + +
+ +
+ + setUsernameForm({ ...usernameForm, new_username: e.target.value })} + placeholder="Π’Π²Π΅Π΄ΠΈΡ‚Π΅ Π½ΠΎΠ²ΠΎΠ΅ имя" + className={`w-full ${theme.input} ${theme.border} border rounded-xl px-4 py-3 ${theme.text} focus:outline-none focus:ring-2 focus:ring-blue-500 transition`} + /> +

+ ΠœΠΈΠ½ΠΈΠΌΡƒΠΌ 3 символа +

+
+ +
+ +
+ setUsernameForm({ ...usernameForm, password: e.target.value })} + placeholder="Π’Π²Π΅Π΄ΠΈΡ‚Π΅ Ρ‚Π΅ΠΊΡƒΡ‰ΠΈΠΉ ΠΏΠ°Ρ€ΠΎΠ»ΡŒ" + className={`w-full ${theme.input} ${theme.border} border rounded-xl px-4 py-3 pr-12 ${theme.text} focus:outline-none focus:ring-2 focus:ring-blue-500 transition`} + /> + +
+
+ +
+

+ ⚠️ ПослС измСнСния ΠΈΠΌΠ΅Π½ΠΈ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ Π²Ρ‹ Π±ΡƒΠ΄Π΅Ρ‚Π΅ автоматичСски ΠΏΠ΅Ρ€Π΅Π»ΠΎΠ³ΠΈΠ½Π΅Π½Ρ‹ с Π½ΠΎΠ²Ρ‹ΠΌ ΠΈΠΌΠ΅Π½Π΅ΠΌ. +

+
+ + +
+
+ )} + + {/* Password Tab */} + {activeTab === 'password' && ( +
+

Π˜Π·ΠΌΠ΅Π½ΠΈΡ‚ΡŒ ΠΏΠ°Ρ€ΠΎΠ»ΡŒ

+
+
+ +
+ setPasswordForm({ ...passwordForm, old_password: e.target.value })} + placeholder="Π’Π²Π΅Π΄ΠΈΡ‚Π΅ Ρ‚Π΅ΠΊΡƒΡ‰ΠΈΠΉ ΠΏΠ°Ρ€ΠΎΠ»ΡŒ" + className={`w-full ${theme.input} ${theme.border} border rounded-xl px-4 py-3 pr-12 ${theme.text} focus:outline-none focus:ring-2 focus:ring-blue-500 transition`} + /> + +
+
+ +
+ +
+ setPasswordForm({ ...passwordForm, new_password: e.target.value })} + placeholder="Π’Π²Π΅Π΄ΠΈΡ‚Π΅ Π½ΠΎΠ²Ρ‹ΠΉ ΠΏΠ°Ρ€ΠΎΠ»ΡŒ" + className={`w-full ${theme.input} ${theme.border} border rounded-xl px-4 py-3 pr-12 ${theme.text} focus:outline-none focus:ring-2 focus:ring-blue-500 transition`} + /> + +
+

+ ΠœΠΈΠ½ΠΈΠΌΡƒΠΌ 6 символов +

+
+ +
+ + setPasswordForm({ ...passwordForm, confirm_password: e.target.value })} + placeholder="ΠŸΠΎΠ²Ρ‚ΠΎΡ€ΠΈΡ‚Π΅ Π½ΠΎΠ²Ρ‹ΠΉ ΠΏΠ°Ρ€ΠΎΠ»ΡŒ" + className={`w-full ${theme.input} ${theme.border} border rounded-xl px-4 py-3 ${theme.text} focus:outline-none focus:ring-2 focus:ring-blue-500 transition`} + /> +
+ +
+

+ ⚠️ ПослС измСнСния пароля ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠΉΡ‚Π΅ Π½ΠΎΠ²Ρ‹ΠΉ ΠΏΠ°Ρ€ΠΎΠ»ΡŒ для Π²Ρ…ΠΎΠ΄Π° Π² систСму. +

+
+ + +
+
+ )} + + + ); +} diff --git a/Π“ΠžΠ’ΠžΠ’Πž.md b/Π“ΠžΠ’ΠžΠ’Πž.md index 52e9f4c..a8d0b8b 100644 --- a/Π“ΠžΠ’ΠžΠ’Πž.md +++ b/Π“ΠžΠ’ΠžΠ’Πž.md @@ -7,7 +7,7 @@ - **Новый**: Sofa12345 / arkonsad123 ### 2. Π”ΠΎΠ±Π°Π²Π»Π΅Π½Π° систСма Ρ‚ΠΈΠΊΠ΅Ρ‚ΠΎΠ² 🎫 -- Кнопка "Π’ΠΈΠΊΠ΅Ρ‚Ρ‹" Π² header рядом с "ΠŸΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΠΈ" +- Кнопка "Π’ΠΈΠΊΠ΅Ρ‚Ρ‹" Π² header - Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ Ρ‚ΠΈΠΊΠ΅Ρ‚ΠΎΠ² с Ρ‚Π΅ΠΌΠΎΠΉ ΠΈ описаниСм - Π§Π°Ρ‚ для общСния Π² Ρ‚ΠΈΠΊΠ΅Ρ‚Π΅ - Π’Ρ€ΠΈ статуса: @@ -21,7 +21,16 @@ - Π’ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡ‚ΡŒ ΠΎΡ‚Π²Π΅Ρ‡Π°Ρ‚ΡŒ Π½Π° Ρ‚ΠΈΠΊΠ΅Ρ‚Ρ‹ - ΠžΡ‚Π΄Π΅Π»ΡŒΠ½Ρ‹ΠΉ Π±Π΅ΠΉΠ΄ΠΆ Π² интСрфСйсС -### 4. Π£Π»ΡƒΡ‡ΡˆΠ΅Π½ΠΎ ΡƒΠΏΡ€Π°Π²Π»Π΅Π½ΠΈΠ΅ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡΠΌΠΈ +### 4. Π”ΠΎΠ±Π°Π²Π»Π΅Π½ Π»ΠΈΡ‡Π½Ρ‹ΠΉ ΠΊΠ°Π±ΠΈΠ½Π΅Ρ‚ πŸ‘€ +- Кнопка "Π›ΠΈΡ‡Π½Ρ‹ΠΉ ΠΊΠ°Π±ΠΈΠ½Π΅Ρ‚" Π² header рядом с "Π’ΠΈΠΊΠ΅Ρ‚Ρ‹" +- Π’Ρ€ΠΈ Π²ΠΊΠ»Π°Π΄ΠΊΠΈ: + - πŸ“Š ΠžΠ±Π·ΠΎΡ€ - статистика профиля + - πŸ‘€ Имя ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ - ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠ΅ ΠΈΠΌΠ΅Π½ΠΈ + - πŸ”’ ΠŸΠ°Ρ€ΠΎΠ»ΡŒ - ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠ΅ пароля +- Бтатистика ΠΏΠΎ сСрвСрам ΠΈ Ρ‚ΠΈΠΊΠ΅Ρ‚Π°ΠΌ +- Бписок своих сСрвСров + +### 5. Π£Π»ΡƒΡ‡ΡˆΠ΅Π½ΠΎ ΡƒΠΏΡ€Π°Π²Π»Π΅Π½ΠΈΠ΅ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡΠΌΠΈ - Π’Ρ‹ΠΏΠ°Π΄Π°ΡŽΡ‰ΠΈΠΉ список для Π²Ρ‹Π±ΠΎΡ€Π° Ρ€ΠΎΠ»ΠΈ - Π’Ρ€ΠΈ Ρ€ΠΎΠ»ΠΈ: ΠŸΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒ, Π’Π΅Ρ…. ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΊΠ°, Администратор - Π¦Π²Π΅Ρ‚Π½Ρ‹Π΅ ΠΈΠ½Π΄ΠΈΠΊΠ°Ρ‚ΠΎΡ€Ρ‹ Ρ€ΠΎΠ»Π΅ΠΉ @@ -80,6 +89,9 @@ http://localhost:3000 - βœ… Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ Ρ‚ΠΈΠΊΠ΅Ρ‚ΠΎΠ² - βœ… ΠžΠ±Ρ‰Π΅Π½ΠΈΠ΅ Π² своих Ρ‚ΠΈΠΊΠ΅Ρ‚Π°Ρ… - βœ… Π‘ΠΌΠ΅Π½Π° Ρ‚Π΅ΠΌΡ‹ интСрфСйса +- βœ… Π›ΠΈΡ‡Π½Ρ‹ΠΉ ΠΊΠ°Π±ΠΈΠ½Π΅Ρ‚ с статистикой +- βœ… ИзмСнСниС ΠΈΠΌΠ΅Π½ΠΈ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ +- βœ… ИзмСнСниС пароля ### Для Ρ‚Π΅Ρ…. ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΊΠΈ - βœ… ВсС возмоТности ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ @@ -110,6 +122,7 @@ MC Panel/ β”‚ β”œβ”€β”€ themes.js # ΠšΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΡ Ρ‚Π΅ΠΌ β”‚ └── components/ β”‚ β”œβ”€β”€ Auth.jsx # Авторизация +β”‚ β”œβ”€β”€ Profile.jsx # Π›ΠΈΡ‡Π½Ρ‹ΠΉ ΠΊΠ°Π±ΠΈΠ½Π΅Ρ‚ β”‚ β”œβ”€β”€ Tickets.jsx # Бписок Ρ‚ΠΈΠΊΠ΅Ρ‚ΠΎΠ² β”‚ β”œβ”€β”€ TicketChat.jsx # Π§Π°Ρ‚ Ρ‚ΠΈΠΊΠ΅Ρ‚Π° β”‚ β”œβ”€β”€ CreateTicketModal.jsx # Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ Ρ‚ΠΈΠΊΠ΅Ρ‚Π° @@ -121,6 +134,7 @@ MC Panel/ β”‚ └── ДокумСнтация/ β”œβ”€β”€ Π“ΠžΠ’ΠžΠ’Πž.md # Π­Ρ‚ΠΎΡ‚ Ρ„Π°ΠΉΠ» + β”œβ”€β”€ PROFILE_SYSTEM.md # ДокумСнтация Π»ΠΈΡ‡Π½ΠΎΠ³ΠΎ ΠΊΠ°Π±ΠΈΠ½Π΅Ρ‚Π° β”œβ”€β”€ TICKETS_SYSTEM.md # ДокумСнтация систСмы Ρ‚ΠΈΠΊΠ΅Ρ‚ΠΎΠ² β”œβ”€β”€ CHANGELOG.md # Π˜ΡΡ‚ΠΎΡ€ΠΈΡ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠΉ └── Π‘Π«Π‘Π’Π Π«Π™_БВАРВ.md # Быстрый старт @@ -160,6 +174,7 @@ MC Panel/ - βœ… БистСма ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Π΅ΠΉ - βœ… БистСма Ρ‚ΠΈΠΊΠ΅Ρ‚ΠΎΠ² - βœ… Роль Ρ‚Π΅Ρ…. ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΊΠΈ +- βœ… Π›ΠΈΡ‡Π½Ρ‹ΠΉ ΠΊΠ°Π±ΠΈΠ½Π΅Ρ‚ - βœ… 5 Ρ‚Π΅ΠΌ оформлСния - βœ… Π‘ΠΎΠ²Ρ€Π΅ΠΌΠ΅Π½Π½Ρ‹ΠΉ интСрфСйс diff --git a/Π›Π˜Π§ΠΠ«Π™_ΠšΠΠ‘Π˜ΠΠ•Π’_Π“ΠžΠ’ΠžΠ’.md b/Π›Π˜Π§ΠΠ«Π™_ΠšΠΠ‘Π˜ΠΠ•Π’_Π“ΠžΠ’ΠžΠ’.md new file mode 100644 index 0000000..5b7ab5e --- /dev/null +++ b/Π›Π˜Π§ΠΠ«Π™_ΠšΠΠ‘Π˜ΠΠ•Π’_Π“ΠžΠ’ΠžΠ’.md @@ -0,0 +1,183 @@ +# βœ… Π›ΠΈΡ‡Π½Ρ‹ΠΉ ΠΊΠ°Π±ΠΈΠ½Π΅Ρ‚ Π³ΠΎΡ‚ΠΎΠ²! + +## πŸŽ‰ Π§Ρ‚ΠΎ Π΄ΠΎΠ±Π°Π²Π»Π΅Π½ΠΎ + +### Кнопка "Π›ΠΈΡ‡Π½Ρ‹ΠΉ ΠΊΠ°Π±ΠΈΠ½Π΅Ρ‚" +РасполоТСна Π² header рядом с ΠΊΠ½ΠΎΠΏΠΊΠΎΠΉ "Π’ΠΈΠΊΠ΅Ρ‚Ρ‹". Доступна всСм ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡΠΌ. + +### Π’Ρ€ΠΈ Π²ΠΊΠ»Π°Π΄ΠΊΠΈ + +#### 1. πŸ“Š ΠžΠ±Π·ΠΎΡ€ +**Π˜Π½Ρ„ΠΎΡ€ΠΌΠ°Ρ†ΠΈΡ ΠΎ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Π΅:** +- Имя ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ +- Роль с Ρ†Π²Π΅Ρ‚Π½Ρ‹ΠΌ Π±Π΅ΠΉΠ΄ΠΆΠ΅ΠΌ +- Аватар + +**Бтатистика:** +- πŸ–₯️ **Π‘Π΅Ρ€Π²Π΅Ρ€Ρ‹**: всСго, ΠΌΠΎΠΈ, доступныС +- 🎫 **Π’ΠΈΠΊΠ΅Ρ‚Ρ‹**: всСго, Π½Π° рассмотрСнии, Π² Ρ€Π°Π±ΠΎΡ‚Π΅, Π·Π°ΠΊΡ€Ρ‹Ρ‚ΠΎ +- πŸ›‘οΈ **Роль**: Π½Π°Π·Π²Π°Π½ΠΈΠ΅ ΠΈ описаниС ΠΏΡ€Π°Π² + +**Бписок сСрвСров:** +- ВсС сСрвСры ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ +- НазваниС ΠΈ ID +- ΠšΡ€Π°ΡΠΈΠ²Ρ‹Π΅ ΠΊΠ°Ρ€Ρ‚ΠΎΡ‡ΠΊΠΈ + +#### 2. πŸ‘€ Имя ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ +**ВозмоТности:** +- ΠŸΡ€ΠΎΡΠΌΠΎΡ‚Ρ€ Ρ‚Π΅ΠΊΡƒΡ‰Π΅Π³ΠΎ ΠΈΠΌΠ΅Π½ΠΈ +- Π’Π²ΠΎΠ΄ Π½ΠΎΠ²ΠΎΠ³ΠΎ ΠΈΠΌΠ΅Π½ΠΈ (ΠΌΠΈΠ½ΠΈΠΌΡƒΠΌ 3 символа) +- ΠŸΠΎΠ΄Ρ‚Π²Π΅Ρ€ΠΆΠ΄Π΅Π½ΠΈΠ΅ ΠΏΠ°Ρ€ΠΎΠ»Π΅ΠΌ +- АвтоматичСский ΠΏΠ΅Ρ€Π΅Π»ΠΎΠ³ΠΈΠ½ + +**Π§Ρ‚ΠΎ обновляСтся:** +- Π’Π»Π°Π΄Π΅Π»ΡŒΡ†Ρ‹ сСрвСров +- Доступы ΠΊ сСрвСрам +- JWT Ρ‚ΠΎΠΊΠ΅Π½ + +#### 3. πŸ”’ ΠŸΠ°Ρ€ΠΎΠ»ΡŒ +**ВозмоТности:** +- Π’Π²ΠΎΠ΄ Ρ‚Π΅ΠΊΡƒΡ‰Π΅Π³ΠΎ пароля +- Π’Π²ΠΎΠ΄ Π½ΠΎΠ²ΠΎΠ³ΠΎ пароля (ΠΌΠΈΠ½ΠΈΠΌΡƒΠΌ 6 символов) +- ΠŸΠΎΠ΄Ρ‚Π²Π΅Ρ€ΠΆΠ΄Π΅Π½ΠΈΠ΅ Π½ΠΎΠ²ΠΎΠ³ΠΎ пароля +- Показ/скрытиС ΠΏΠ°Ρ€ΠΎΠ»Π΅ΠΉ (ΠΈΠΊΠΎΠ½ΠΊΠ° Π³Π»Π°Π·Π°) + +**Π‘Π΅Π·ΠΎΠΏΠ°ΡΠ½ΠΎΡΡ‚ΡŒ:** +- ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° Ρ‚Π΅ΠΊΡƒΡ‰Π΅Π³ΠΎ пароля +- ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° совпадСния Π½ΠΎΠ²Ρ‹Ρ… ΠΏΠ°Ρ€ΠΎΠ»Π΅ΠΉ +- Π₯Π΅ΡˆΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ (bcrypt) + +## πŸš€ Как ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ + +### ΠžΡ‚ΠΊΡ€Ρ‹Ρ‚ΡŒ Π»ΠΈΡ‡Π½Ρ‹ΠΉ ΠΊΠ°Π±ΠΈΠ½Π΅Ρ‚ +1. Π’ΠΎΠΉΠ΄ΠΈΡ‚Π΅ Π² панСль +2. НаТмитС ΠΊΠ½ΠΎΠΏΠΊΡƒ "Π›ΠΈΡ‡Π½Ρ‹ΠΉ ΠΊΠ°Π±ΠΈΠ½Π΅Ρ‚" Π² header +3. ΠžΡ‚ΠΊΡ€ΠΎΠ΅Ρ‚ΡΡ страница с Π²ΠΊΠ»Π°Π΄ΠΊΠ°ΠΌΠΈ + +### ΠŸΠΎΡΠΌΠΎΡ‚Ρ€Π΅Ρ‚ΡŒ статистику +1. ΠžΡ‚ΠΊΡ€ΠΎΠΉΡ‚Π΅ Π²ΠΊΠ»Π°Π΄ΠΊΡƒ "ΠžΠ±Π·ΠΎΡ€" +2. ΠŸΠΎΡΠΌΠΎΡ‚Ρ€ΠΈΡ‚Π΅ ΠΈΠ½Ρ„ΠΎΡ€ΠΌΠ°Ρ†ΠΈΡŽ ΠΎ ΠΏΡ€ΠΎΡ„ΠΈΠ»Π΅ +3. ΠŸΠΎΡΠΌΠΎΡ‚Ρ€ΠΈΡ‚Π΅ статистику +4. ΠŸΠΎΡΠΌΠΎΡ‚Ρ€ΠΈΡ‚Π΅ список сСрвСров + +### Π˜Π·ΠΌΠ΅Π½ΠΈΡ‚ΡŒ имя ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ +1. ΠžΡ‚ΠΊΡ€ΠΎΠΉΡ‚Π΅ Π²ΠΊΠ»Π°Π΄ΠΊΡƒ "Имя ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ" +2. Π’Π²Π΅Π΄ΠΈΡ‚Π΅ Π½ΠΎΠ²ΠΎΠ΅ имя (ΠΌΠΈΠ½ΠΈΠΌΡƒΠΌ 3 символа) +3. Π’Π²Π΅Π΄ΠΈΡ‚Π΅ Ρ‚Π΅ΠΊΡƒΡ‰ΠΈΠΉ ΠΏΠ°Ρ€ΠΎΠ»ΡŒ +4. НаТмитС "Π˜Π·ΠΌΠ΅Π½ΠΈΡ‚ΡŒ имя ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ" +5. Π’Ρ‹ Π±ΡƒΠ΄Π΅Ρ‚Π΅ автоматичСски ΠΏΠ΅Ρ€Π΅Π»ΠΎΠ³ΠΈΠ½Π΅Π½Ρ‹ + +⚠️ **Π’Π°ΠΆΠ½ΠΎ**: ПослС измСнСния ΠΈΠΌΠ΅Π½ΠΈ обновятся всС сСрвСры ΠΈ доступы. + +### Π˜Π·ΠΌΠ΅Π½ΠΈΡ‚ΡŒ ΠΏΠ°Ρ€ΠΎΠ»ΡŒ +1. ΠžΡ‚ΠΊΡ€ΠΎΠΉΡ‚Π΅ Π²ΠΊΠ»Π°Π΄ΠΊΡƒ "ΠŸΠ°Ρ€ΠΎΠ»ΡŒ" +2. Π’Π²Π΅Π΄ΠΈΡ‚Π΅ Ρ‚Π΅ΠΊΡƒΡ‰ΠΈΠΉ ΠΏΠ°Ρ€ΠΎΠ»ΡŒ +3. Π’Π²Π΅Π΄ΠΈΡ‚Π΅ Π½ΠΎΠ²Ρ‹ΠΉ ΠΏΠ°Ρ€ΠΎΠ»ΡŒ (ΠΌΠΈΠ½ΠΈΠΌΡƒΠΌ 6 символов) +4. ΠŸΠΎΠ΄Ρ‚Π²Π΅Ρ€Π΄ΠΈΡ‚Π΅ Π½ΠΎΠ²Ρ‹ΠΉ ΠΏΠ°Ρ€ΠΎΠ»ΡŒ +5. НаТмитС "Π˜Π·ΠΌΠ΅Π½ΠΈΡ‚ΡŒ ΠΏΠ°Ρ€ΠΎΠ»ΡŒ" + +⚠️ **Π’Π°ΠΆΠ½ΠΎ**: ПослС измСнСния пароля ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠΉΡ‚Π΅ Π½ΠΎΠ²Ρ‹ΠΉ ΠΏΠ°Ρ€ΠΎΠ»ΡŒ для Π²Ρ…ΠΎΠ΄Π°. + +## 🎨 Π”ΠΈΠ·Π°ΠΉΠ½ + +### Π‘ΠΎΠ²Ρ€Π΅ΠΌΠ΅Π½Π½Ρ‹ΠΉ интСрфСйс +- Π’ΠΊΠ»Π°Π΄ΠΊΠΈ с ΠΏΠ»Π°Π²Π½Ρ‹ΠΌΠΈ ΠΏΠ΅Ρ€Π΅Ρ…ΠΎΠ΄Π°ΠΌΠΈ +- Π¦Π²Π΅Ρ‚Π½Ρ‹Π΅ ΠΊΠ°Ρ€Ρ‚ΠΎΡ‡ΠΊΠΈ статистики +- Иконки для ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ Ρ€Π°Π·Π΄Π΅Π»Π° +- Адаптивный Π΄ΠΈΠ·Π°ΠΉΠ½ + +### Π€ΠΎΡ€ΠΌΡ‹ +- Валидация ΠΏΠΎΠ»Π΅ΠΉ +- Показ/скрытиС ΠΏΠ°Ρ€ΠΎΠ»Π΅ΠΉ +- ΠŸΡ€Π΅Π΄ΡƒΠΏΡ€Π΅ΠΆΠ΄Π΅Π½ΠΈΡ +- Π˜Π½Π΄ΠΈΠΊΠ°Ρ†ΠΈΡ Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠΈ + +### Π¦Π²Π΅Ρ‚ΠΎΠ²Ρ‹Π΅ схСмы +- Π Π°Π±ΠΎΡ‚Π°Π΅Ρ‚ со всСми 5 Ρ‚Π΅ΠΌΠ°ΠΌΠΈ +- Π“Ρ€Π°Π΄ΠΈΠ΅Π½Ρ‚Π½Ρ‹ΠΉ Π»ΠΎΠ³ΠΎΡ‚ΠΈΠΏ +- Π¦Π²Π΅Ρ‚Π½Ρ‹Π΅ Π±Π΅ΠΉΠ΄ΠΆΠΈ Ρ€ΠΎΠ»Π΅ΠΉ + +## πŸ“‹ API Endpoints + +### PUT /api/profile/username +Π˜Π·ΠΌΠ΅Π½ΠΈΡ‚ΡŒ имя ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ + +```bash +curl -X PUT http://localhost:8000/api/profile/username \ + -H "Authorization: Bearer YOUR_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "new_username": "NewUsername", + "password": "current_password" + }' +``` + +### PUT /api/profile/password +Π˜Π·ΠΌΠ΅Π½ΠΈΡ‚ΡŒ ΠΏΠ°Ρ€ΠΎΠ»ΡŒ + +```bash +curl -X PUT http://localhost:8000/api/profile/password \ + -H "Authorization: Bearer YOUR_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "old_password": "old_password", + "new_password": "new_password" + }' +``` + +### GET /api/profile/stats +ΠŸΠΎΠ»ΡƒΡ‡ΠΈΡ‚ΡŒ статистику профиля + +```bash +curl -X GET http://localhost:8000/api/profile/stats \ + -H "Authorization: Bearer YOUR_TOKEN" +``` + +## πŸ” Π‘Π΅Π·ΠΎΠΏΠ°ΡΠ½ΠΎΡΡ‚ΡŒ + +### ИзмСнСниС ΠΈΠΌΠ΅Π½ΠΈ +1. βœ… ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° пароля +2. βœ… ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° ΡƒΠ½ΠΈΠΊΠ°Π»ΡŒΠ½ΠΎΡΡ‚ΠΈ +3. βœ… ОбновлСниС Π²Π»Π°Π΄Π΅Π»ΡŒΡ†Π΅Π² сСрвСров +4. βœ… ОбновлСниС доступов +5. βœ… Новый JWT Ρ‚ΠΎΠΊΠ΅Π½ + +### ИзмСнСниС пароля +1. βœ… ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° Ρ‚Π΅ΠΊΡƒΡ‰Π΅Π³ΠΎ пароля +2. βœ… ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° Π΄Π»ΠΈΠ½Ρ‹ (ΠΌΠΈΠ½ΠΈΠΌΡƒΠΌ 6 символов) +3. βœ… Π₯Π΅ΡˆΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ (bcrypt) + +## βœ… Π“ΠΎΡ‚ΠΎΠ²ΠΎ! + +Π›ΠΈΡ‡Π½Ρ‹ΠΉ ΠΊΠ°Π±ΠΈΠ½Π΅Ρ‚ ΠΏΠΎΠ»Π½ΠΎΡΡ‚ΡŒΡŽ ΠΈΠ½Ρ‚Π΅Π³Ρ€ΠΈΡ€ΠΎΠ²Π°Π½ Π² MC Panel. ВсС ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΠΈ ΠΌΠΎΠ³ΡƒΡ‚: +- πŸ“Š ΠŸΡ€ΠΎΡΠΌΠ°Ρ‚Ρ€ΠΈΠ²Π°Ρ‚ΡŒ статистику +- πŸ‘€ Π˜Π·ΠΌΠ΅Π½ΡΡ‚ΡŒ имя ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ +- πŸ”’ Π˜Π·ΠΌΠ΅Π½ΡΡ‚ΡŒ ΠΏΠ°Ρ€ΠΎΠ»ΡŒ +- πŸ–₯️ Π’ΠΈΠ΄Π΅Ρ‚ΡŒ свои сСрвСры +- 🎫 Π’ΠΈΠ΄Π΅Ρ‚ΡŒ статистику ΠΏΠΎ Ρ‚ΠΈΠΊΠ΅Ρ‚Π°ΠΌ + +### ВСстированиС + +1. **Π’ΠΎΠΉΠ΄ΠΈΡ‚Π΅ Π² панСль** + - Π›ΠΎΠ³ΠΈΠ½: Sofa12345 + - ΠŸΠ°Ρ€ΠΎΠ»ΡŒ: arkonsad123 + +2. **ΠžΡ‚ΠΊΡ€ΠΎΠΉΡ‚Π΅ Π»ΠΈΡ‡Π½Ρ‹ΠΉ ΠΊΠ°Π±ΠΈΠ½Π΅Ρ‚** + - НаТмитС ΠΊΠ½ΠΎΠΏΠΊΡƒ "Π›ΠΈΡ‡Π½Ρ‹ΠΉ ΠΊΠ°Π±ΠΈΠ½Π΅Ρ‚" + +3. **ΠŸΠΎΡΠΌΠΎΡ‚Ρ€ΠΈΡ‚Π΅ статистику** + - Π’ΠΊΠ»Π°Π΄ΠΊΠ° "ΠžΠ±Π·ΠΎΡ€" + +4. **Π˜Π·ΠΌΠ΅Π½ΠΈΡ‚Π΅ имя (ΠΎΠΏΡ†ΠΈΠΎΠ½Π°Π»ΡŒΠ½ΠΎ)** + - Π’ΠΊΠ»Π°Π΄ΠΊΠ° "Имя ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ" + - Π’Π²Π΅Π΄ΠΈΡ‚Π΅ Π½ΠΎΠ²ΠΎΠ΅ имя ΠΈ ΠΏΠ°Ρ€ΠΎΠ»ΡŒ + +5. **Π˜Π·ΠΌΠ΅Π½ΠΈΡ‚Π΅ ΠΏΠ°Ρ€ΠΎΠ»ΡŒ (ΠΎΠΏΡ†ΠΈΠΎΠ½Π°Π»ΡŒΠ½ΠΎ)** + - Π’ΠΊΠ»Π°Π΄ΠΊΠ° "ΠŸΠ°Ρ€ΠΎΠ»ΡŒ" + - Π’Π²Π΅Π΄ΠΈΡ‚Π΅ старый ΠΈ Π½ΠΎΠ²Ρ‹ΠΉ ΠΏΠ°Ρ€ΠΎΠ»ΠΈ + +## 🎯 Π§Ρ‚ΠΎ дальшС? + +БистСма Π»ΠΈΡ‡Π½ΠΎΠ³ΠΎ ΠΊΠ°Π±ΠΈΠ½Π΅Ρ‚Π° Π³ΠΎΡ‚ΠΎΠ²Π° ΠΊ использованию. Π’Π΅ΠΏΠ΅Ρ€ΡŒ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΠΈ ΠΌΠΎΠ³ΡƒΡ‚ ΠΏΠΎΠ»Π½ΠΎΡΡ‚ΡŒΡŽ ΡƒΠΏΡ€Π°Π²Π»ΡΡ‚ΡŒ своим ΠΏΡ€ΠΎΡ„ΠΈΠ»Π΅ΠΌ! + +**ΠΠ°ΡΠ»Π°ΠΆΠ΄Π°ΠΉΡ‚Π΅ΡΡŒ! πŸš€**