diff --git a/SSL/localhost.crt b/SSL/localhost.crt new file mode 100644 index 0000000..92bb661 --- /dev/null +++ b/SSL/localhost.crt @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIIC1zCCAb+gAwIBAgIUPHdL8t6goPcrwjyu1bR0S+Ts7O8wDQYJKoZIhvcNAQEL +BQAwFDESMBAGA1UEAwwJMTI3LjAuMC4xMB4XDTI1MDgyMTA4NTUzMFoXDTI2MDgy +MTA4NTUzMFowFDESMBAGA1UEAwwJMTI3LjAuMC4xMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAmsutLy1yUE1TCvs475LV0UwGrSvuW818ccxh5LEKUiad +iWuv0NL5/OxaG+dWJvVzOz9uBafcxbBYwJ65XqOTXM44FKCKuDtYG/7E2TDc5KPH +/7f/GCFla+2k/aep2VagtTK+AFRiTFcmm9C34mFu34O/2fiAiNROiRBE+SxajPdQ +DEd71yyCypnJWMdvxODToaRSwBNPMCOd/QceHgWUxFuC3813ewASuqKMP+M9ZNEU +QfFPypkgqGvDMUun6olXHs34qYAlu8w1Fk9fu6+9JAEKONtvXaGQcBkF0LBwTicZ +LgDheKc2ZG0wHyrFSs8rMee4zd/gvcgaw7G1bxgcbQIDAQABoyEwHzAdBgNVHQ4E +FgQU9PnSNORZygTvdFeXfjbeYsxbbs8wDQYJKoZIhvcNAQELBQADggEBABo5fok7 +NeeX/0UL01HEy/BwGNxZnscl3tAfTsLTCwhGC4ADd4ppGROTfv+R68eMSM8ZFtR7 +wLO6zK/VE0bidyw9jXN1Tp9etv25IjfkQB4LqNcs6b7LgoeXWklVW9Ap4K2h6A6q +C1lBs8nVW6YtTHE2BPF2dNFMuuqnYiquw3bEikjrZ3waEYnxYP+l9eAFgcAEqyQB +5RKgqq6fv4q7PxyXmvKy18OFxalVogoUFNiyZOoNpgj8ZbqIKru20oU/Xi821yAU +xhGrQn/aY5IshTpNsjzxcP9LYQjOFUId/ISxA9tPucHctkMm/dnbcJmoPZlStIaK +j3xnOy8TLQLoNG4= +-----END CERTIFICATE----- diff --git a/SSL/localhost.key b/SSL/localhost.key new file mode 100644 index 0000000..7d6708c --- /dev/null +++ b/SSL/localhost.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCay60vLXJQTVMK ++zjvktXRTAatK+5bzXxxzGHksQpSJp2Ja6/Q0vn87Fob51Ym9XM7P24Fp9zFsFjA +nrleo5NczjgUoIq4O1gb/sTZMNzko8f/t/8YIWVr7aT9p6nZVqC1Mr4AVGJMVyab +0LfiYW7fg7/Z+ICI1E6JEET5LFqM91AMR3vXLILKmclYx2/E4NOhpFLAE08wI539 +Bx4eBZTEW4LfzXd7ABK6oow/4z1k0RRB8U/KmSCoa8MxS6fqiVcezfipgCW7zDUW +T1+7r70kAQo4229doZBwGQXQsHBOJxkuAOF4pzZkbTAfKsVKzysx57jN3+C9yBrD +sbVvGBxtAgMBAAECggEAHe4dW5aiOIgm8yPdn6cIzDnyvYeVxC7gbcF6uSUJLZa1 +VbWablpox0Bs8OHDTO+fGvjZ/WJ2Y8wD8SSPi469xs9B1S0wfUxOLkWNyr/xqgzH +9jgdrQ0CBwMRaJFDXFhtZPO9fKNUSVX9i1mpQG6d6T5YeV0c/zfrwoFAkNxEWuaG +hmp+F7OW3QcvZhLm3jJo2PnWpVfhbcMyw2MDVfSVzcZrZS1qFp0AvalcOI3z1QWd +1CsJYVASc7adyfev0qJ64EmgdI6sIBbI1cxb7gDLDX48f2rSwznEvgQ1gKUdZ5a5 +FBI/hSV8XkDDYNzaniFeQavHZ8BjrU66UxCd0x3JoQKBgQDaW54nommnUQYzOmYB +0tdAHBDrxjSQt5aUf+XngWMykpIYEYA0rtS5Q1i4CUVK+F4bKfCiURf4PSyp9vOs +4DLOC+e3O+ZGbXbIgmi9SuU6//T+wJY/NHppo1rIZldTMNNCdltW4nroO0ROSebW +Ot8I0JDLexwS6wedfGsnirggIQKBgQC1evu21yor0pZe6laPzsnnArX7NSdPiA8L +TidODrGXO9PzB2CifaQENBFe4SHKTVk5FtqUd5I0EJ32yUBDHbnqfVIXV7/juZxA +Ez98NyD70wT4FmiHVAG2SrN96NfaRASBaWlGk8oAA1P4pOQao6IVpE4ICQua1qXZ +jVa6gSIizQKBgHzKS1J/YT478f0o7M8x/c4nvAvi1Hu4UBhCF9P8gAKQ/Wk/5zM0 +j2HpeTSEJ9O2qaGAkINTMQ2veG0SMySjZKWY7C4tz2aEfJQO42j6+pKDYTKOQc+R +YDwrHWDWDCYBC1s6ZOz8th0ucdUEKyZXsstI1tdTXjH5m/qG9n48qwnBAoGBAIqC +UclL0QjfUhshGW3qsthYjlIrUFR4EJtdS8xclJhLdkmfUNbTs7Htl3BlvWdsv3M+ +CReVZwXLxPRMx3oyHupkOZpNQ8xnpmSVDDgez1LDzKAYYjEfIatiujmX097nWNT4 +gfv24g4+fAFiwCHAz/1aiJ/DX5MOzkgLgq0Xz9jlAoGBAL521PPgAWdNngZh/3IZ +bcC88v3bgreZfw3FL6FEMiPD/lO4h021LqkwvicL9TlgsFxsdpZfEX01jMF4J23o +Ll0JQ4r6zPl0kO0dmeEN3vt/YWToekUfE9fOXjuXIPSgMU+8+xREO8PBtgE9q8WR +Nk49oeSsHE4dDm/hMnWUgRcp +-----END PRIVATE KEY----- diff --git a/SSL/ssl b/SSL/ssl new file mode 100644 index 0000000..92cb993 --- /dev/null +++ b/SSL/ssl @@ -0,0 +1,105 @@ +самоподписанный сертификат. проблема в том, что не полуается его в доверенные добавть + +1. Запустить эмулятор, потом добавить сертификат в sdcard/Download +adb push "/home/tatyana/Рабочий стол/ssl_test/localhost.crt" /sdcard/Download/ + + + +tatyana@fedora:~/Рабочий стол/ssl_test$ adb push "/home/tatyana/Рабочий стол/ssl_test/localhost.crt" + /sdcard/Download/ +/home/tatyana/Рабочий стол/...pped. 12.2 MB/s (1046 bytes in 0.000s) +tatyana@fedora:~/Рабочий стол/ssl_test$ + + +2. Сверзу нажать груг = хоум, Зайти в настройки устройства в самом эмуляторе + +3. Найти "Encryption and Credentials /«Шифрование и учетные данные + +4. Выбираем установка (второй пункт vpn ....) + +5. сертификат будет в загрузках (пункт 1) на карте + +6. Объединить ключ и сертификат +openssl pkcs12 -export -inkey localhost.key -in localhost.crt -out localhost.p12 + +7. перенести на устройство +adb push /home/tatyana/Рабочий\ стол/ssl_test/localhost.p12 /sdcard/Download/ + + +///////////////////////////////////////// +2 способ поместить в доверенные в ручную + +app/ + src/ + main/ + res/ + raw/ + your_cert.der + + + +Конечно! Ниже я подробно объясню, как доверять самоподписанному сертификату в эмуляторе Android для разработки, используя network_security_config.xml. Это наиболее безопасный и удобный способ для тестирования в приложениях без необходимости рутировать устройство или эмулятор. +Подробная инструкция: доверие к самоподписанному сертификату в Android через network_security_config.xml +Шаг 1. Подготовьте ваш сертификат +1.1. Экспортируйте сертификат в нужном формате + +Если у вас есть .p12 или .crt, убедитесь, что он в формате .der или .pem. + + Для этого используйте команду OpenSSL: + +bash + +# Извлечь сертификат из p12 в PEM +openssl pkcs12 -in localhost.p12 -clcerts -nokeys -out localhost.pem + +или +bash + +# Конвертировать PEM в DER (бинарный формат) +openssl x509 -in localhost.pem -outform DER -out localhost.der + +Важно: Назовите файл, например, your_cert.der. +1.2. Поместите сертификат в проект + + В вашем проекте Android создайте папку res/raw/ (если ее еще нет). + Поместите туда файл your_cert.der. + +Шаг 2. Создайте файл конфигурации network_security_config.xml + +Этот файл укажет вашему приложению доверять вашему сертификату. +2.1. Создайте папку и файл + + В вашем проекте перейдите по пути: app/src/main/res/xml/ + Если папки xml нет, создайте ее. + Внутри создайте файл network_security_config.xml. + +2.2. Напишите содержимое файла +xml + + + + + your.server.domain + + + + + + +Обратите внимание: + + Замените your.server.domain на ваш реальный сервер или IP, к которому вы подключаетесь. + Если вы хотите доверять всему трафику (не рекомендуется), можно оставить , а не указывать конкретный. + +Шаг 3. Обновите AndroidManifest.xml + +Добавьте ссылку на конфигурацию безопасности в ваш манифест: +xml + + + ... + + + diff --git a/src/AppRoutes.tsx b/src/AppRoutes.tsx index 3f72b07..0269bad 100644 --- a/src/AppRoutes.tsx +++ b/src/AppRoutes.tsx @@ -4,9 +4,9 @@ import Welcome from "./pages/Welcome"; import Login from "./pages/Login"; import Home from "./pages/Home"; import ForgotPasword from "./pages/ForgotPassword"; -import Courses from "./pages/Courses"; +import { Courses } from "./pages/Courses"; import CourseExercises from "./pages/CourseExercises"; -import Exercise from "./pages/Exercise"; +import { Exercise } from "./pages/Exercise"; import Settings from "./pages/Settings"; import CourseComplete from "./pages/CourseComplete"; diff --git a/src/pages/Courses old.tsx b/src/pages/Courses old.tsx new file mode 100644 index 0000000..b2b79c6 --- /dev/null +++ b/src/pages/Courses old.tsx @@ -0,0 +1,133 @@ +"use client" + +import { useState, useEffect } from 'react'; +import BottomNavigation from "../components/BottomNavigation" +import { useHistory } from "react-router-dom" +import HeaderNav from "../components/HeaderNav" +import { getRouteCourseExercises } from "../shared/consts/router" + +// Типизация Exercise +interface Exercise { + id: number; + title: string; + desc: string; + url_file_img: string; +} + +const ProgressLine = () => { + return ( +
+ ); +} + +const AnalitcsCards = () => { + return ( +
+
+
+ {/* Прогрессная часть */} + + + {/* Текст поверх линии */} +
Вы прошли реабилитацию на {85}%
+
+
+
+ ) +} + +export const Courses = () => { + const history = useHistory() + + // Внутренний компонент для загрузки упражнений + const Exercises = () => { + const [exercises, setExercises] = useState([]); + + +useEffect(() => { + fetch('/pacient/exercises') + .then(res => { + if (!res.ok) throw new Error('Ошибка при загрузке упражнений'); + return res.json(); + }) + .then((data: Exercise[]) => { + console.log('test'); + setExercises(data); + + }) + .catch(err => { + setError(err.message); + + }); +}, []); + + return ( +
+ {/* Можно добавить заголовок или обертку */} +
    + {exercises.map(ex => ( +
  • +

    {ex.title}

    +

    {ex.desc}

    + {/* Вставка изображения */} + {ex.title} +
  • + ))} +
+
+ ); + }; + + return ( +
+ + + + {/* Внедрение компонента упражнений */} + + + {/* Можно оставить закомментированный блок или убрать его */} + {/* +
+
+

Мои курсы

+ +
+
+ {courses.map((course) => ( +
history.push(getRouteCourseExercises(':id'))} + className="bg-white/30 backdrop-blur-2xl rounded-3xl p-6 border border-white/20 shadow-xl cursor-pointer hover:shadow-2xl transition-all duration-300 transform hover:scale-[1.02]" + > +
+ +
+

{course.name}

+
+
+
+

{course.progress}% завершено

+
+
+ {/* Иконка стрелки */} + {/**/} + {/*
+
+
+ ))} +
+
*/} + + +
+ ) +} \ No newline at end of file diff --git a/src/pages/Courses.tsx b/src/pages/Courses.tsx index 74a6409..1db36ae 100644 --- a/src/pages/Courses.tsx +++ b/src/pages/Courses.tsx @@ -1,93 +1,100 @@ "use client" -import type React from "react" +import { useState, useEffect } from 'react'; import BottomNavigation from "../components/BottomNavigation" -import { useHistory } from "react-router-dom" - +import { useHistory } from "react-router-dom" // для v5 import HeaderNav from "../components/HeaderNav" -import { getRouteCourseExercises } from "../shared/consts/router" +import { connect } from '../confconnect'; -const ProgressLine = () => { - return (
- ); +interface Course { + ID: number; + title: string; + desc: string; + url_file_img: string; } -const AnalitcsCards = () => { +export const Courses = () => { + const history = useHistory(); - {/* Analytics Cards */ } - return ( -
-
-
- {/* Прогрессная часть */} - + const [courses, setCourses] = useState([]); + const [error, setError] = useState(''); + const token = localStorage.getItem('authToken'); - {/* Текст поверх линии */} -
Вы прошли реабилитацию на {85}%
-
-
-
- ) -} + useEffect(() => { + if (!token) { + setError('Токен не найден'); + return; + } -const Home: React.FC = () => { - const history = useHistory() + connect.get('/pacient/courses', { + headers: { + Authorization: `Bearer ${token}`, + }, + }) + .then(response => { + console.log('Response status:', response.status); + + // Предполагаемая структура: + // response.data.courses — массив пользователей + const users = response.data.courses || []; - const courses = [ - { id: 1, name: "Восстановление колена", progress: 75, color: "from-[#2BACBE] to-[#2BACBE]/80" }, - { id: 2, name: "Укрепление спины", progress: 45, color: "from-[#5F5C5C] to-[#5F5C5C]/80" }, - { id: 3, name: "Реабилитация плеча", progress: 90, color: "from-[#2BACBE]/80 to-[#5F5C5C]" }, - ]; + // Собираем все курсы из всех пользователей + const allCourses: Course[] = []; + + users.forEach(user => { + if (user.Courses && Array.isArray(user.Courses)) { + user.Courses.forEach(course => { + // Можно добавить проверку или преобразование + allCourses.push({ + ID: course.id, // или course.ID, зависит от структуры + title: course.title, + desc: course.desc, + url_file_img: course.url_file_img, + }); + }); + } + }); + + setCourses(allCourses); + }) + .catch(error => { + if (error.response) { + console.error('Ошибка ответа сервера:', error.response.status, error.response.data); + setError(`Ошибка сервера: ${error.response.status}`); + } else if (error.request) { + console.error('Нет ответа от сервера:', error.request); + setError('Нет ответа от сервера'); + } else { + console.error('Ошибка при настройке запроса:', error.message); + setError(`Ошибка: ${error.message}`); + } + }); + }, [token]); return (
- + {error &&

{error}

} - {/* Courses */} -
-
-

Мои курсы

- {/* */} -
-
- {courses.map((course) => ( -
history.push(getRouteCourseExercises(':id'))} - className="bg-white/30 backdrop-blur-2xl rounded-3xl p-6 border border-white/20 shadow-xl cursor-pointer hover:shadow-2xl transition-all duration-300 transform hover:scale-[1.02]" - > -
- -
-

{course.name}

-
-
-
-

{course.progress}% завершено

-
-
- - -
-
+ {/* Выводим список курсов */} +
+ {courses.length > 0 ? ( + courses.map(course => ( +
+

{course.title}

+

{course.desc}

+ {course.url_file_img && ( + {course.title} + )}
- ))} -
+ )) + ) : ( + !error &&

Нет назначенных курсов

+ )}
- ) -} - -export default Home + ); +}; \ No newline at end of file diff --git a/src/pages/Exercise.tsx b/src/pages/Exercise.tsx index 12c44b0..b43f781 100644 --- a/src/pages/Exercise.tsx +++ b/src/pages/Exercise.tsx @@ -1,5 +1,5 @@ "use client" -import type React from "react" + import { useState, useEffect } from "react" import { useHistory } from "react-router-dom" @@ -9,7 +9,19 @@ import HeaderNav from "../components/HeaderNav" import video from "../assets/img/video.mov" -const Exercise: React.FC = () => { +export interface Exercise { + id: number; + title: string; + desc: string; + url_file: string; + url_file_img: string; +} + + + + + +export const Exercise = () => { const history = useHistory() // const { id } = useParams<{ id: string }>() const [isPlaying, setIsPlaying] = useState(false) @@ -104,7 +116,7 @@ const Exercise: React.FC = () => { - {/* Video/Image Section */} +
@@ -129,7 +141,7 @@ const Exercise: React.FC = () => { {isPlaying ? : }
- {/* Live indicators */} + {isPlaying && (
@@ -138,14 +150,14 @@ const Exercise: React.FC = () => {
)} - {/* Timer Display */} +
{formatTime(currentTime)}
- {/* Exercise Steps - Vertical Scroll */} +
{exerciseSteps.map((step, index) => (
{ ) } -export default Exercise + diff --git a/ответы сервера b/ответы сервера new file mode 100644 index 0000000..5d92415 --- /dev/null +++ b/ответы сервера @@ -0,0 +1,70 @@ +curl -i -X OPTIONS http://localhost:8093/auth/api/login -H "Origin: http://localhost" -H "Access-Control-Request-Method: POST" + + + +tatyana@fedora:~/Рабочий стол/RehabMobileApp/RehabMobileApp/android$ curl -i -X OPTIONS http://localhost:8093/auth/api/login -H "Origin: http://localhost" -H "Access-Control-Request-Method: POST" +HTTP/1.1 204 No Content +Access-Control-Allow-Credentials: true +Access-Control-Allow-Headers: Accept-Language,Access-Control-Allow-Origin,Authorization,Content-Language,X-Csrf-Token,X-Xsrf-Token,Origin,X-Requested-With,Content-Type,Accept +Access-Control-Allow-Methods: PUT,GET,POST,PATCH,OPTIONS,DELETE +Access-Control-Allow-Origin: http://localhost +Vary: Origin +Vary: Access-Control-Request-Method +Vary: Access-Control-Request-Headers +Date: Fri, 22 Aug 2025 08:09:40 GMT + + +tatyana@fedora:~/Рабочий стол/RehabMobileApp/RehabMobileApp/android$ curl -i -X POST http://localhost:8093/auth/api/login \ +-H "Origin: http://localhost" \ +-H "Content-Type: application/json" \ +-d '{"Email": "krasnova@mail.ru", "Password": "Tatyana1"}' + + +curl -H "Authorization: Bearer YOUR_TOKEN" http://localhost:8093/pacient/courses + + + + "courses": [ + { + "CreatedAt": "2025-08-19T10:50:04.454157+03:00", + "UpdatedAt": "2025-08-19T10:50:04.454157+03:00", + "DeletedAt": null, + "ID": 25, + "Name": "krasnova@mail.ru", + "Email": "krasnova@mail.ru", + "Password": "$2a$10$ryI/OSON56IlmDL0sAHhmOk9vhFkuTRieFlj32p/cKYCrPWGOJZKC", + "Role": "", + "UserDoctorID": 0, + "ExerciseCourses": null, + "Courses": [ + { + "ID": 0, + "CreatedAt": "2025-08-15T11:46:58.879953+03:00", + "UpdatedAt": "2025-08-15T11:46:58.879953+03:00", + "DeletedAt": null, + "id": 1, + "title": "Восстановление", + "desc": "Восстановиться", + "time_course": "", + "day_course": 0, + "url_file_img": "https://vivasport.ru/assets/images/news/12555/spine-training-1.webp", + "users": null, + "exercises": null + } + ] + }, + { + "CreatedAt": "2025-08-20T14:19:19.754239+03:00", + "UpdatedAt": "2025-08-20T14:19:19.754239+03:00", + "DeletedAt": null, + "ID": 26, + "Name": "krasnova5@mail.ru", + "Email": "krasnova5@mail.ru", + "Password": "$2a$10$sdcCn51708LV5y6wsmOho.dLtpDGf43muXobIcvGYQ1.TtB1WxFju", + "Role": "", + "UserDoctorID": 0, + "ExerciseCourses": null, + "Courses": [] + } + ], + "sessionname": "krasnova@mail.ru" \ No newline at end of file