v0.4.0
add: - legal .md files - routers - home view - rewrite App.vue - update footer / header - update features
This commit is contained in:
Generated
+85
-2
@@ -1,24 +1,27 @@
|
|||||||
{
|
{
|
||||||
"name": "uvpn.shop",
|
"name": "uvpn.shop",
|
||||||
"version": "0.2.0",
|
"version": "0.3.1",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "uvpn.shop",
|
"name": "uvpn.shop",
|
||||||
"version": "0.2.0",
|
"version": "0.3.1",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@lucide/vue": "^1.16.0",
|
"@lucide/vue": "^1.16.0",
|
||||||
"@tailwindcss/vite": "^4.3.0",
|
"@tailwindcss/vite": "^4.3.0",
|
||||||
"@tresjs/cientos": "^5.7.1",
|
"@tresjs/cientos": "^5.7.1",
|
||||||
"@tresjs/core": "^5.8.1",
|
"@tresjs/core": "^5.8.1",
|
||||||
|
"marked": "^18.0.5",
|
||||||
"swiper": "^12.1.4",
|
"swiper": "^12.1.4",
|
||||||
"tailwindcss": "^4.3.0",
|
"tailwindcss": "^4.3.0",
|
||||||
"three": "^0.184.0",
|
"three": "^0.184.0",
|
||||||
"vue": "^3.5.32",
|
"vue": "^3.5.32",
|
||||||
|
"vue-router": "^4.6.4",
|
||||||
"vue3-simple-icons": "^16.10.0"
|
"vue3-simple-icons": "^16.10.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@tailwindcss/typography": "^0.5.19",
|
||||||
"@vitejs/plugin-vue": "^6.0.6",
|
"@vitejs/plugin-vue": "^6.0.6",
|
||||||
"vite": "^8.0.8",
|
"vite": "^8.0.8",
|
||||||
"vite-plugin-vue-devtools": "^8.1.1"
|
"vite-plugin-vue-devtools": "^8.1.1"
|
||||||
@@ -1134,6 +1137,19 @@
|
|||||||
"node": ">= 20"
|
"node": ">= 20"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@tailwindcss/typography": {
|
||||||
|
"version": "0.5.19",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.19.tgz",
|
||||||
|
"integrity": "sha512-w31dd8HOx3k9vPtcQh5QHP9GwKcgbMp87j58qi6xgiBnFFtKEAgCWnDw4qUT8aHwkCp8bKvb/KGKWWHedP0AAg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"postcss-selector-parser": "6.0.10"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@tailwindcss/vite": {
|
"node_modules/@tailwindcss/vite": {
|
||||||
"version": "4.3.0",
|
"version": "4.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.3.0.tgz",
|
||||||
@@ -1665,6 +1681,19 @@
|
|||||||
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
|
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/cssesc": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"cssesc": "bin/cssesc"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/csstype": {
|
"node_modules/csstype": {
|
||||||
"version": "3.2.3",
|
"version": "3.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
|
||||||
@@ -2289,6 +2318,18 @@
|
|||||||
"@jridgewell/sourcemap-codec": "^1.5.5"
|
"@jridgewell/sourcemap-codec": "^1.5.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/marked": {
|
||||||
|
"version": "18.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/marked/-/marked-18.0.5.tgz",
|
||||||
|
"integrity": "sha512-S6GcvALHg6K4ohtu4E7x0a1AqhAjp6cV8KhLSyN9qVapnzJkusVBxZRcIU9AeYsbe6P1hKDusSbEOzGyyuce6w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"marked": "bin/marked.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 20"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/meshoptimizer": {
|
"node_modules/meshoptimizer": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/meshoptimizer/-/meshoptimizer-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/meshoptimizer/-/meshoptimizer-1.1.1.tgz",
|
||||||
@@ -2441,6 +2482,20 @@
|
|||||||
"node": "^10 || ^12 || >=14"
|
"node": "^10 || ^12 || >=14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/postcss-selector-parser": {
|
||||||
|
"version": "6.0.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz",
|
||||||
|
"integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"cssesc": "^3.0.0",
|
||||||
|
"util-deprecate": "^1.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/potpack": {
|
"node_modules/potpack": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/potpack/-/potpack-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/potpack/-/potpack-1.0.2.tgz",
|
||||||
@@ -2799,6 +2854,13 @@
|
|||||||
"browserslist": ">= 4.21.0"
|
"browserslist": ">= 4.21.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/util-deprecate": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/vite": {
|
"node_modules/vite": {
|
||||||
"version": "8.0.14",
|
"version": "8.0.14",
|
||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-8.0.14.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-8.0.14.tgz",
|
||||||
@@ -3001,6 +3063,27 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/vue-router": {
|
||||||
|
"version": "4.6.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.6.4.tgz",
|
||||||
|
"integrity": "sha512-Hz9q5sa33Yhduglwz6g9skT8OBPii+4bFn88w6J+J4MfEo4KRRpmiNG/hHHkdbRFlLBOqxN8y8gf2Fb0MTUgVg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@vue/devtools-api": "^6.6.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/posva"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"vue": "^3.5.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/vue-router/node_modules/@vue/devtools-api": {
|
||||||
|
"version": "6.6.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz",
|
||||||
|
"integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/vue3-simple-icons": {
|
"node_modules/vue3-simple-icons": {
|
||||||
"version": "16.10.0",
|
"version": "16.10.0",
|
||||||
"resolved": "https://registry.npmjs.org/vue3-simple-icons/-/vue3-simple-icons-16.10.0.tgz",
|
"resolved": "https://registry.npmjs.org/vue3-simple-icons/-/vue3-simple-icons-16.10.0.tgz",
|
||||||
|
|||||||
+4
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "uvpn.shop",
|
"name": "uvpn.shop",
|
||||||
"version": "0.3.1",
|
"version": "0.4.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -13,13 +13,16 @@
|
|||||||
"@tailwindcss/vite": "^4.3.0",
|
"@tailwindcss/vite": "^4.3.0",
|
||||||
"@tresjs/cientos": "^5.7.1",
|
"@tresjs/cientos": "^5.7.1",
|
||||||
"@tresjs/core": "^5.8.1",
|
"@tresjs/core": "^5.8.1",
|
||||||
|
"marked": "^18.0.5",
|
||||||
"swiper": "^12.1.4",
|
"swiper": "^12.1.4",
|
||||||
"tailwindcss": "^4.3.0",
|
"tailwindcss": "^4.3.0",
|
||||||
"three": "^0.184.0",
|
"three": "^0.184.0",
|
||||||
"vue": "^3.5.32",
|
"vue": "^3.5.32",
|
||||||
|
"vue-router": "^4.6.4",
|
||||||
"vue3-simple-icons": "^16.10.0"
|
"vue3-simple-icons": "^16.10.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@tailwindcss/typography": "^0.5.19",
|
||||||
"@vitejs/plugin-vue": "^6.0.6",
|
"@vitejs/plugin-vue": "^6.0.6",
|
||||||
"vite": "^8.0.8",
|
"vite": "^8.0.8",
|
||||||
"vite-plugin-vue-devtools": "^8.1.1"
|
"vite-plugin-vue-devtools": "^8.1.1"
|
||||||
|
|||||||
@@ -0,0 +1,107 @@
|
|||||||
|
# Пользовательское соглашение
|
||||||
|
|
||||||
|
### 1. Общие положения
|
||||||
|
|
||||||
|
1.1. Настоящее Пользовательское соглашение (далее — **«Соглашение»** ) регулирует порядок использования онлайн-сервиса (далее — **«Сервис»** ), предоставляемого Администрацией.
|
||||||
|
|
||||||
|
1.2. Используя Сервис, включая запуск бота, регистрацию, оплату услуг или получение доступа к материалам, Пользователь подтверждает, что полностью ознакомился с условиями настоящего Соглашения и принимает их в полном объёме.
|
||||||
|
|
||||||
|
1.3. В случае несогласия с условиями Соглашения Пользователь обязан прекратить использование Сервиса.
|
||||||
|
|
||||||
|
### 2. Характер услуг и цифровых товаров
|
||||||
|
|
||||||
|
2.1. Сервис предоставляет цифровые товары и услуги нематериального характера, включая, но не ограничиваясь: информационные материалы, обучающие программы, консультации, цифровые продукты и сервисные услуги.
|
||||||
|
|
||||||
|
2.2. Материалы, предоставляемые через Сервис, могут включать:
|
||||||
|
|
||||||
|
* информацию из открытых источников;
|
||||||
|
* авторские материалы Администрации и/или третьих лиц;
|
||||||
|
* аналитические обзоры, подборки, рекомендации, структурированные данные.
|
||||||
|
|
||||||
|
2.3. Пользователь осознаёт и соглашается, что ценность цифровых товаров и услуг Сервиса заключается **в систематизации, анализе, форме подачи, сопровождении, поддержке и обновлениях** , а не в эксклюзивности отдельных фрагментов информации.
|
||||||
|
|
||||||
|
2.4. Сервис не заявляет и не гарантирует уникальность, исключительность или недоступность отдельных элементов материалов вне Сервиса.
|
||||||
|
|
||||||
|
### 3. Отказ от гарантий и ответственности
|
||||||
|
|
||||||
|
3.1. Сервис предоставляется на условиях **«AS IS» («как есть»)** .
|
||||||
|
|
||||||
|
3.2. Администрация не гарантирует:
|
||||||
|
|
||||||
|
* соответствие Сервиса ожиданиям Пользователя;
|
||||||
|
* достижение каких-либо финансовых, коммерческих, профессиональных или иных результатов;
|
||||||
|
* бесперебойную и безошибочную работу Сервиса.
|
||||||
|
|
||||||
|
3.3. Администрация не несёт ответственности за:
|
||||||
|
|
||||||
|
* любые прямые или косвенные убытки, включая упущенную выгоду;
|
||||||
|
* последствия применения Пользователем полученных материалов;
|
||||||
|
* действия или бездействие третьих лиц;
|
||||||
|
* временные технические сбои и ограничения доступа.
|
||||||
|
|
||||||
|
3.4. Все решения о применении материалов, рекомендаций и услуг принимаются Пользователем **самостоятельно и на его риск** .
|
||||||
|
|
||||||
|
### 4. Законность использования
|
||||||
|
|
||||||
|
4.1. Сервис не предназначен для поощрения, организации или содействия противоправной деятельности.
|
||||||
|
|
||||||
|
4.2. Пользователь обязуется использовать Сервис исключительно в рамках применимого законодательства и правил третьих сторон.
|
||||||
|
|
||||||
|
4.3. Ответственность за законность использования материалов и услуг Сервиса полностью возлагается на Пользователя.
|
||||||
|
|
||||||
|
### 5. Интеллектуальная собственность
|
||||||
|
|
||||||
|
5.1. Все материалы, размещённые в Сервисе, охраняются законодательством об интеллектуальной собственности.
|
||||||
|
|
||||||
|
5.2. Пользователю запрещается копировать, распространять, перепродавать, передавать третьим лицам или иным образом использовать материалы Сервиса без разрешения правообладателя.
|
||||||
|
|
||||||
|
5.3. Нарушение прав интеллектуальной собственности может повлечь ограничение доступа к Сервису без компенсации.
|
||||||
|
|
||||||
|
### 6. Ограничение доступа
|
||||||
|
|
||||||
|
6.1. Администрация вправе приостановить или ограничить доступ Пользователя к Сервису в случае:
|
||||||
|
|
||||||
|
* нарушения условий настоящего Соглашения;
|
||||||
|
* выявления злоупотреблений;
|
||||||
|
* требований законодательства или платёжных провайдеров.
|
||||||
|
|
||||||
|
6.2. Ограничение доступа не освобождает Пользователя от обязательств, возникших ранее.
|
||||||
|
|
||||||
|
6.3. Администрация оставляет за собой право отказывать в обслуживании Пользователям, чьи действия могут создавать повышенные риски для Сервиса, платёжных провайдеров или третьих лиц.
|
||||||
|
|
||||||
|
### 7. Платежи и возвраты
|
||||||
|
|
||||||
|
7.1. Оплата услуг и цифровых товаров производится на условиях, указанных в Сервисе до момента оплаты.
|
||||||
|
|
||||||
|
7.2. В связи с нематериальным характером цифровых товаров и услуг, **возврат денежных средств после предоставления доступа не осуществляется** , за исключением случаев, указанных ниже.
|
||||||
|
|
||||||
|
7.3. Возврат средств возможен только если:
|
||||||
|
|
||||||
|
* услуга не была оказана по технической вине Сервиса;
|
||||||
|
* доступ к цифровому товару фактически не был предоставлен.
|
||||||
|
|
||||||
|
7.4. Для рассмотрения вопроса о возврате Пользователь обязан обратиться в службу поддержки в течение **24 часов** с момента оплаты.
|
||||||
|
|
||||||
|
7.5. Решение о возврате принимается Администрацией индивидуально.
|
||||||
|
|
||||||
|
7.6. Пользователь подтверждает, что обязуется **не инициировать возврат платежа (chargeback)** через платёжные системы без предварительного обращения в службу поддержки Сервиса.
|
||||||
|
|
||||||
|
### 8. Конфиденциальность
|
||||||
|
|
||||||
|
8.1. Администрация может собирать минимально необходимые технические данные для обеспечения работы Сервиса.
|
||||||
|
|
||||||
|
8.2. Администрация принимает разумные меры для защиты данных, однако не гарантирует абсолютную безопасность передаваемой информации.
|
||||||
|
|
||||||
|
### 9. Изменение условий
|
||||||
|
|
||||||
|
9.1. Администрация вправе вносить изменения в настоящее Соглашение.
|
||||||
|
|
||||||
|
9.2. Актуальная версия Соглашения публикуется в Сервисе.
|
||||||
|
|
||||||
|
9.3. Продолжение использования Сервиса означает согласие Пользователя с обновлёнными условиями.
|
||||||
|
|
||||||
|
### 10. Контактная информация
|
||||||
|
|
||||||
|
10.1. По всем вопросам Пользователь может обратиться в службу поддержки через форму в самом боте.
|
||||||
|
|
||||||
|
**Используя Сервис (в том числе запуская бота и/или вводя команду /start), Пользователь подтверждает, что ознакомлен с настоящим Соглашением и принимает его условия в полном объёме.**
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
# Политика конфиденциальности
|
||||||
|
|
||||||
|
Политика конфиденциальности регулирует сбор, использование и защиту информации пользователей сервиса. Собираются идентификаторы аккаунта, техническая информация и история взаимодействий. Данные используются для обеспечения работы сервиса, связи с пользователем и анализа. Передача информации третьим лицам возможна только в законодательно установленных случаях или с согласия пользователя. Хранение данных осуществляется в течение необходимого срока, их защита — в разумных пределах. Пользователь самостоятельно несёт ответственность за риски, связанные с передачей данных. Администрация вправе вносить изменения в Политику без уведомления — согласие считается принятым при дальнейшем использовании сервиса.
|
||||||
|
|
||||||
|
**1. Общие положения**
|
||||||
|
|
||||||
|
1.1. Настоящая Политика конфиденциальности (далее — «Политика») регулирует порядок обработки и защиты информации, которую Пользователь передаёт при использовании сервиса (далее — «Сервис»).
|
||||||
|
|
||||||
|
1.2. Используя Сервис, Пользователь подтверждает своё согласие с условиями Политики. Если Пользователь не согласен с условиями — он обязан прекратить использование Сервиса.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**2. Сбор информации**
|
||||||
|
|
||||||
|
2.1. Сервис может собирать следующие типы данных:
|
||||||
|
|
||||||
|
* идентификаторы аккаунта (логин, ID, никнейм и т.п.);
|
||||||
|
* техническую информацию (IP-адрес, данные о браузере, устройстве и операционной системе);
|
||||||
|
* историю взаимодействий с Сервисом.
|
||||||
|
* 2.2. Сервис не требует от Пользователя предоставления паспортных данных, документов, фотографий или другой личной информации, кроме минимально необходимой для работы.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**3. Использование информации**
|
||||||
|
|
||||||
|
3.1. Сервис может использовать полученную информацию исключительно для:
|
||||||
|
|
||||||
|
* обеспечения работы функционала;
|
||||||
|
* связи с Пользователем (в том числе для уведомлений и поддержки);
|
||||||
|
* анализа и улучшения работы Сервиса.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**4. Передача информации третьим лицам**
|
||||||
|
|
||||||
|
4.1. Администрация не передаёт полученные данные третьим лицам, за исключением случаев:
|
||||||
|
|
||||||
|
* если это требуется по закону;
|
||||||
|
* если это необходимо для исполнения обязательств перед Пользователем (например, при работе с платёжными системами);
|
||||||
|
* если Пользователь сам дал на это согласие.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**5. Хранение и защита данных**
|
||||||
|
|
||||||
|
5.1. Данные хранятся в течение срока, необходимого для достижения целей обработки.
|
||||||
|
|
||||||
|
5.2. Администрация принимает разумные меры для защиты данных, но не гарантирует абсолютную безопасность информации при передаче через интернет.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**6. Отказ от ответственности**
|
||||||
|
|
||||||
|
6.1. Пользователь понимает и соглашается, что передача информации через интернет всегда сопряжена с рисками.
|
||||||
|
|
||||||
|
6.2. Администрация не несёт ответственности за утрату, кражу или раскрытие данных, если это произошло по вине третьих лиц или самого Пользователя.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**7. Изменения в Политике**
|
||||||
|
|
||||||
|
7.1. Администрация вправе изменять условия Политики без предварительного уведомления.
|
||||||
|
|
||||||
|
7.2. Продолжение использования Сервиса после внесения изменений означает согласие Пользователя с новой редакцией Политики.
|
||||||
+7
-16
@@ -1,25 +1,16 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="bg-gray-50 dark:bg-gray-950 text-gray-900 dark:text-white font-sans antialiased min-h-screen">
|
<div class="bg-gray-50 dark:bg-gray-950 text-gray-900 dark:text-white font-sans antialiased min-h-screen flex flex-col">
|
||||||
<AppHeader />
|
<AppHeader />
|
||||||
<HeroSection />
|
|
||||||
<FeaturesSection />
|
<main class="grow">
|
||||||
<CompatibilitySection />
|
<RouterView />
|
||||||
<PricingSection />
|
</main>
|
||||||
<StepsSection />
|
|
||||||
<TestimonialsSection />
|
|
||||||
<FaqSection />
|
|
||||||
<AppFooter />
|
<AppFooter />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import AppHeader from './components/AppHeader.vue'
|
import AppHeader from './components/AppHeader.vue'
|
||||||
import HeroSection from './components/HeroSection.vue'
|
|
||||||
import FeaturesSection from './components/FeaturesSection.vue'
|
|
||||||
import CompatibilitySection from './components/CompatibilitySection.vue'
|
|
||||||
import PricingSection from './components/PricingSection.vue'
|
|
||||||
import StepsSection from './components/StepsSection.vue'
|
|
||||||
import TestimonialsSection from './components/TestimonialsSection.vue'
|
|
||||||
import FaqSection from './components/FaqSection.vue'
|
|
||||||
import AppFooter from './components/AppFooter.vue'
|
import AppFooter from './components/AppFooter.vue'
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1 +1,2 @@
|
|||||||
@import "tailwindcss";
|
@import "tailwindcss";
|
||||||
|
@plugin "@tailwindcss/typography";
|
||||||
|
|||||||
@@ -6,9 +6,9 @@
|
|||||||
<span class="text-lg font-bold">uVPN</span>
|
<span class="text-lg font-bold">uVPN</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex gap-6 text-gray-500 dark:text-gray-400 text-sm">
|
<div class="flex gap-6 text-gray-500 dark:text-gray-400 text-sm">
|
||||||
<a href="#" class="hover:text-gray-900 dark:hover:text-gray-400 dark:text-white transition">Политика конфиденциальности</a>
|
<router-link to="/docs/policy" class="hover:text-gray-900 dark:hover:text-white text-gray-500 dark:text-gray-400 transition hover:cursor-pointer">Политика конфиденциальности</router-link>
|
||||||
<a href="#" class="hover:text-gray-900 dark:hover:text-gray-400 dark:text-white transition">Условия использования</a>
|
<router-link to="/docs/agreement" class="hover:text-gray-900 dark:hover:text-white text-gray-500 dark:text-gray-400 hover:cursor-pointer">Условия использования</router-link>
|
||||||
<a href="#" class="hover:text-gray-900 dark:hover:text-gray-400 dark:text-white transition">Поддержка</a>
|
<a href="mailto:support@uvpn.shop" target="_blank" class="hover:text-gray-900 dark:hover:text-white text-gray-500 dark:text-gray-400 transition">Поддержка</a>
|
||||||
</div>
|
</div>
|
||||||
<p class="text-gray-500 dark:text-gray-400 text-sm">
|
<p class="text-gray-500 dark:text-gray-400 text-sm">
|
||||||
© 2026 uVPN. Все права защищены.
|
© 2026 uVPN. Все права защищены.
|
||||||
|
|||||||
@@ -1,31 +1,29 @@
|
|||||||
<template>
|
<template>
|
||||||
<header class="flex items-center justify-between px-6 py-4 md:px-12 border-b border-gray-200 dark:border-gray-800 sticky top-0 bg-white/80 dark:bg-gray-900/80 backdrop-blur-xs z-50">
|
<header class="flex items-center justify-between px-6 py-4 md:px-12 border-b border-gray-200 dark:border-gray-800 sticky top-0 bg-white/80 dark:bg-gray-900/80 backdrop-blur-xs z-50">
|
||||||
<div class="flex items-center gap-3">
|
|
||||||
|
<!-- Логотип -->
|
||||||
|
<router-link to="/" class="flex items-center gap-3 hover:opacity-90 transition">
|
||||||
<img src="/images/logo.png" alt="uVPN logo" class="size-8" />
|
<img src="/images/logo.png" alt="uVPN logo" class="size-8" />
|
||||||
<span class="text-xl font-bold tracking-tight">uVPN</span>
|
<span class="text-xl font-bold tracking-tight">uVPN</span>
|
||||||
</div>
|
</router-link>
|
||||||
|
|
||||||
<!-- Якорные ссылки -->
|
<!-- Навигация: теперь это router-link с хэшем секции -->
|
||||||
<nav class="hidden md:flex items-center gap-6 text-sm text-gray-500 dark:text-gray-400">
|
<nav class="hidden md:flex items-center gap-6 text-sm text-gray-500 dark:text-gray-400">
|
||||||
<a href="#hero" @click.prevent="scrollTo('hero')" class="hover:text-gray-900 dark:hover:text-gray-400 dark:text-white transition">Главная</a>
|
<router-link :to="{ path: '/', hash: '#hero' }" class="hover:text-gray-900 dark:hover:text-white text-gray-500 dark:text-gray-400 transition">Главная</router-link>
|
||||||
<a href="#features" @click.prevent="scrollTo('features')" class="hover:text-gray-900 dark:hover:text-gray-400 dark:text-white transition">Преимущества</a>
|
<router-link :to="{ path: '/', hash: '#features' }" class="hover:text-gray-900 dark:hover:text-white text-gray-500 dark:text-gray-400 transition">Преимущества</router-link>
|
||||||
<a href="#pricing" @click.prevent="scrollTo('pricing')" class="hover:text-gray-900 dark:hover:text-gray-400 dark:text-white transition">Тарифы</a>
|
<router-link :to="{ path: '/', hash: '#pricing' }" class="hover:text-gray-900 dark:hover:text-white text-gray-500 dark:text-gray-400 transition">Тарифы</router-link>
|
||||||
<a href="#steps" @click.prevent="scrollTo('steps')" class="hover:text-gray-900 dark:hover:text-gray-400 dark:text-white transition">Как начать</a>
|
<router-link :to="{ path: '/', hash: '#steps' }" class="hover:text-gray-900 dark:hover:text-white text-gray-500 dark:text-gray-400 transition">Как начать</router-link>
|
||||||
<a href="#testimonials" @click.prevent="scrollTo('testimonials')" class="hover:text-gray-900 dark:hover:text-gray-400 dark:text-white transition">Отзывы</a>
|
<router-link :to="{ path: '/', hash: '#testimonials' }" class="hover:text-gray-900 dark:hover:text-white text-gray-500 dark:text-gray-400 transition">Отзывы</router-link>
|
||||||
<a href="#footer" @click.prevent="scrollTo('faq')" class="hover:text-gray-900 dark:hover:text-gray-400 dark:text-white transition">FAQ</a>
|
<router-link :to="{ path: '/', hash: '#faq' }" class="hover:text-gray-900 dark:hover:text-white text-gray-500 dark:text-gray-400 transition">FAQ</router-link>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<button class="bg-blue-600 dark:bg-blue-500 hover:bg-blue-700 transition px-5 py-2 rounded-lg font-medium text-sm text-white">
|
<!-- Кнопка тарифов -->
|
||||||
|
<router-link :to="{ path: '/', hash: '#pricing' }" class="bg-blue-600 dark:bg-blue-500 hover:bg-blue-700 transition px-5 py-2 rounded-lg font-medium text-sm text-white">
|
||||||
Попробовать бесплатно
|
Попробовать бесплатно
|
||||||
</button>
|
</router-link>
|
||||||
</header>
|
</header>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
const scrollTo = (id) => {
|
// Скрипты больше не нужны, всю магию сделает роутер!
|
||||||
const el = document.getElementById(id)
|
</script>
|
||||||
if (el) {
|
|
||||||
el.scrollIntoView({ behavior: 'smooth' })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|||||||
@@ -0,0 +1,85 @@
|
|||||||
|
<template>
|
||||||
|
<div class="max-w-4xl mx-auto px-4 py-12">
|
||||||
|
<!-- Навигация: теперь используем router-link вместо обычных кнопок -->
|
||||||
|
<nav class="flex space-x-6 mb-8 pb-2">
|
||||||
|
<router-link
|
||||||
|
to="/docs/policy"
|
||||||
|
class="px-1 py-2 text-sm font-medium transition-colors border-b-2 -mb-2.5"
|
||||||
|
active-class="border-blue-600 text-blue-600 dark:text-blue-400 dark:border-blue-400"
|
||||||
|
inactive-class="border-transparent text-slate-500 hover:text-slate-700 dark:text-slate-400"
|
||||||
|
>
|
||||||
|
Политика конфиденциальности
|
||||||
|
</router-link>
|
||||||
|
|
||||||
|
<router-link
|
||||||
|
to="/docs/agreement"
|
||||||
|
class="px-1 py-2 text-sm font-medium transition-colors border-b-2 -mb-2.5"
|
||||||
|
active-class="border-blue-600 text-blue-600 dark:text-blue-400 dark:border-blue-400"
|
||||||
|
inactive-class="border-transparent text-slate-500 hover:text-slate-700 dark:text-slate-400"
|
||||||
|
>
|
||||||
|
Пользовательское соглашение
|
||||||
|
</router-link>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<!-- Область отображения Markdown (Tailwind Typography) -->
|
||||||
|
<div
|
||||||
|
v-if="htmlContent"
|
||||||
|
v-html="htmlContent"
|
||||||
|
class="prose prose-slate max-w-none dark:prose-invert"
|
||||||
|
></div>
|
||||||
|
|
||||||
|
<div v-else-if="isLoading" class="text-center py-12 text-slate-500 dark:text-slate-400">
|
||||||
|
Загрузка документа...
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else class="text-center py-12 text-red-500">
|
||||||
|
Документ не найден или произошла ошибка.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, watch, onMounted } from 'vue'
|
||||||
|
import { marked } from 'marked'
|
||||||
|
|
||||||
|
// Принимаем параметр docName из роутера
|
||||||
|
const props = defineProps({
|
||||||
|
docName: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const htmlContent = ref('')
|
||||||
|
const isLoading = ref(true)
|
||||||
|
|
||||||
|
// Функция загрузки md-файла
|
||||||
|
const fetchMarkdown = async (name) => {
|
||||||
|
isLoading.value = true
|
||||||
|
htmlContent.value = ''
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Загрузка из папки public/docs/
|
||||||
|
const response = await fetch(`/docs/${name}.md`)
|
||||||
|
if (!response.ok) throw new Error('Файл отсутствует')
|
||||||
|
|
||||||
|
const markdownText = await response.text()
|
||||||
|
htmlContent.value = marked(markdownText)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Ошибка отрисовки md:', error)
|
||||||
|
htmlContent.value = ''
|
||||||
|
} finally {
|
||||||
|
isLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Загружаем файл при первой инициализации страницы
|
||||||
|
onMounted(() => {
|
||||||
|
fetchMarkdown(props.docName)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Отслеживаем изменение ссылки (например, если пользователь перешел с policy на agreement)
|
||||||
|
watch(() => props.docName, (newName) => {
|
||||||
|
fetchMarkdown(newName)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
@@ -38,7 +38,7 @@
|
|||||||
<div class="bg-white dark:bg-gray-900 rounded-2xl p-8 text-center hover:bg-gray-100 dark:hover:bg-gray-800 transition border border-gray-200 dark:border-gray-800">
|
<div class="bg-white dark:bg-gray-900 rounded-2xl p-8 text-center hover:bg-gray-100 dark:hover:bg-gray-800 transition border border-gray-200 dark:border-gray-800">
|
||||||
<RefreshCw class="w-12 h-12 text-blue-600 dark:text-blue-400 mx-auto mb-4" />
|
<RefreshCw class="w-12 h-12 text-blue-600 dark:text-blue-400 mx-auto mb-4" />
|
||||||
<h3 class="text-xl font-semibold mb-3">Стабильная работа</h3>
|
<h3 class="text-xl font-semibold mb-3">Стабильная работа</h3>
|
||||||
<p class="text-gray-500 dark:text-gray-400">Автоматические обновления конфигураций — VPN работает даже при новых блокировках</p>
|
<p class="text-gray-500 dark:text-gray-400">Автоматические обновления конфигураций — мы первые подстраиваемся под новые условия</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
+6
-1
@@ -1,5 +1,10 @@
|
|||||||
import { createApp } from 'vue'
|
import { createApp } from 'vue'
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
|
import router from './router'
|
||||||
import './assets/styles.css'
|
import './assets/styles.css'
|
||||||
|
|
||||||
createApp(App).mount('#app')
|
const app = createApp(App)
|
||||||
|
|
||||||
|
app.use(router)
|
||||||
|
|
||||||
|
app.mount('#app')
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import { createRouter, createWebHistory } from 'vue-router'
|
||||||
|
import HomeView from '../views/HomeView.vue'
|
||||||
|
import DocViewer from '../components/DocViewer.vue'
|
||||||
|
|
||||||
|
const router = createRouter({
|
||||||
|
history: createWebHistory(),
|
||||||
|
|
||||||
|
scrollBehavior(to, from, savedPosition) {
|
||||||
|
// Если в ссылке есть хэш (например, #pricing)
|
||||||
|
if (to.hash) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
// Даем 200 миллисекунд компонентам (Three.js/Swiper) на полную отрисовку
|
||||||
|
setTimeout(() => {
|
||||||
|
resolve({
|
||||||
|
el: to.hash,
|
||||||
|
behavior: 'smooth',
|
||||||
|
// Корректировка отступа сверху, чтобы липкая (sticky) шапка не перекрывала заголовок блока
|
||||||
|
offset: { x: 0, y: 80 }
|
||||||
|
})
|
||||||
|
}, 200)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Если хэша нет, при переходе на новую страницу просто скроллим в самый верх
|
||||||
|
return { top: 0 }
|
||||||
|
},
|
||||||
|
|
||||||
|
routes: [
|
||||||
|
{ path: '/', name: 'home', component: HomeView },
|
||||||
|
{ path: '/docs/:docName', name: 'documents', component: DocViewer, props: true }
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
export default router
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<HeroSection />
|
||||||
|
<FeaturesSection />
|
||||||
|
<CompatibilitySection />
|
||||||
|
<PricingSection />
|
||||||
|
<StepsSection />
|
||||||
|
<TestimonialsSection />
|
||||||
|
<FaqSection />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import HeroSection from '../components/HeroSection.vue'
|
||||||
|
import FeaturesSection from '../components/FeaturesSection.vue'
|
||||||
|
import CompatibilitySection from '../components/CompatibilitySection.vue'
|
||||||
|
import PricingSection from '../components/PricingSection.vue'
|
||||||
|
import StepsSection from '../components/StepsSection.vue'
|
||||||
|
import TestimonialsSection from '../components/TestimonialsSection.vue'
|
||||||
|
import FaqSection from '../components/FaqSection.vue'
|
||||||
|
</script>
|
||||||
@@ -2,7 +2,6 @@ import { fileURLToPath, URL } from 'node:url'
|
|||||||
|
|
||||||
import { defineConfig } from 'vite'
|
import { defineConfig } from 'vite'
|
||||||
import vue from '@vitejs/plugin-vue'
|
import vue from '@vitejs/plugin-vue'
|
||||||
import vueDevTools from 'vite-plugin-vue-devtools'
|
|
||||||
import tailwindcss from '@tailwindcss/vite'
|
import tailwindcss from '@tailwindcss/vite'
|
||||||
|
|
||||||
|
|
||||||
@@ -10,7 +9,6 @@ import tailwindcss from '@tailwindcss/vite'
|
|||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [
|
plugins: [
|
||||||
vue(),
|
vue(),
|
||||||
vueDevTools(),
|
|
||||||
tailwindcss(),
|
tailwindcss(),
|
||||||
],
|
],
|
||||||
resolve: {
|
resolve: {
|
||||||
|
|||||||
Reference in New Issue
Block a user