Содержание
Seafile
Seafile - это некое подобие облачного диска, который умеет синхронизировать папку между несколькими устройствами и делать это не так что сетевая папка, а именно синхронизация локальной, т.е. в момент когда интернета нет ваши файлики у вас под рукой и на всех устройствах должны быть актуальными.
В отличие от Nextcloud/OwnCloud этот сервис работает не на PHP, его ядро написано на Си, само хранилище в специфичном формате в виде блоков, изменения в файлах отправляются только теми кусками, которые изменились (как в rsync), в общем, это одно из самых производительных открытых решений.
Для всего хранилища наверно нет, но вот то что должно быть оперативно под рукой самое то…
Данная статья как будто не про мануал, а про понимание как это работает
Я настраиваю сервис, поэтапно его усложняя и модифицируя…
В конце опишу различные тонкости, с которыми столкнусь в ходе изучения этого продукта.
Официальная документация
Раскатка из compose
В официальной документации есть достаточно избыточная инструкция
https://manual.seafile.com/13.0/setup/setup_ce_by_docker/
В ней говорится следующее:
# создаем папку и переходим в нее mkdir /opt/seafile cd /opt/seafile
# скачаем шаблон с env файлом, который нужно будет подредактировать (пароли, домены, логины...) wget -O .env https://manual.seafile.com/13.0/repo/docker/ce/env
#скачаем три шаблона docker-compose wget https://manual.seafile.com/13.0/repo/docker/ce/seafile-server.yml wget https://manual.seafile.com/13.0/repo/docker/seadoc.yml wget https://manual.seafile.com/13.0/repo/docker/caddy.yml
#отредактируем .env nano .env
#стартуем... docker compose up -d
Еще в инструкции подробно расписаны все переменные, которые нужно отредактировать в .env файле
Использование существующего сервера mysql
И есть еще одна страничка, которая описывает использование внешнего mysql сервера
https://manual.seafile.com/13.0/setup/setup_with_an_existing_mysql_server/
Зачем такое нужно?
А представьте что у нас несколько десятков подобных сервисов - разные блоги, форумы, планировщики, хранилища, и у каждого свой контейнер с базой…
Что лучше, один сервер mysql или несколько десятков серверов с точки зрения экономии ресурсов на скромном железе? Я считаю что лучше один, но это лично мое.
И второй момент, который пока не подтверждаю на 100%, вероятно я не умею готовить докер,
у меня были такие приложения типа импорт каталога, обработка сотен тысяч зависимых объектов…
Мое приложение выполняло ну очень много операций за секунду по нажатию одной кнопочки и вот когда mysql положил внутрь докера, то вместо 3 секунд эта обработка длилась 5-6 секунд,
но тут опять же, скорее всего я просто не умею готовить докер и тюннить высокоскоростные файловые операции.
Отдельный реверс-прокси
Опять кидаю ссылку на орфициальную документацию, используйте ее как первоисточник, т.к. в данной статье будет лишь выжимка конкретно под мою ситуацию
https://manual.seafile.com/13.0/setup/use_other_reverse_proxy/#add-reverse-proxy-for-related-services
Опять отвечаю на вопрос зачем? У меня стоит один единственный nginx, и только он слушает 80 и 443 порты и он проксирует трафик на несколько десятков контейнеров, а контейнеры в свою очередь шарят порты которые доступны только на локальном хосте.
Второй момент - на этом nginx стоит certbot, который автоматически беспокоится о продлении всех сертификатов lets-encrypt…
Итак, приступим, будем делать по официальной документации, но всё равно по-своему.
А вы должны делать по-своему, т.к. каждая ситуация уникальна, вероятно что мой опыт вам пригодится.
Подготовка
У меня есть папка /storage, куда примонтирован RAID массив, т.е. все данные зеркалированы дважды…
Далее я создал папку /storage/DockerApps чтобы локализовать конфиги и данные всех моих сервисов,
если потребуется мигрировать на новый сервер, то я просто подключу внешний диск на 6тб и перенесу на него эту папку.
Аналогично, я локализовал место хранения различных докер-томов и mysql-data чтобы они хранились на рейд-томе.
# 1. Создаем директорию сервиса mkdir -p /storage/DockerApps/seafile/
Если что-то пошло не по плану
docker compose down
и
чистим папку и по новой,
иначе, инициализация дважды не пройдет
Дальше качнем конфиги, но не все, из этого набора нам не нужен caddy
#переходим в папку cd /storage/DockerApps/seafile/ #это делаем, только если не правили и не готовили свой env или он вам не нужен wget -O .env https://manual.seafile.com/13.0/repo/docker/ce/env
wget https://manual.seafile.com/13.0/repo/docker/ce/seafile-server.yml wget https://manual.seafile.com/13.0/repo/docker/seadoc.yml
Поправим env
Ознакомимся… если хотим, то руками поправим, если не хотим - идем дальше, будут команды
nano .env
Удалим caddy
sed -i "s/,caddy\.yml\|caddy\.yml,//g" .env grep COMPOSE_FILE .env
Должно получиться так
COMPOSE_FILE='seafile-server.yml,seadoc.yml'
Основная папка BASIC_STORAGE_PATH
В этой папке будут подпапки для остальных контейнеров и их данных
sed -i 's|^BASIC_STORAGE_PATH=/opt|BASIC_STORAGE_PATH=/storage/DockerApps/seafile|g' .env grep BASIC_STORAGE_PATH= .env
Внутри как видим остальные пути строятся относительно этой папки
BASIC_STORAGE_PATH=/storage/DockerApps/seafile SEAFILE_VOLUME=$BASIC_STORAGE_PATH/seafile-data SEAFILE_MYSQL_VOLUME=$BASIC_STORAGE_PATH/seafile-mysql/db SEAFILE_CADDY_VOLUME=$BASIC_STORAGE_PATH/seafile-caddy SEADOC_VOLUME=$BASIC_STORAGE_PATH/seadoc-data
Заполним секреты (пароли)
JWT_PRIVATE_KEY
sed -i "s|^JWT_PRIVATE_KEY=.*|JWT_PRIVATE_KEY=$(pwgen 42)|g" .env sed -i "s|^SEAFILE_MYSQL_DB_PASSWORD=.*|SEAFILE_MYSQL_DB_PASSWORD=$(pwgen 32)|g" .env sed -i "s|^INIT_SEAFILE_MYSQL_ROOT_PASSWORD=.*|INIT_SEAFILE_MYSQL_ROOT_PASSWORD=$(pwgen 42)|g" .env sed -i "s|^INIT_SEAFILE_ADMIN_PASSWORD=.*|INIT_SEAFILE_ADMIN_PASSWORD=$(pwgen 42)|g" .env sed -i "s|^REDIS_PASSWORD=.*|REDIS_PASSWORD=$(pwgen 42)|g" .env
Проверим
grep -E "^JWT_PRIVATE_KEY=|^SEAFILE_MYSQL_DB_PASSWORD=|^INIT_SEAFILE_MYSQL_ROOT_PASSWORD=|^INIT_SEAFILE_ADMIN_PASSWORD=|^REDIS_PASSWORD=" .env
Должно быть так
JWT_PRIVATE_KEY=ooJ2igier2iekeag1aepaiye6YaibohyooD1ieshuu SEAFILE_MYSQL_DB_PASSWORD=Daeth1aem1aedoh2Reixu6thieree6ba REDIS_PASSWORD=ohm5Aith4mahdaa5ay4ooPooyeijee2eevie2OwaeR INIT_SEAFILE_MYSQL_ROOT_PASSWORD=aGh9wafe3yaej2yei4ohg1vair6ucuRaeDa1adoopa INIT_SEAFILE_ADMIN_PASSWORD=ou1eiwukooTee0ang3feel3EeHa6voozipeeShie8R
Только не спешите меня взламывать, я еще раз эту команду выполню и все пароли поменяются))
Поправим compose.yml
Прокинем локальный порт в seafile
На данном этапе мы просто получим результат, который будем наращивать, поэтому мы даже домен не задали, только учтите, тут мы поиграемся и вероятно всё удалим и заново создадим…
nano seafile-server.yml
найдем и раскоментируем, отредактируем строчки в сервисе seafile
seafile: image: ${SEAFILE_IMAGE:-seafileltd/seafile-mc:13.0-latest} container_name: seafile restart: unless-stopped ports: - "8123:80"
таким образом, мы прокинули порт 8123 внутрь контейнера и можем подключаться к нему или с локального пк или в локальной сети (ведь мы за роутером) балуемся,
иначе не оставляйте на долгое время это…
Стартуем!!!
docker compose up -d
ждем пока все контейнеры запустятся…
На этом этапе посмотрим логи
docker logs -f seafile
Если видим фразу
---------------------------------------- Successfully created seafile admin ---------------------------------------- Seahub is started Done.
То открываем в браузере наш адрес по ip и порту 8123 (или какой вы задали)
Берем логин и пароль из файла .env и заходим
INIT_SEAFILE_ADMIN_EMAIL - логин
INIT_SEAFILE_ADMIN_PASSWORD - пароль
Ошибок нет, файлы загружаются, кто молодец?
Мы молодцы, статья закончилась, остальное сами)))
Что бы сделать еще?
Ну это решение хоть и рабочее, но не продакшн…
- Нужно перенести базу данных из контейнера на основной сервак (который централизованно бекапится).
- Нужно настроить nginx проксирование и сертификат, чтобы всё было доступно из инета и шифровалось.
- Нужно прикрутить onlyoffice или поднастроить seadoc, чтобы он работал (но это потом)
Миграция БД на хост
Определимся с подсетью
В mysql при создании пользователей принято после собаки указывать подсеть из которой ему доступно подключиться
Например так root@127.0.0.1 или root@192.168.1.%
Наш compose создает что-то случайное и мы заранее не сможем создать пользователя и прописать шлюз через который будет подключаться,
а если создали контейнер и сеть, то уже поздно создавать пользователя, у нас что-то обязательно рухнет при инициализации.
Вот такая команда покажет все действующие подсети докера
docker network ls --format "{{.Name}}" | \ xargs -I {} sh -c 'echo "Network: {}"; docker network inspect {} --format '\''{{range .IPAM.Config}}{{.Subnet}} - {{.Gateway}}{{println}}{{end}}'\'' \ 2>/dev/null; echo "---"'
Вот пример ее вывода (у меня как оказалось еще много забытых сетей)
Network: bridge 172.17.0.0/16 - 172.17.0.1 --- Network: host --- Network: nextcloud_net 172.22.0.0/16 - 172.22.0.1 ---
Ну и как мы видим, например 172.23.0.0 не занята, давайте этим воспользуемся и создадим…
docker network create \ --driver bridge \ --subnet=172.23.0.0/16 \ --gateway=172.23.0.1 \ seafile-net
Теперь подсеть создана вме docker-compose и не управляется им и вообще прибита гвоздями…
Если вы раскатаете другой композ а потом этот вернете на место ничего не уедет.
Но есть один момент, открываем наш seadoc и seafile-server.yml
И меняем кусочек
networks: seafile-net: name: seafile-net
на
networks: seafile-net: name: seafile-net external: true
Это знак для compose, что сеть создавать не нужно, она уже кем то была создана.
На этом этапе docker-compose.yml пока рабочий, можно даже снова запустить и проверить, всё работает, мы только прибили гвоздями сеть.
У нас есть два пути - когда сервер чистый и можно всё с нуля на новой БД инициализировать
и когда сервер какое то время поработал и нужно переехать из докера на полноценную базу, вероятно даже на отдельном сервере…
Предлагаю для начала мигрировать
Переносим бд из докера
Этот вариант мне чем-то нравится, что не потребуется создавать пользователя root,
можно инициализироваться в докере, а потом перенести на другой сервер имея только ограниченную учетную запись.
Резервирование дампа
сперва подгрузим .env чтобы задействовать его переменные
cd /storage/DockerApps/seafile source .env
создадим на хосте папку с дампами (мы внутри /storage/DockerApps/seafile)
mkdir dumps
Резервируем три базы, используя переменные
docker exec seafile-mysql \ mysqldump -u ${SEAFILE_MYSQL_DB_USER} \ -p${SEAFILE_MYSQL_DB_PASSWORD} \ ${SEAFILE_MYSQL_DB_CCNET_DB_NAME} > ./dumps/ccnet_dump.sql docker exec seafile-mysql \ mysqldump -u ${SEAFILE_MYSQL_DB_USER} \ -p${SEAFILE_MYSQL_DB_PASSWORD} \ ${SEAFILE_MYSQL_DB_SEAFILE_DB_NAME} > ./dumps/seafile_dump.sql docker exec seafile-mysql \ mysqldump -u ${SEAFILE_MYSQL_DB_USER} \ -p${SEAFILE_MYSQL_DB_PASSWORD} \ ${SEAFILE_MYSQL_DB_SEAHUB_DB_NAME} > ./dumps/seahub_dump.sql
Подключение mysql к хосту
Как помним, мы создали подсеть с шлюзом (первый адрес) 172.23.0.1
Берем и перевинчиваем в файле .env адрес БД
## Database SEAFILE_MYSQL_DB_HOST=172.23.0.1 #SEAFILE_MYSQL_DB_HOST=db
Создаем бд, разворачиваем из дампа
Почистим если существуют (вы должны понимать что делаете)
source .env mariadb -u root << EOF DROP DATABASE IF EXISTS ${SEAFILE_MYSQL_DB_CCNET_DB_NAME}; DROP DATABASE IF EXISTS ${SEAFILE_MYSQL_DB_SEAFILE_DB_NAME}; DROP DATABASE IF EXISTS ${SEAFILE_MYSQL_DB_SEAHUB_DB_NAME}; EOF
создадим базы, юзеров и дадим права
mariadb -u root << EOF CREATE DATABASE ${SEAFILE_MYSQL_DB_CCNET_DB_NAME} CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE DATABASE ${SEAFILE_MYSQL_DB_SEAFILE_DB_NAME} CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE DATABASE ${SEAFILE_MYSQL_DB_SEAHUB_DB_NAME} CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER '${SEAFILE_MYSQL_DB_USER}'@'172.23.%' IDENTIFIED BY '${SEAFILE_MYSQL_DB_PASSWORD}'; GRANT ALL PRIVILEGES ON ${SEAFILE_MYSQL_DB_CCNET_DB_NAME}.* TO '${SEAFILE_MYSQL_DB_USER}'@'172.23.%'; GRANT ALL PRIVILEGES ON ${SEAFILE_MYSQL_DB_SEAFILE_DB_NAME}.* TO '${SEAFILE_MYSQL_DB_USER}'@'172.23.%'; GRANT ALL PRIVILEGES ON ${SEAFILE_MYSQL_DB_SEAHUB_DB_NAME}.* TO '${SEAFILE_MYSQL_DB_USER}'@'172.23.%'; -- Добавляем GRANT OPTION, так как это часто требуется для инициализации Seafile GRANT GRANT OPTION ON ${SEAFILE_MYSQL_DB_CCNET_DB_NAME}.* TO '${SEAFILE_MYSQL_DB_USER}'@'172.23.%'; GRANT GRANT OPTION ON ${SEAFILE_MYSQL_DB_SEAFILE_DB_NAME}.* TO '${SEAFILE_MYSQL_DB_USER}'@'172.23.%'; GRANT GRANT OPTION ON ${SEAFILE_MYSQL_DB_SEAHUB_DB_NAME}.* TO '${SEAFILE_MYSQL_DB_USER}'@'172.23.%'; FLUSH PRIVILEGES; EOF
Ну и грузим дампы, которые создавали
mariadb -u root "${SEAFILE_MYSQL_DB_CCNET_DB_NAME}" < ./dumps/ccnet_dump.sql mariadb -u root "${SEAFILE_MYSQL_DB_SEAFILE_DB_NAME}" < ./dumps/seafile_dump.sql mariadb -u root "${SEAFILE_MYSQL_DB_SEAHUB_DB_NAME}" < ./dumps/seahub_dump.sql
На данном этапе пока не трогаем композ, мы только поменяли хост в .env, если что, будет шаг назад
docker compose down docker compose up -d
На данном этапе всё получилось, убираем из compose файла контейнер с базой данных, комментируем этот участок в файле seafile-server.yml
# db: # image: ${SEAFILE_DB_IMAGE:-mariadb:10.11} # container_name: seafile-mysql # restart: unless-stopped # environment: # - MYSQL_ROOT_PASSWORD=${INIT_SEAFILE_MYSQL_ROOT_PASSWORD:-} # - MYSQL_LOG_CONSOLE=true # - MARIADB_AUTO_UPGRADE=1 # volumes: # - "${SEAFILE_MYSQL_VOLUME:-/opt/seafile-mysql/db}:/var/lib/mysql" # networks: # - seafile-net # healthcheck: # test: # [ # "CMD", # "/usr/local/bin/healthcheck.sh", # "--connect", # "--mariadbupgrade", # "--innodb_initialized", # ] # interval: 20s # start_period: 30s # timeout: 5s # retries: 10
остановим бд
docker stop seafile-mysql docker rm seafile-mysql
есть зависимость у seadoc
#depends_on: # db: # condition: service_healthy
А еще в seafile-server
depends_on: # db: # condition: service_healthy redis: condition: service_started
Вариант 2 - чистая база
Если у вас новая установка, база была чистой или вы изначально создаете на чистой базе
- точно также комментируем контейнер БД и меняем в файле .env адрес подключения на шлюз нашей подсети
- убираем зависимости от db в compose
docker compose down
Очистка
это опционалдьно, если уже инициализировали
сперва почистим если что то есть на бд хоста
mariadb -u root << EOF DROP USER IF EXISTS '${SEAFILE_MYSQL_DB_USER}'@'172.23.%'; DROP DATABASE IF EXISTS ${SEAFILE_MYSQL_DB_CCNET_DB_NAME}; DROP DATABASE IF EXISTS ${SEAFILE_MYSQL_DB_SEAFILE_DB_NAME}; DROP DATABASE IF EXISTS ${SEAFILE_MYSQL_DB_SEAHUB_DB_NAME}; FLUSH PRIVILEGES; EOF
потом почистим папки, если они есть, то инит не произойдет
rm -r ./seafile-mysql/ ./seafile-data/ ./seadoc-data/
Создание пользователя
mariadb -u root << EOF CREATE USER IF NOT EXISTS 'root'@'172.23.%' IDENTIFIED BY '${INIT_SEAFILE_MYSQL_ROOT_PASSWORD}'; GRANT ALL PRIVILEGES ON *.* TO 'root'@'172.23.%' WITH GRANT OPTION; FLUSH PRIVILEGES; EOF
Инициализация
docker up -d
ну и ждем логи
docker logs -r seafile
успех это
---------------------------------------- Successfully created seafile admin ---------------------------------------- Seahub is started Done.
Дальше нужно удалить этого временого рута из бд
mariadb -u root << EOF DROP USER IF EXISTS 'root'@'172.23.%'; FLUSH PRIVILEGES; EOF
Дальше и пользователь оказывается был создан seafile@% т.е. с любых хостов, а нам надо ограничить по сети
Настройка nginx
Не забываем определиться с доменом, например, super.puper.disk.ru
Этот домен нужно вписать в конфиг .env
SEAFILE_SERVER_HOSTNAME=...
Создаем файлик /etc/nginx/sites-enabled/seadoc
server { listen 80; # Или 443 ssl, если будете настраивать SSL позже server_name super.puper.disk.ru; # Основной прокси для Seafile location / { proxy_pass http://127.0.0.1:8123; # <-- Проксируем на порт хоста, куда проброшен порт 80 контейнера seafile proxy_read_timeout 310s; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # Важно для правильного определения HTTPS внутри контейнера proxy_set_header Connection ""; proxy_http_version 1.1; client_max_body_size 0; } # Прокси для Seadoc API location /sdoc-server/ { proxy_pass http://127.0.0.1:8124/; # <-- Проксируем на порт хоста, куда проброшен порт 80 контейнера seadoc proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Host $server_name; proxy_set_header X-Forwarded-Proto $scheme; # Важно client_max_body_size 100m; } # Прокси для Seadoc WebSocket location /socket.io { proxy_pass http://127.0.0.1:8124; # <-- Проксируем на порт хостат 80 контейнера seadoc proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_redirect off; proxy_buffers 8 32k; proxy_buffer_size 64k; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # Важно proxy_set_header Host $http_host; proxy_set_header X-NginX-Proxy true; } # Добавьте логи (по желанию) #access_log /var/log/nginx/suped.disk.h.dev66.ru.access.log; #error_log /var/log/nginx/suped.disk.h.dev66.ru.error.log; }
Проверим, перезагрузим…
nginx -t service nginx restart
Настроим ssl
В файле env
SEAFILE_SERVER_PROTOCOL=https
Запустим certbot, он сам всё поправит…
Теперь открываем по ссылке https://...
OnlyOffice
https://manual.seafile.com/13.0/extension/only_office/
Я тут не буду рассматривать прикрутку того офиса, который поднял для nextcloud, его у меня скорее всего не будет
Сперва качаем composer…
wget https://manual.seafile.com/13.0/repo/docker/onlyoffice.yml
Допишем в .env еще строчки
# OnlyOffice ONLYOFFICE_IMAGE=onlyoffice/documentserver:8.1.0.1 ONLYOFFICE_VOLUME=/storage/DockerApps/seafile/onlyoffice-data ONLYOFFICE_PORT=6233 ONLYOFFICE_JWT_SECRET=сгенерим его через pwgen
Далее в файле onlyoffice.yml
пропишем опять что у нас сеть внешняя
networks: seafile-net: name: seafile-net external: true
Уберем лейблы caddy
labels: caddy: ${SEAFILE_SERVER_PROTOCOL:-http}://${SEAFILE_SERVER_HOSTNAME:?Variable is not set or empty}:${ONLYOFFICE_PORT:-6233} caddy.reverse_proxy: "{{upstreams}}"
и сервис тоже caddy уберем
Получим что то такое
services: onlyoffice: image: ${ONLYOFFICE_IMAGE:-onlyoffice/documentserver:8.1.0.1} restart: unless-stopped container_name: seafile-onlyoffice environment: #- DB_TYPE=${DB_TYPE:-mariadb} #- DB_HOST=${SEAFILE_MYSQL_DB_HOST:-db} #- DB_USER=${SEAFILE_MYSQL_DB_USER:-seafile} #- DB_PWD=${SEAFILE_MYSQL_DB_PASSWORD:?Variable is not set or empty} - JWT_ENABLED=true - JWT_SECRET=${ONLYOFFICE_JWT_SECRET:?Variable is not set or empty} volumes: - ${ONLYOFFICE_VOLUME:-/opt/onlyoffice}/logs:/var/log/onlyoffice - ${ONLYOFFICE_VOLUME:-/opt/onlyoffice}/data:/var/www/onlyoffice/Data - ${ONLYOFFICE_VOLUME:-/opt/onlyoffice}/lib:/var/lib/onlyoffice labels: caddy: ${SEAFILE_SERVER_PROTOCOL:-http}://${SEAFILE_SERVER_HOSTNAME:?Variable is not set or empty}:${ONLYOFFICE_PORT:-6233} caddy.reverse_proxy: "{{upstreams}}" networks: - seafile-net networks: seafile-net: name: seafile-net external: true
Подредактируем файлик .env
COMPOSE_FILE='seafile-server.yml,seadoc.yml,onlyoffice.yml'
Пересоздаем контейнеры
docker compose down docker compose up -d
Проверяем что сервис работает
curl -I http://127.0.0.1:6233/welcome/
Далее правим nginx
nginx -t && systemctl reload nginx
Onlyoffice на отдельном домене… seafile-data/seafile/conf/seahub_settings.py
ENABLE_ONLYOFFICE = True ONLYOFFICE_APIJS_URL = 'http://super.puper.disk.ru/onlyofficeds/web-apps/apps/api/documents/api.js' ONLYOFFICE_JWT_SECRET = 'ваш_секрет_из_этапа_1' # Опционально: OFFICE_PREVIEW_MAX_SIZE = 30 * 1024 * 1024 ONLYOFFICE_EDIT_FILE_EXTENSION = ('docx', 'pptx', 'xlsx', 'csv')



