Vesp 3.0

Совершенно неожиданно вышла новая версия Vesp - 3.0.
Как известно, Vesp состоит из Vue, Eloquent, Slim и Phinx. 3 части названия состовляет PHP бэкенд (пакет vesp/core), оставшаяся 1 часть - это фронтенд на Vue (пакет @vesp/frontend).
В новой версии обновлены они оба, но фронтенду досталось сильно больше из-за перехода на Vue 3.

Vesp/Core

Минимальная версия теперь PHP 8.1, прекрасно поддерживается и текущая стабильная 8.3.
Зависимости обновлены до последних версий: Eloquent 10, JWT 6, PhpUnit 10.
Во всех классах, где это возможно, добавлены типы переменным. Например, было так
abstract class ModelController extends Controller
{
    /** @var string $model */
    protected $model;
    protected $primaryKey = 'id';
    protected $maxLimit = 1000;
А стало вот так
abstract class ModelController extends Controller
{
    protected string $model;
    protected string|array $primaryKey = 'id';
    protected int $maxLimit = 1000;
Если вы будете обновлять на новую версию старый проект, то IDE вам обязательно должно подчеркнуть ошибки в старых контроллерах и моделях - просто укажите правильные типы, ничего сложного.
Для более удобного расширения контроллеров создан отдельный метод инициализации. Раньше для переопределения основного метода __invoke надо было прописывать кучу всего обязятельного, и только где-то в конце менять нужное, а теперь вот так:
class MyController extends Controller
{
    public function __invoke(RequestInterface $request, ResponseInterface $response): ResponseInterface
    {
        // Выставление $this->request, $this->response, $this->route, $this->user 
        // и свойств, доступных через $this->getProperties()
        $this->initController($request, $response);

        // Дальше ваша особенная логика
    }
}
Контроллеры теперь отдают не красиво распечатанный JSON, а обычный в одну строку - так расходуется меньше трафика на ответы от сервера. Флаг JSON_PRETTY_PRINT в ответах заменён на JSON_THROW_ON_ERROR.
В контроллере изображений добавлена поддержка композитного ключа. Везде эта поддержка была, а в картинках почему-то не было.
Добавил поддержку нового имени для куки авторизации auth:token, старая auth._token.local тоже осталась. Она была от фронтового пакет @nuxtjs/auth, в новой версии фронтенда его нет, так что имя куки стало проще.
Очень много изменений было в работе с файлами. Основное отличие - теперь файловые методы привязаны не к файловой системе, а к собственно модели, через новый trait FileModel.
Все методы, типа uploadFile, getFile, getSaveName теперь в нём, а значит, что вы можете создать сколько угодно моделей для разной логики работы с файлами. Файловая система может быть одна, а вот логика наименования файлов в ней зависеть от модели. Лично мне так кажется гораздо более удобно и логичнее.
Добавлена композитная модель UserFile, как пример использования нового трейта. Довольно часто в проектах нужно загружать какие-то файлы юзерам, так что лишним не будет.
Улучшена работа с большими файлами: и загрузка и отдача. Автоматическое получение размеров картинок теперь работает только для файлов <= 50 мегабайт.
composer.json и composer.lock переехали внутрь директории core. Раньше были снаружи, что как-то не согласуется с полным разделением бэкенда и фронтенда.
Тесты теперь можно быстренько прогнать через Docker.
git clone https://github.com/bezumkin/vesp-core.git
cd ./vesp-core
docker-compose up
Вам это не нужно, а мне приятно.

@Vesp/Frontend

Здесь было гораздо больше изменений, благодаря переходу на:
  • Vue 3
  • Nuxt 3
  • Typescript
  • Bootstrap 5 SCSS
  • Bootstrap Vue Next
  • Pinia вместо Vuex
  • Vue Toastification для всплывающих уведомлений
Благодаря новому композитному синтаксису пришлось переписать вообще всё. Плюс, для нового Nuxt нет многих старых привычных модулей, например авторизации и подключения Font Awesome - и мне пришлось написать их самостоятельно.
Как и раньше, @vesp/frontend является модулем для Nuxt, нужно просто подключить его и указать настройки. Работает поддержка типов, всё экспортируется, подсказки в IDE вам помогут:
Благодаря новым возможностям Nuxt больше нет разделения на 2 приложения: admin и site. Админка теперь это просто вложенные странички сайта, которые не рендерятся на сервере. А весь остальной сайт рендерится.
Соответственно, нет нужды и запускать 2 процесса Nuxt через concurrently - меньше тратится ресурсов.
Я крайне не советую обновлять старые проекты на новую версию, это равносильно переписыванию всего с нуля. Если, конечно, именно такой задачи у вас и не стоит.
Bootstrap Vue Next сейчас в очень активной разработке, и похоже будет там еще долго. Но я всё равно смог с ним подружиться и наладить нормальную работу. Поэтому версия пакета будет жёстко фиксироваться, чтобы он сам не обновлялся и не ломал уже работающее.
Я постарался, как мог, прописать поддержку собственных типов Vesp, вы увидете это в исходниках. Рекомендую пользоваться, чтобы делать меньше ошибок.
Typescript не является обязательным, он не участвует в компиляции и его ошибки ни на что не влияют, они должны просто помогать вам писать более правильный код.
SCSS файлы так же экспортируются модулем, вы можете их легко подключить в своих стилях:
@import 'bootstrap-scss/bootstrap';
@import '@vesp/frontend/assets/components';
@import '@vesp/frontend/assets/toast';
@import '@fortawesome/fontawesome-svg-core/styles.css';
Понятное дело, что там всё завязано на Bootstrap и вы можете переопределить переменные SCSS.
На данный момент модуль добавляет следующие компоненты:
  • vesp-table - компонент для вывода таблиц админки
  • vesp-modal - модалка с поддержкой отправки форм и обновления соответствующей таблицы
  • vesp-fa - иконки FontAwesome
  • vesp-change-locale - компонент для переключения языка
  • vesp-input-alias - компонент для ввода адресов страниц, использует slugify
  • vesp-input-combo-box - ввод с автодополнением через запросы к API
  • vesp-input-date-picker - ввод даты или диапазона дат, использует vue-datepicker-next
  • vesp-input-password - ввод пароля с кнопочкой "показать\скрыть"
  • vesp-input-remote-links - ввод JSON массива "название сервиса-адрес", например для ссылок на соцсети
  • vesp-input-text-mask - ввод телефонов или других данных по маске, использует text-mask-core
В основном эти компоненты расширяют что-то из BootstrapVue.
Также есть разные полезные функции:
  • useGet, usePost, usePut, usePatch, useDelete - простые запросы в API с поддержкой авторизации и безо всяких наворотов, вроде сохранения состояния и кэширования.
  • useApi - тоже самое, но с возможностью более тонкого указания параметров - функции выше используют эту функцию внутри себя
  • useCustomFetch - а это как раз запрос в API со всеми наворотами, плюс поддержка авторизации. Подробнее читать в документации Nuxt
try {
  const data = await useGet('user/profile')
  console.log(data)
} catch (e) {
  console.error(e)
}
Дальше работа с авторизацией через композит useAuth:
const {loggedIn, token, user, login, logout, loadUser, setToken} = useAuth()

console.log(loggedIn.value, user.value, token.value)
Экспортируются переменные:
  • loggedIn - computed переменная со статусом авторизации, внутри true или false.
  • token - ref на куку с токеном, может быть строкой или undefined.
  • user - ref на юзера, может быть объектом типа VespUser или undefined.
И полезные функции:
  • login(username, password) - авторизация
  • logout() - выход и удаление токена на сервере
  • loadUser() - загрузка профиля с сервера
  • setToken(string) - выставление нового токена в куку авторизации
Простейший пример авторизации:
<template>
  <div v-if="loggedIn">
    <pre>{{ user }}</pre>
    <b-button @click="() => logout()">Logout</b-button>
  </div>
  <div v-else>
    <b-form @submit.prevent="onSubmit">
      <b-form-group label="Username">
        <b-form-input v-model="form.username" required />
      </b-form-group>
      <b-form-group label="Password">
        <vesp-input-password v-model="form.password" required />
      </b-form-group>

      <b-button type="submit">Submit</b-button>
    </b-form>
  </div>
</template>

<script setup lang="ts">
const {loggedIn, user, login, logout} = useAuth()
const form = ref({username: '', password: ''})

async function onSubmit() {
  try {
    await login(form.value.username, form.value.password)
    form.value = {username: '', password: ''}
  } catch (e) {
    console.error(e)
  }
}
</script>
Дальше функции по выводу всплывающих сообщений:
  • useToastInfo(message, options)
  • useToastSuccess(message, options)
  • useToastError(message, options)
  • useToastsClear() - убирает выведенные сообщения
Всё понятно из названий. Первым параметром идёт сообщение, вторым можно указать дополнительные опции для вывода.
Если при запросе в API произошла ошибка, она будет выведена автоматически через useToastError().
Есть еще полезные функции:
  • getApiUrl() - определяет базовый адрес для запроса в API
  • getImageLink(VespFile, VespFileOptions) - формирует ссылку на вывод картинки из API, принимает переменные указанных типов
  • hasScope(permission) - проверяет, есть ли у юзера разрешение на что-то, типа users/get
Скорее всего, список всяких полезных функций еще будет расширяться.
Самое прикольное, что ничего импортировать не нужно: ни компоненты, ни функции, ни авторизацию. Оно всё просто доступно во всех vue файлах, за это отвечает Nuxt.

Docker

Новая версия рассчитана на работу в Docker. По умолчанию прописан production конфиг, который вы можете переопределить файлом docker-compose.override.yml.
Я приложил в комплект такой файлик с расширением dist, его можно переименовать и запускать контейнеры в режиме для разработки. Разница, в общем-то, только в открытии внешних портов, но вы можете переопределить и что-то еще.
Также упростил файл .env и прописал умолчания в docker-compose.yml, теперь из настроек для Docker только хост Nginx и возможность установить Xdebug в PHP.
В общем, никаких раздельных конфигов, всё оптимизировано и упрощено.

Заключение

Новая версия теперь по умолчанию в репозитории, запустить новый проект проще простого.
git clone https://github.com/bezumkin/vesp.git
cd vesp
cp .env.dist .env
cp docker-compose.override.yml.dist docker-compose.override.yml
docker-compose up --build
Если у вас уже есть проект с названием vesp в докере, нужно указать новое имя в .env перед запуском, например:
COMPOSE_PROJECT_NAME=vesp-new
Миграции и сиды теперь накатываются при первом же старте, никаких лишних телодвижений.
Дожидаемся в консоли надписи Vite client warmed up in, открываем 127.0.0.1:8080 и входим в админку, используя логин и пароль admin.
Все свои новые проекты я буду делать на этой версии, так что будут еще разные правки по мере нахождения проблем.

108 комментариев

Александр Наумов
Василий, добрый день!
Когда выполняю команды в такой последовательности:
cp .env.dist .env
cp docker-compose.override.yml.dist docker-compose.override.yml
docker-compose up --build
Получаю ошибку:
ERROR: The Compose file './../docker-compose.override.yml' is invalid because:
services.mariadb.restart contains an invalid type, it should be a string
services.nginx.restart contains an invalid type, it should be a string
services.node.restart contains an invalid type, it should be a string
services.php-fpm.restart contains an invalid type, it should be a string
Когда в такой:
cp .env.dist .env
docker-compose up --build
Все встает и работает.
Александр Наумов
Да, только вот строк Vite client warmed up in в консоли я не дождался, загрузка остановилось на:
Отсюда и в админку не попал.
Василий Наумкин
То, что этих строк нет - это логично, потому что нет файла docker-compose.override.yml и фронт запустился в production режиме, а не в режиме разработки.
Но сайт всё равно должен работать по адресу 127.0.0.1:8080:
Василий Наумкин
ERROR: The Compose file './../docker-compose.override.yml' is invalid because:
Возможно, у тебя другая версия Docker, но решается это просто. Нужно поменять все
restart: no
на
restart: "no"
То есть, сделать их строками. Внёс это исправление в репозиторий.
Если что, у меня такая версия
Василий Наумкин
Разобрался, почему у тебя была ругань не на строки в конфиге. Это потому, что используется старая версия docker-compose v1, когда он еще был отдельным приложением на Python.
С версии 2 он встроен в Docker и вызывается как docker compose - то есть через пробел, теперь это внутренняя команда самого докера.
У меня на MacOS работает и так и эдак - у обеих команд версии 2+, а вот на Ubuntu нет. Более того, официальная инструкция по установке докера предлагает отдельный docker-compose и вовсе удалить.
А еще есть возможность создать ссылку для совместимости:
Буду менять свои инструкции на новый формат команд.
Александр Наумов
Буду менять свои инструкции на новый формат команд.
Василий, и еще просьба, добавь в инструкцию, как открыть порт для БД в режиме разработки.
Но в принципе и не помешало бы подружить с phpMyAdmin даже на сервере, хотя бы иметь инструкцию, как это безопасно сделать, мало ли если пойдет что-то не так, когда добавляешь свои наработки в рабочий проект и приходится вмешиваться. Конечно VESP не так, как MODX завязан на БД, но привычка то осталась )).
Василий Наумкин
когда добавляешь свои наработки в рабочий проект и приходится вмешиваться
Для этого и придуманы миграции. Тестируешь всё локально и в продакшене они так же работают. Смысл переходить на Docker, если ты на живом сервере собираешься что-то делать?
Это же главная фишка, что у тебя локально и на сервере одна и та же система, которая ведёт себя одинаково.
PhpMyAdmin просто некуда подключаться, в рабочем режиме открыт только порт 8080, а БД изолирована. К ней можно подключиться только через консоль докера:
docker exec -ti имяконтейнера mariadb -uvesp -pvesp vesp
Александр Наумов
Василий, спасибо! А за это: docker exec -ti имяконтейнера mariadb -uvesp -pvesp vesp- отдельное спасибо!
Для этого и придуманы миграции. Тестируешь всё локально и в продакшене они так же работают. Смысл переходить на Docker, если ты на живом сервере собираешься что-то делать?
Я это все прекрасно понимаю и благодаря тебе сейчас пытаюсь всему этому научится )).
Александр Наумов
Василий, добрый день! Спасибо!
Решил данную проблему полной переустановкой Докера, видимо была сильно старая версия.
Сейчас у меня проблема с входом в админк. Дождался в консоли появления Vite server warmed up in 32919ms и пошел смотреть админку http://127.0.0.1:8080/admin, а там ошибка 401:
А в консоли ошибка 404 не может обнаружить файл Service Worker:
Александр Наумов
Василий, с этой проблемой разобрался.
Но сейчас не пускает в админку. Пароль принимает, toast показывает приветствие, а больше ничего не происходит.
Василий Наумкин
Попробуй из анонимного режима.
Есть подозрение, что у тебя браузер что-то закэшировал по этому адресу от старых проектов. Не зря же он ищет несуществующий sw.js.
Александр Наумов
Спасибо, попробовал разные варианты и браузеры менял но не вышло, буду переустанавливать.
Но вот Орбита встала, как надо и в админку впускает!
Василий Наумкин
Это странно, учитывая, что новый Vesp и сделан на базе Орбиты - я оттуда всё перетаскивал, включая конфиги.
Смотри тогда, что в консоли браузера и докера - должна быть какая-то ошибка. И еще, попробуй всё сделать заново - я вчера закомитил разные улучшения в Vesp, надо обновить исходники.
Если не разберёшься, напиши мне в телеграм, помогу.
Александр Наумов
Василий, добрый день!
Понял откуда ноги растут, почему не впускает в админку:
Если устанавливаю так, то проблемы нет:
git clone https://github.com/bezumkin/vesp.git vesp
cd vesp
cp .env.dist .env
docker-compose up --build
А если так, то в админку не впускает:
git clone https://github.com/bezumkin/vesp.git vesp
cd vesp
cp .env.dist .env
cp docker-compose.override.yml.dist docker-compose.override.yml
docker-compose up --build
Может у меня проблема в установленном Docker, устанавливал его по этой инструкции https://drive.google.com/file/d/1aZxgFjnIFDLTw57jMbIHq7oyK_2xi6Ro/view на Ubuntu 22.04, Docker 24.0.5?
Василий Наумкин
На будущее - Docker нужно ставить по инструкции с официального сайта, так надёжнее.
У тебя, получается, вся разница только в файле docker-compose.override.yml, но я не вижу там ничего такого, что бы могло сломать авторизацию.
Просто открытые порты и restart: no сервисам, чтобы они не стартовали автоматически при старте системы. Видимо, дело в том, что frontend запускается в development режиме и что-то не работает.
Сейчас попробую загрузиться с Ubuntu Live USB и там проверить.
Василий Наумкин
Поставил Ubuntu 22.04.3 LTS на внешний HDD (офигел от тормозов, конечно, после SSD) и всё у меня заработало.
Контейнер с node запустился только со второго раза, первый раз yarn выдавал ошибку ENOSYS Function not implemented. Второй раз всё ок. Ну и разрешения на upload и tmp указал 777.
Авторизация работает в режиме разработки, всё хорошо.
Александр Наумов
Спасибо!
Я еще сразу же после запуска получаю ошибку 500 и только после Ctrl + F5 я могу видеть кнопку входа.
Буду дальше пробовать, переустановлю Докер по оф. инструкции, может версия Docker Compose у меня не подходящая.
Александр Наумов
В принципе я понял в чем у меня проблема, все дело правах на папки которые создал движок по команде: docker-compose up --build.
Я не мог авторизоваться, потому что у папки docker/mariadb/ был какой-то загадочный владелец user #999.
Сейчас ставлю в такой последовательности:
git clone https://github.com/bezumkin/vesp.git vesp
cd vesp
cp .env.dist .env
docker-compose up --build
После, как все новые папки созданы, устанавливаю на все права 777, а потом:
cp docker-compose.override.yml.dist docker-compose.override.yml
docker-compose up --build
И вроде все работает как надо.
Василий Наумкин
У меня такой же владелец 999 с группой 999 и это никак не мешает.
Более того, у тебя вроде как на той же машине нет проблем с Орбитой, у которой наверняка те же права на директорию. В общем, мистика.
Александр Наумов
Спасибо, буду разбираться!
У Орбиты в инструкции не было команды: cp docker-compose.override.yml.dist docker-compose.override.yml, отсюда на начальном этапе и проблем не заметил.
Василий, а ты команду docker с sudo в Ubunte запускал?
Василий Наумкин
Василий, а ты команду docker с sudo в Ubunte запускал?
Вроде да, не помню уже.
Если получаешь access denied, значит нужно использовать sudo. Если нет - нет.
Александр Наумов
Спасибо!
Если речь идет о WSL - не лишне будет выполнить
sudo chmod -R 777 tmp
У меня тоже не пускало, теперь я команды установки последовательно выполняю. Сначала клонирую репозиторий, потом расшариваю папку tmp и далее запуск. Во всяком случае теперь админка работает.
Василий Наумкин
Только что проверял на Windows с Docker на Hyper-V - и там никаких проблем с разрешениями не было.
Судя по цвету терминала у Александа вообще Ubuntu =)
да, глупость сморозил))
Василий, есть пара вопросов по запуску в Докере. Буду благодарен, если прокомментируешь.
Сначала про production. Как понял, если я не переименовывать файл docker-compose.override.yml.dist, то проект запускается в режиме production. Об этом же выводится сообщение - vite v5.0.11 building SSR bundle for production... В этом случае при запуске не выводится сообщение - Vite client warmed up in ... Вместо этого - Listening on http://[::]:3000 . На локалке в этом режиме все запускается и админка работает. Правильно ли я понимаю, что в качестве инструкции по дальнейшей настройке рабочего сервера можно использовать твой пост про запуск Орбиты?
Василий Наумкин
Да, верно.
У тебя на сеервере образуется локальный порт 8080, на который нужно направить Nginx с настроенными сертификатами и правильным доменным именем. Если проектов несколько, то нужно поменять им порты, чтобы не пересекались.
У меня, например, все порты от 10000.
Понял, спасибо! А порты меняются в docker-compose.yml - NGINX_PORT=${NGINX_PORT:-8080} ? А "нодовский" порт 3000 - это чисто внутри Докера? Если у меня на сервере порт 3000 занят - они не пересекаются?
Василий Наумкин
docker-compose.yml менять не нужно, просто укажи новый порт в .env файле
NGINX_PORT=10002
Порты внутри докера не пересекаются. Если что-то пересечется - увидишь ошибку.
Ок, спасибо! А при запуска в "локальном режиме" (с активированным файлом docker-compose.override.yml) у меня почему-то контейнер php-fpm автоматом не запускается вместе с другими контейнерами. Выдает ошибку php-fpm-1 exited with code 255 . Правда лечится это элементароно - ручным запуском контейнера. После чего все прекрасно работает. Может особенность Докера в WSL.
Василий Наумкин
Посмотри предыдущие сообщения от этого php-from контейнера, может там разгадка.
Может быть, там упоминается об ошибках, но что с этим делать (кроме запуска контейнера вручную) я пока не знаю.
In Connector.php line 65:
SQLSTATE[HY000] [2002] Connection refused
migrate [-c|--configuration CONFIGURATION] [-p|--parser PARSER] [--no-info] [-e|--environment ENVIRONMENT] [-t|--target TARGET] [-d|--date DATE] [-x|--dry-run] [--fake]
Василий Наумкин
Connection refused
Это значит, что БД не принимает подключение и поэтому контейнер PHP не может выполнить команду и останавливается.
Если ты затем руками его запускаешь и всё хорошо, значит БД просто не успевает запуститься и принять первое подключение от PHP. Почему не успевает - непонятно.
Чтобы такого не было в конфиге докера для php-fpm прописана зависимость
    depends_on:
      - mariadb
Можно попробовать поменять команду на запуск в override файле таким образом:
    command: sh -c 'composer install && sleep 5 && composer db:migrate && php-fpm'
То есть, добавить таймаут 5 секунд перед попыткой выполнения миграций.
Ну да! Теперь все запускается. Спасибо!
В качестве дополнительной информации опишу "результаты дальнейших тестов".) VESP в нынешней редакции у меня запускается, только если я запускаю его с "дефолтными" значениями DB_DATABASE, DB_USERNAME и DB_PASSWORD - vesp. Звучит как бред, но это факт.)) Т.е. я запускаю единой командой:
git clone https://github.com/bezumkin/vesp.git
cd vesp
cp .env.dist .env
cp docker-compose.override.yml.dist docker-compose.override.yml
docker-compose up --build
Далее чтобы контейнер php-fpm-1 "не брыкался" я добавляю && sleep 5 && в docker-compose.override.yml как ты советовал. В админку пускает, в моем случае по-крайней мере, только после расшаривания прав на папку tmp, что проблемой, разумеется не является. И после этого я запускаю и все ОК.
Но далее, я решил изменить данные DB. Поскольку я не знаю как это сделать в Докере, и можно ли вообще это сделать - я переустановил VESP и изменил значения DB_DATABASE, DB_USERNAME и DB_PASSWORD в .env. Предварительно вычистил Докер. И вот в этом случае php-fpm-1 отказывается работать, даже если добавить && sleep 5 && или запустить его вручную. Причем в обоих режимах - на продакшене то же самое. Выдается ошибка:
SQLSTATE[HY000] [1045] Access denied for user 'vesp3'@'172.19.0.3' (using password: YES) - vesp3 я проставил значения DB.
Василий Наумкин
И вот в этом случае php-fpm-1 отказывается работать, даже если добавить && sleep 5 && или запустить его вручную.
Ну так а ты поменял настройки БД в файле .env? PHP же использует их для подключения, видимо они не совпадают.
VESP в нынешней редакции у меня запускается, только если я запускаю его с "дефолтными" значениями DB_DATABASE, DB_USERNAME и DB_PASSWORD - vesp. Звучит как бред, но это факт.))
А зачем тебе их вообще менять? Они не имеют никакого значения: БД внутри докера, закрыта по умолчанию для подключения снаружи. При разработке порт открывается, но это же на локальном компе.
БД изолирована внутри докера, поэтому логин и пароль вообще без разницы, нужны только для подключения соседних контейнеров, таких как PHP.
Ну так а ты поменял настройки БД в файле .env? PHP же использует их для подключения, видимо они не совпадают.
Конечно поменял. DB_DATABASE, DB_USERNAME и DB_PASSWORD вместо vesp поставил vesp3. А что, эта версия ставится с предустановленными настройками бд, юзера и пароля? Наверное поэтому она и устанавливается командой docker-compose up --build, а прошлая - просто docker-compose up? Насколько помню в прошлой редакции VESP я мог изменять данные DB.
А зачем тебе их вообще менять? Они не имеют никакого значения: БД внутри докера, закрыта по умолчанию для подключения снаружи. При разработке порт открывается, но это же на локальном компе.
Дело не в том, что мне нужно их менять. Просто я поменял и вижу проблему, а пока еще толком не знаю как правильно нужно работать с Докером. Если менять не нужно и это снимет вопросы - значит и не буду менять. Я просто подумал, а если на сервере у меня будет висеть несколько проектов на VESP - как к базам подключаться из PHPMyAdmin? Короче по-старинке еще мыслю - как будто "не в Докере".)
Василий Наумкин
А что, эта версия ставится с предустановленными настройками бд, юзера и пароля?
Открой файл docker-compose.yml - там прописаны все значения по умолчанию. Их можно переопределить, указав в .env:
# Это для инициализации БД в Docker
MARIADB_DATABASE=
MARIADB_USER=
MARIADB_PASSWORD=

# Это для подключения PHP
DB_DATABASE=
DB_USERNAME=
DB_PASSWORD=
Указанные значения в этих переменных должны совпадать.
а пока еще толком не знаю как правильно нужно работать с Докером
Ну так может уже пора книжку почитать какую, или видеокурсы посмотреть по Docker?
как к базам подключаться из PHPMyAdmin?
Никак, в рабочем режиме контейнеры закрыты от подключения снаружи. Можно зайти только через docker exec -ti имяконтейнера mariadb
Блин вот стыдоба то(( . Замылилась голова от переустановок и в двух конфигах запутался)
Ну так может уже пора книжку почитать какую, или видеокурсы посмотреть по Docker?
Конечно пора. Спасибо большое за помощь!
Александр Наумов
Василий, добрый день!
Столкнулся с проблемой, что бы изменения вступили в силу, приходится остановить контейнеры и заново их запустить.
Что сделать, что бы стало работать без перезагрузки?
Александр Наумов
Может быть проблема моя в том, что я работаю без файла docker-compose.override.yml?
Василий Наумкин
Естественно.
Когда у тебя frontend в production режиме - он собран в готовые файлы, которые не меняются от того, что ты поменял их исходники.
Именно для этого и нужен development режим, чтобы было отслеживание изменений и пересборка.
Там же команды разные в yml файлах: или yarn start или yarn dev - не обратил внимания?
Александр Наумов
Спасибо!!
Василий, возник вопрос, напрямую не связанный с VESP, но может у тебя будет возможность подсказать в каком направлении копать. Я добавил в админку страницу для постов - frontend\src\pages\admin\posts.vue . И далее страницу редактирования постов (frontend\src\pages\admin\posts\[id]\edit.vue) и форму создания - frontend\src\pages\admin\posts\create.vue .
Форма создания поста, по аналогии с user-roles\create.vue (содержит тег <forms-user-role v-model="record" />) и users\create.vue (<forms-user v-model="record" />) должна содержать тег <forms-post v-model="record" />. Но в таком виде модал не вызывается. Но стоит мне заменить код моей формы на код формы из Орбиты, или просто заменить тег <forms-post v-model="record" /> на <forms-page v-model="record" /> как модал начинает вызываться. Форма, при этом, конечно вызывается без полей, т.к. она никак не связана с моей моделью Post, но модал даже в таком виде работает. И я не могу понять, почему не вызывается модал с моим тегом <forms-post v-model="record" />? Ведь именно так должно быть правильно. P.S. Добавил картинку с анимацией, как это происходит.
Василий Наумкин
И я не могу понять, почему не вызывается модал с моим тегом ? Ведь именно так должно быть правильно.
Да, так и должно быть. Раз модалка не появляется, значит в твоей форме какая-то ошибка. Открывай консоль браузера и смотри, на что ругается. Без консоли при разработке фронтенда ловить вообще нечего.
Ты сам файл-то создал в components/forms/post.vue? С таким кодом компонента модалка работать должна:
<template>
    <div>Привет!</div>
</template>

<script setup lang="ts"></script>
Ну а дальше добавляй поля формы по образу и подобию. Если сломается - будет понятно, от чего.
Добавил картинку с анимацией, как это происходит.
На этот сайт можно грузить короткие MP4 видосики.
Файл-то components/forms/post.vue у меня был. Я взял за основу файл с Орбиты и переделал под свои поля. Но, очевидно там были ошибки. С твоим "минималистичным" кодом формы модал заработал! Спасибо за помощь! Буду разбираться дальше.
Александр Наумов
Василий, добрый день!
Если есть возможность, перепиши, пожалуйста, кусок кода на Vue 3 в Composition API. Читаю документацию, но пока во многом не разобрался, думаю на этом примере получится быстрее понять как оно устроено.
  mounted() {
    window.addEventListener("scroll", this.handleScroll);
  },
  methods: {
    // Скролинг Вверх - Вниз
    handleScroll() {
      if (process.client) {
        const currentScrollPosition = window.scrollY;

        if (currentScrollPosition < this.scrollPosition) {
          console.log("Scrolling up");
          this.showFullNav = true;
        } else {
          console.log("Scrolling down");
          this.showFullNav = false;
        }

        this.scrollPosition = window.scrollY;
      }
    },
}
Василий Наумкин
Примерно так:
const showFullNav = ref()
const scrollPosition = ref()

function handleScroll() {
    if (process.client) {
        const currentScrollPosition = window.scrollY;

        if (currentScrollPosition < scrollPosition.value) {
            console.log("Scrolling up");
           showFullNav.value = true;
        } else {
            console.log("Scrolling down");
            showFullNav.value = false;
        }

        scrollPosition.value = window.scrollY;
    }
}

onMounted(() => {
    window.addEventListener("scroll", handleScroll)
})
Александр Наумов
Василий, спасибо большое!
Еще пару вопросов:
  1. this в Composition API не используем?

  2. Плагины не нужно регистрировать в nuxt.config.ts?

Василий Наумкин
this в Composition API не используем?
Именно! Нет никакого this в новом синтаксисе, в этом весь смысл - использовать везде функции, не зависящие от контекста исполнения, то есть от this.
Плагины не нужно регистрировать в nuxt.config.ts?
Не нужно, да. Если они лежат в директории plugins, то новый Nuxt автоматически их регистрирует. То же самое и с components.
Александр Наумов
Спасибо, большое, сэкономил мне кучу времени!
Александр Наумов
Василий, добрый день!
Подключаю модуль в nuxt.config.ts
  modules: ['@vesp/frontend', '@nuxtjs/color-mode'],
В итоге ошибка:
ERROR  Cannot find module '/vesp/frontend/@nuxtjs/color-mode'
Почему-то модуль ищет в пакете Vesp.
Подскажи, пожалуйста, как регистрировать установленный модуль?
Устанавливаю так:
 cd /frontend 
yarn add --dev @nuxtjs/color-mode
Василий Наумкин
Почему-то модуль ищет в пакете Vesp.
Нет, это путь к зависимостям на диске внутри Docker - /vesp/frontend/node_modules. Что-то ты куда-то не туда установил, похоже сделал это не внутри контейнера Docker.
Меня настораживает, что ты делаешь cd /frontend. Во-первых, это путь от корня твоего диска, а во-вторых - при открытии консоли фронтенда, ты уже должен быть в нужной директории:
В любом случае, этот модуль тебе не нужен, потому что useColorMode уже есть в BootstrapVue.
Лучше использовать его, чтобы не было такого предупреждения:
Александр Наумов
похоже сделал это не внутри контейнера Docker.
Да, так точно. Устанавливаю не внутри контейнера vesp-node-1, спасибо, буду знать.
В любом случае, этот модуль тебе не нужен, потому что useColorMode уже есть в BootstrapVue.
О, за это, спасибо, большое! Так конечно правильнее и легче будет.
Александр Наумов
Василий, добрый день!
Подключаюсь в тестовом режиме к БД через MySQL Workbench - работает. Хочу просмотреть таблицы - Workbench схлопывается. В файл docker-compose.yml добавил phpMyAdmin
  phpmyadmin:
    image: phpmyadmin
    restart: always
    ports:
      - 8888:80
    depends_on:
      - mariadb
    environment:
      - PMA_ARBITRARY=1
Хочу подключится через него - тоже не выходит.
Подскажи, пожалуйста, в чем может быть проблема. Записал коротенькое видео:
Александр Наумов
С Workbench я вроде разобрался, хотелось бы еще, ради эксперимента, phpMyAdmin таким способом подключить.
Василий Наумкин
хотелось бы еще, ради эксперимента, phpMyAdmin таким способом подключить.
Зачем? Есть же нормальные десктопные приложения для работы с БД, в тот же PhpStorm встроен DataGrip.
Я никогда не ставил PhpMyAdmin в Docker, так что ничего подсказать не могу.
Александр Наумов
Зачем? Есть же нормальные десктопные приложения для работы с БД, в тот же PhpStorm встроен DataGrip.
Василий, спасибо, уже так делаю!
Александр Наумов
Василий, добрый день!
Тебе случайно не встречалась ошибка запрета нижнего подчеркивания в названии css селектора?
Василий Наумкин
Это Stylelint, такие правила прописаны в Vesp.
Можешь прописать свои - https://stylelint.io
Александр Наумов
Спасибо большое!
Александр Наумов
Василий, добрый день!
можешь прописать свои
Укажи, пожалуйста, где это сделать?
Я только один файл .stylelintcache в корне нашел, а в модуле @vesp ничего не нашел.
Василий Наумкин
Всё верно, этот файл тебе и нужно менять.
Александр Наумов
Василий, добрый день!
Может быть ты имел дело с PWA в Nuxt 3, не подскажешь какой модуль лучше выбрать:
Есть официальный модуль: vite-pwa-nuxt, есть как будто второй официальный модуль @nuxtjs/pwa?
Александр Наумов
Похоже с вопросом поспешил, судя по обновлениям на Гитхабе @nuxtjs/pwa для Nuxt 2.
Василий Наумкин
С PWA пока не разбирался, мне кажется это не особо популярная штука.
Если надо просто иконки добавить, то вот приятный сервис - https://realfavicongenerator.net
Александр Наумов
Если надо просто иконки добавить, то вот приятный сервис - https://realfavicongenerator.net
А сюда public/favicons с помощью этого сервиса иконки сгенерированы?
Василий Наумкин
Честно говоря, не помню как именно было на этом сайте, он уже давно работает.
А так этот сервис у меня в закладках и я его регулярно использую.
Александр Наумов
С PWA пока не разбирался, мне кажется это не особо популярная штука.
Я подписан на PWA — русскоязычное сообщество в Телеграмме, правда не сильно успеваю читать все что там пишут, но по ощущениям есть надежда, что PWA потеснит нативные приложения. Даже iphone 14 стал принимать push-уведомления и разрешил ставить на iphone приложения из других store, хотя Apple долго держался так как хорошо зарабатывал на размещении приложений в Apple Store и ему было не выгодно развитие PWA.
Василий, подскажи, пожалуйста:
Есть сборка Nuxt3 + @vite-pwa/nuxt https://github.com/jahidanowar/nuxt3-pwa, делаю все так же на VESP 3 но никак не могу добиться, что бы сгенерировался манифест, перепробовал кучу вариантов.
Вопрос: в nuxt.config.ts мы пишем:
 modules: [
    '@vite-pwa/nuxt'
  ],
  pwa: {
    /* PWA options */
  }
И этого достаточно, что бы в теории заработало?
Василий Наумкин
И этого достаточно, что бы в теории заработало?
По идее - да. Во всяком случае, именно так модули устанавливаются в Nuxt.
Александр Наумов
Ок, спасибо!
Александр Наумов
Нашел проблему, закомментировал в nuxt.config.ts строку:
{rel: 'manifest', href: '/favicons/site.webmanifest', color: '#fff'},
Теперь манифест создается.
Александр Наумов
Вот список крупных сайтов с PWA https://github.com/FluorescentHallucinogen/pwa-awesome-ru?tab=readme-ov-file#%D0%BF%D1%80%D0%B8%D0%BC%D0%B5%D1%80%D1%8B-pwa у Тинькова очень хорошо сделан, из-за реактивных технологий не поймешь, что не нативное приложение.
Правильно ли я понял, что это только для тех, у кого есть Apple Laptop и 10+ лет опыта бэкенд разработки?
запустить новый проект проще простого.
То, что я вижу тут комментариях, это как люди с виндой натурально "пляшут" с бубнами (у всех разные).
Василий, уточните пожалуйста, на modhost работать не будет, верно? Потому что:
-bash: docker: command not found
-bash: apt: command not found
-bash: sudo: command not found
-bash: apt-get: command not found
Ну и так далее. Т.е. никакие нужные допы я не смог поставить. Вероятно, я что то не так делаю. Ладно. Спасибо. Короче, зачем я вообще это тут пишу.
Василий Наумкин
на modhost работать не будет, верно?
Верно, там нет Docker.
Нужен свой сервер, но я бы советовал начать с разработки на локальной машине.
Александр Наумов
Василий, добрый день!
Не подскажешь, как подключить плагины bootstrap-vue-next?
В Vesp 2 мы подключали нужные плагины в nuxt.config.js, а в Vesp 3 не пойму где их подключать, например тот же navbar в Орбите не нашел как он подключен.
Василий Наумкин
А здесь ничего не надо вручную подключать, всё само автоматически импортируется.
Просто используй любые нужные компоненты BootstrapVue в коде и будет работать.
Александр Наумов
Спасибо, понял!
Александр Наумов
Василий добрый день!
Скажи, пожалуйста, как формируется ключ vueuse-color-scheme?
Пересмотрел все твои правки https://github.com/bezumkin/orbita/commit/1bdc25082af220669d469d0f2204dfe29f859c78, когда ты добавил Орбите темную тему, так и не понял, как VueUse генерирует этот ключ.
И еще скажи, в сохранении выбранной темы участвует только localStorage, в Pinia не участвует?
Помню, что в Vesp 2 при сохранении корзины данные писались в localStorage и Vuex.
Василий Наумкин
Орбита использует Bootstrap 5.3, а он поддерживает смену темы прямо из коробки.
useColorMode нужен только для переключаения атрибута data-bs-theme у тега body:
const {system, store} = useColorMode({attribute: 'data-bs-theme', selector: 'body'})
console.log(system) // это системное значение темы - тёмная или светлая
store.value = 'dark' // а это переключение темы в браузере
Дальше уже работает Bootstrap, никакие ключи я не формирую.
И еще скажи, в сохранении выбранной темы участвует только localStorage, в Pinia не участвует?
Нет, я не знаю зачем это писать в общее хранилище, если вся работа идёт в одном месте и другим компонентам эти данные не нужны.
Александр Наумов
Василий, спасибо!!!
Дело в том, что я решил добавить на главную страницу сайта анимированную презентацию, и сделать ее отключение для тех, кто ее уже видел и ему она надоела.
Я считал что Vuex, Pinia нужны для vue router для сохранения данных при переходе между страницами без перезагрузки, а если страница перезагружается то данные берутся с localStorage. Сейчас смотрю на компонент переключения темы с светлой на темную и понимаю, что он не использует Pinia а использует только localStorage.
Вот сейчас не могу понять, а как правильно, нужно ли в моей задачи использовать Pinia и localStorage или только один localStorage?
Нашел мануал, как совместно использовать Pinia и localStorage https://runthatline.com/how-to-use-local-storage-pinia/.
Василий Наумкин
Pinia, как и Vuex нужны для обмена данными между компонентами.
Хранилища - реактивные. Если вывод формы авторизации завязан на значение в хранилище, то он отреагирует сразу после изменения этого значения. Таким образом форму авторизации можно показывать при попытке оставить комментарий, или голосовании - то есть из любого места приложения.
А в случае с localStorage, никакой реакции не будет, потому что компонент должен сам как-то следить за обновлением значения. Поэтому localStorage годится только для хранения данных между обновлениями страницы.
Для VueRouter ни реактивные хранилища, ни localStorage не нужны. Для твой задачи достаточно одного localStorage, просто запоминать, что юзер скрыл анимацию.
Александр Наумов
Спасибо большое за такой развернутый ответ!!
У меня вопрос по оформлению страниц на фронте. Понятно, что это не вопрос VESP-а... На стороне бэкенда я создал некое подобие блоговой ленты, содержащей все посты и страницу отдельного поста. Т.е. API отдает страницы http://127.0.0.1:8080/api/admin/posts и http://127.0.0.1:8080/api/web/posts/post-1 . Как понимаю, это означает, что бэкенд работает и данные в JSON отдает. Вопрос только в том, как это вывести на фронте? Вообще область бэкенда стала более понятной. Не на уровне детального понимания, но свет в конце туннеля забрезжил.) Плюс php подучиваю. А вот с фронтом пока беда - в "Data fetching" на Nuxt 3 я пока не могу въехать. Смотрю код отдельных страниц на Орбите, но там более сложная модель данных.
Василий Наумкин
Как понимаю, это означает, что бэкенд работает и данные в JSON отдает.
Верно.
Вопрос только в том, как это вывести на фронте?
Если это Vesp 3, то примерно так:
<template>
    <div class="wrapper">
        <div v-for="item in items" :key="item.id">
            <pre>{{ item }}</pre>
            <b-link :to="{name: 'posts-alias', params: {alias: item.alias}}">{{ item.title }}</b-link>
        </div>
    </div>
</template>

<script setup>
const url = 'web/posts'
const items = ref([])

try {
    items.value = await useGet(url)
} catch (e) {}
</script>
Это для вывода списка заметок со ссылкой на страницу каждой заметки.
Страница отдельного поста заработала сразу в том виде, как ты написал.) А вот в ленте постов контент не выводился - выдавалось
"500 Missing required param "alias""
. Я пытался по-разному варьировать код и когда в ссылке вместо 'posts-alias' написал просто 'posts' ( т.е. -
<b-link :to="{name: 'posts', params: {alias: item.alias}}">{{ item.title }}</b-link>
) - получил на фронте контент, но в виде массива, как в бэкенде.)
Посты у меня состоят из title, content и уникального алиаса. На фронте я создал страницу frontend/src/pages/posts/[alias].vue для вывода отдельного поста. Может ты сможешь подсказать как самым простым методом вывести пусть хоть один title, чтобы дальше можно было это изучать и развивать?
Василий Наумкин
А страница с заметкой должна выглядеть как-то так:
<template>
    <div>
        <h1>{{ item.title }}</h1>
        <pre>{{ item }}</pre>
    </div>
</template>

<script setup>
const url = 'web/posts/' + useRoute().params.alias
const item = ref({})

try {
    item.value = await useGet(url)
} catch (e) {
    showError({statusCode: e.statusCode || 500, statusMessage: e.statusMessage || 'Server Error'})
}
</script>
Код пишу по памяти, не проверял - но примерно так всё и работает.
Огромное тебе спасибо!))
Александр Наумов
Василий, добрый день!
Не подскажешь, если установлено несколько копий VESP, то не будет ли проблем если они будут использовать одними и теме же контейнерами?
И еще, после обновления Докера возникла проблема, когда вторая копия VESP начала переименовывать контейнер добавляя в его имя ID:
А потом, при запуске, схлопывается после ошибки:
В итоге, сейчас могу пользоваться только первой копией VESPа, которая создала контейнеры, а все последующие установки VESPа, которые обращаются к этим контейнерам не запускаются из-за ошибки о которой написал выше.
Василий Наумкин
Обрати внимание на настройку COMPOSE_PROJECT_NAME в файле .env.
Она должна быть уникальна для каждого проекта.
Александр Наумов
Блин, спасибо!
Совсем забыл, что в файле .env настройки хранятся, я дальше nuxt.config.ts не смотрю. Сори.
Василий, ты пишешь про trait FileModel. В дефолтной установке VESP3, которую мы ставим
git clone https://github.com/bezumkin/vesp.git
его нет. Он есть только в репозитории vesp-core. Мне нужно как-то обновить ядро VESP, чтобы получить эти файлы? И еще вопрос - мне теперь нужно разобраться с добавлением изображений к постам. В предыдущем курсе (на Vesp/Nuxt2) загрузка изображений реализовывалась проще, как понимаю теперь для работы с новым Nuxt лучше освоить трейты?
Василий Наумкин
Дефолтная установка Vesp тащит с собой и vesp/core, без него она просто не работает.
Загрузка файлов реализуется так же, просто наследуешь свою модель от Vesp\Models\File, а она уже использует нужный трейт.
Трейты к Nuxt не имеют никакого отношения, они для PHP.
Я имел в виду, что в репозитории Vesp, который мы скачиваем, в отличии от репозитория vesp-core, в директории core/src/Models нет директории Traits с файлом FileModel.php. И дефолтный файл core/src/Models/File.php у нас выглядит совсем не так. Там нет ссылок на трейты.
Блин, туплю. Я смотрю на файлы своего проекта и не вижу там этих трейтов. Очевидно речь идет о файлах в докеровском контейнере php-fpm-1. Там есть файл /vesp/core/vendor/vesp/core/src/Models/Traits/FileModel.php . И значит мне в своем проекте директория src/Models/Traits с ее содержимым уже не нужна?
Василий Наумкин
И значит мне в своем проекте директория src/Models/Traits с ее содержимым уже не нужна?
Я ж не знаю, зачем ты её создавал. Если там нет ничего нужного - то, выходит, что не нужна.
Я просто немного запутался. Когда в абзаце "Vesp/Core" ты пишешь про "новый trait FileModel", я подумал, что это все должно быть в директории core моего проекта, также как, например, в Орбите есть директория core/src/Models/Traits. И когда ты посоветовал просто "наследовать модель от Vesp\Models\File" и она уже может использовать все методы твоего трейта - я просто не понял откуда все это возьмется, если в директории core у меня ничего такого нет. Я упустил из виду, что vesp-core под капотом Докера и можно так просто наследовать. Ну и Namespaces я еще не успел изучить, чтобы сразу понять твою мысль про Vesp\Models\File. Спасибо за разъяснение!
Александр Наумов
Василий, добрый день!
Не подскажешь алгоритм, экспорта, импорта базы данных?
Захожу в контейнер mariadb, пытаюсь выполнить команду mysqldump, а мне отвечает: command not found. Как работать с большими базами данных?
Василий Наумкин
А там же mariadb-dump с теми же параметрами, вот тебе мой скрипт для скачивания базы с продакшена на локалку для разработки:

SSH=user@host.ru
CONTAINER=project-mariadb-1

ssh ${SSH} "docker exec -t ${CONTAINER} mariadb-dump -uvesp -pvesp vesp \
  --skip-lock-tables --add-drop-table --skip-comments --force --single-transaction --quick --no-tablespaces \
  | bzip2 --fast" | bunzip2 | tail +2 | mysql -h127.0.0.1 -P3333 -uvesp -pvesp vesp
Авторизация у меня через SSH ключ.
Александр Наумов
Василий, спасибо большое!!
ой... по бесплатным урокам по vesp все ссылки битые. А так хотелось...
Василий Наумкин
Какие именно ссылки? Напишите - я поправлю.
А так, весь контент на месте, советую фильтровать по тегам в правой колонке. Например, вот все уроки по переезду с ms2 на Vesp - https://bezumkin.ru/?tags=13&reverse=1
да, теперь вижу, спасибо.
вот все эти ссылки битые -  https://bezumkin.ru/lessons по адресу https://bezumkin.ru/topics/0bbd26e7-abf4-4b01-bf50-b93c44594a8a,
там только  кнопка "Работа на Vesp" ведет куда надо
Василий Наумкин
Ага, вижу, спасибо. Текст подправил, а ссылки работают если их открывать в новом окне - тогда получается редирект.
А при клике в тексте проверки на редирект нет. Подумаю, как поправить.
Василий Наумкин
Поправил, теперь все эти ссылки должны нормально работать на клиенте.
Да. Все работает. Спасибо.
Да. Все в порядке. Спасибо.
bezumkin.ru
Личный сайт Василия Наумкина
Прямой эфир
inna
06.11.2024, 15:47:13
Да. Все работает. Спасибо.
Ivan CR
24.10.2024, 15:20:54
С днем рождения!!! Класс, что в твоей жизни есть такие интересные достижения.
Василий Наумкин
01.07.2024, 11:56:41
Да, верно, именно так. А в контроллере, скорее всего, ловить данные методом post.
Василий Наумкин
26.06.2024, 09:38:15
О, точно, вылезает если не залогинен. Спасибо, исправил!
Василий Наумкин
09.04.2024, 04:45:01
> Ошибка 500 Это не похоже на ошибку Nginx, это скорее всего ошибка PHP - надо смотреть его логи. ...
Василий Наумкин
20.03.2024, 21:21:52
Volledig!
Андрей
14.03.2024, 13:47:10
Василий! Как всегда очень круто! Моё почтение!
russel gal
09.03.2024, 20:17:18
> А этот стоило написать хотя бы затем, чтобы получить комментарий от юзера, который ничего не писал...
Александр Наумов
27.01.2024, 03:06:18
Василий, спасибо! Извини, тупанул.
Василий Наумкин
22.01.2024, 07:43:20
Давай-давай!