From 9ca1667a31d6d60062b7418da666a3ddb46d9147 Mon Sep 17 00:00:00 2001 From: StepanovPlaton Date: Wed, 14 Jan 2026 10:33:07 -0600 Subject: [PATCH] Add matrix, cloud, gitea --- .gitignore | 5 + cloud/.env.example | 5 + cloud/create-database.sh | 1 + cloud/docker-compose.yml | 32 +++++ gitea/docker-compose.yml | 22 ++- matrix/.env.example | 3 + matrix/create_config.sh | 5 + matrix/create_user.sh | 2 + matrix/docker-compose.yml | 38 ++++++ proxy/.env.example | 6 +- proxy/create-first-cert-example.sh | 2 +- proxy/ddns/Dockerfile | 10 ++ proxy/ddns/ddns_updater.py | 170 ++++++++++++++++++++++++ proxy/ddns/domains.txt | 7 + proxy/ddns/domains.txt.example | 10 ++ proxy/ddns/requirements.txt | 2 + proxy/docker-compose.yml | 47 +++++-- proxy/init-compose.yml | 23 ++-- proxy/nginx/conf.d/default.conf.example | 4 +- proxy/nginx/init/default.conf.example | 12 -- proxy/ssl/conf/.keep | 0 proxy/ssl/www/.keep | 0 proxy/update_dns.sh | 49 ------- transmission/docker-compose.yml | 3 - 24 files changed, 356 insertions(+), 102 deletions(-) create mode 100644 cloud/.env.example create mode 100644 cloud/create-database.sh create mode 100644 cloud/docker-compose.yml create mode 100644 matrix/.env.example create mode 100644 matrix/create_config.sh create mode 100644 matrix/create_user.sh create mode 100644 matrix/docker-compose.yml create mode 100644 proxy/ddns/Dockerfile create mode 100644 proxy/ddns/ddns_updater.py create mode 100644 proxy/ddns/domains.txt create mode 100644 proxy/ddns/domains.txt.example create mode 100644 proxy/ddns/requirements.txt delete mode 100644 proxy/nginx/init/default.conf.example delete mode 100644 proxy/ssl/conf/.keep delete mode 100644 proxy/ssl/www/.keep delete mode 100755 proxy/update_dns.sh diff --git a/.gitignore b/.gitignore index 92a91e9..4b73f9b 100644 --- a/.gitignore +++ b/.gitignore @@ -14,4 +14,9 @@ gitea/data/* gitea/db/* gitea/config/* +matrix/data/* +matrix/db + +cloud/filebrowser.db + !.keep diff --git a/cloud/.env.example b/cloud/.env.example new file mode 100644 index 0000000..983ef59 --- /dev/null +++ b/cloud/.env.example @@ -0,0 +1,5 @@ +SHARED_FOLDER=/path/to/folder +USER_ID=1000 +GROUP_ID=1000 +ADMIN_USER=admin +ADMIN_PASSWORD=password # Минимальная длинна 12 символов diff --git a/cloud/create-database.sh b/cloud/create-database.sh new file mode 100644 index 0000000..1785f5d --- /dev/null +++ b/cloud/create-database.sh @@ -0,0 +1 @@ +touch filebrowser.db diff --git a/cloud/docker-compose.yml b/cloud/docker-compose.yml new file mode 100644 index 0000000..d135906 --- /dev/null +++ b/cloud/docker-compose.yml @@ -0,0 +1,32 @@ +services: + filebrowser: + image: filebrowser/filebrowser:latest + container_name: filebrowser + restart: unless-stopped + volumes: + # Папка с вашими файлами (замените /path/to/your/files на реальный путь) + - ${SHARED_FOLDER}:/srv + # База данных настроек и пользователей + - ./filebrowser.db:/database/filebrowser.db + # (Опционально) Конфигурационный файл + # - ./settings.json:/config/settings.json + environment: + - PUID=${USER_ID:?} + - PGID=${GROUP_ID:?} + entrypoint: ["/bin/sh", "-c"] + command: + - | + if [ ! -s /database/filebrowser.db ]; then + filebrowser config init --database /database/filebrowser.db + filebrowser config set --auth.method=json --database /database/filebrowser.db + fi + filebrowser config set --root /srv --database /database/filebrowser.db + filebrowser users add ${ADMIN_USER:?} ${ADMIN_PASSWORD:?} --perm.admin --scope "." --database /database/filebrowser.db || \ + filebrowser users update ${ADMIN_USER:?} --password ${ADMIN_PASSWORD:?} --scope "." --database /database/filebrowser.db + filebrowser --database /database/filebrowser.db --address 0.0.0.0 --port 80 --root /srv + networks: + - cloud_network + +networks: + cloud_network: + driver: bridge diff --git a/gitea/docker-compose.yml b/gitea/docker-compose.yml index 356bfc5..8526865 100644 --- a/gitea/docker-compose.yml +++ b/gitea/docker-compose.yml @@ -1,9 +1,10 @@ services: gitea: image: docker.gitea.com/gitea:1.25.3-rootless + container_name: gitea environment: - GITEA__database__DB_TYPE=postgres - - GITEA__database__HOST=db:5432 + - GITEA__database__HOST=gitea-db:5432 - GITEA__database__NAME=${GITEA_DB:?} - GITEA__database__USER=${GITEA_DB_USER:?} - GITEA__database__PASSWD=${GITEA_DB_PASSWORD:?} @@ -13,14 +14,17 @@ services: - ./config:/etc/gitea - /etc/timezone:/etc/timezone:ro - /etc/localtime:/etc/localtime:ro - ports: - - ${GITEA_HTTP_PORT:?}:3000 - - ${GITEA_SSH_PORT:?}:2222 + #ports: + # - ${GITEA_HTTP_PORT:?}:3000 + # - ${GITEA_SSH_PORT:?}:2222 depends_on: - - db + - gitea-db + networks: + - gitea_network - db: + gitea-db: image: docker.io/library/postgres:14 + container_name: gitea-db restart: always environment: - POSTGRES_USER=${GITEA_DB_USER:?} @@ -28,3 +32,9 @@ services: - POSTGRES_DB=${GITEA_DB:?} volumes: - ./db:/var/lib/postgresql/data + networks: + - gitea_network + +networks: + gitea_network: + driver: bridge diff --git a/matrix/.env.example b/matrix/.env.example new file mode 100644 index 0000000..468cdc6 --- /dev/null +++ b/matrix/.env.example @@ -0,0 +1,3 @@ +MATRIX_DB=matrix +MATRIX_DB_USER=user +MATRIX_DB_PASSWORD=password diff --git a/matrix/create_config.sh b/matrix/create_config.sh new file mode 100644 index 0000000..14194c6 --- /dev/null +++ b/matrix/create_config.sh @@ -0,0 +1,5 @@ +docker run -it --rm \ + -v "$(pwd)/synapse_data:/data" \ + -e SYNAPSE_SERVER_NAME=example.com \ + -e SYNAPSE_REPORT_STATS=yes \ + matrixdotorg/synapse:latest generate diff --git a/matrix/create_user.sh b/matrix/create_user.sh new file mode 100644 index 0000000..b4bd3d0 --- /dev/null +++ b/matrix/create_user.sh @@ -0,0 +1,2 @@ +docker exec -it matrix-synapse-1 register_new_matrix_user \ + -c /data/homeserver.yaml http://localhost:8008 diff --git a/matrix/docker-compose.yml b/matrix/docker-compose.yml new file mode 100644 index 0000000..c66c521 --- /dev/null +++ b/matrix/docker-compose.yml @@ -0,0 +1,38 @@ +services: + matrix-synapse: + image: docker.io/matrixdotorg/synapse:latest + container_name: matrix-synapse + restart: unless-stopped + volumes: + - ./data:/data + environment: + - SYNAPSE_CONFIG_PATH=/data/homeserver.yaml + depends_on: + - matrix-db + networks: + - matrix_network + + matrix-db: + image: docker.io/postgres:14-alpine + container_name: matrix-db + restart: unless-stopped + environment: + - POSTGRES_USER=${MATRIX_DB_USER:?} + - POSTGRES_PASSWORD=${MATRIX_DB_PASSWORD:?} + - POSTGRES_DB=${MATRIX_DB:?} + - POSTGRES_INITDB_ARGS=--encoding=UTF-8 --lc-collate=C --lc-ctype=C + volumes: + - ./db:/var/lib/postgresql/data + networks: + - matrix_network + + matrix-element: + image: vectorim/element-web:latest + container_name: matrix-element + restart: unless-stopped + networks: + - matrix_network + +networks: + matrix_network: + driver: bridge diff --git a/proxy/.env.example b/proxy/.env.example index 60fa8e9..ced3f68 100644 --- a/proxy/.env.example +++ b/proxy/.env.example @@ -1,4 +1,2 @@ -# Используется https://cloud.alviy.com/ddns - -DDNS_TOKEN=token -DDNS_DOMAINS=domain.dynnamn.ru +REGRU_LOGIN=login +REGRU_PASSWORD=password diff --git a/proxy/create-first-cert-example.sh b/proxy/create-first-cert-example.sh index f72d1d2..f1df40f 100644 --- a/proxy/create-first-cert-example.sh +++ b/proxy/create-first-cert-example.sh @@ -1,6 +1,6 @@ docker compose -f init-compose.yml up -d -docker compose run --rm certbot certonly --webroot \ +docker compose run --rm --entrypoint "certbot" certbot certonly --webroot \ --webroot-path=/var/www/certbot \ --email your-email@gmail.com \ --agree-tos \ diff --git a/proxy/ddns/Dockerfile b/proxy/ddns/Dockerfile new file mode 100644 index 0000000..2597046 --- /dev/null +++ b/proxy/ddns/Dockerfile @@ -0,0 +1,10 @@ +FROM python:3.10-slim + +WORKDIR /app + +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +COPY ddns_updater.py . + +ENTRYPOINT ["python", "ddns_updater.py"] diff --git a/proxy/ddns/ddns_updater.py b/proxy/ddns/ddns_updater.py new file mode 100644 index 0000000..b747eb2 --- /dev/null +++ b/proxy/ddns/ddns_updater.py @@ -0,0 +1,170 @@ +import schedule +import requests +import json +import time +import argparse +import logging +import sys + +parser = argparse.ArgumentParser(description="DDNS for reg.ru") +parser.add_argument("login", help="Почта на reg.ru") +parser.add_argument("password", help="Пароль на reg.ru") +parser.add_argument("-d", dest="delay", default=30, type=int, + help="Задержка между проверкой ip в минутах") + +logging.basicConfig( + level=logging.INFO, + format='[%(asctime)s] [%(levelname)s] - %(message)s', + datefmt='%d-%b-%y %H:%M:%S', + handlers=[ + logging.FileHandler("logs.txt"), + logging.StreamHandler(sys.stdout) + ] +) + +# Отключаем лишние логи от requests +logging.getLogger("urllib3").setLevel(logging.WARNING) + +def get_external_ip(): + """Пробует получить внешний IP через разные HTTP сервисы""" + services = [ + "https://api.ipify.org", + "https://ifconfig.me/ip", + "https://ident.me", + "https://icanhazip.com" + ] + + for service in services: + try: + logging.debug(f"Запрос IP через {service}...") + response = requests.get(service, timeout=10) + if response.status_code == 200: + ip = response.text.strip() + if ip: + return ip + except Exception as e: + logging.warning(f"Сервис {service} недоступен: {e}") + continue + return None + +def cheker(): + logging.info("--- Проверка внешнего IP ---") + cur_ip = get_external_ip() + + if not cur_ip: + logging.error("Не удалось определить внешний IP ни через один сервис!") + return + + logging.info(f"Ваш текущий IP: {cur_ip}") + + res = update_ip(cur_ip) + + if res is not True: + code, message = res + logging.error(f"Ошибка API: {code} - {message}") + +def update_ip(ip): + try: + with open("domains.txt", "r") as file: + content = file.read().strip() + + if not content: + logging.warning("Файл domains.txt пуст.") + return True + + auth_data = { + "username": args.login, + "password": args.password, + "output_content_type": "json" + } + + groups = [g for g in content.split("\n\n") if g.strip()] + + for group in groups: + lines = [line.strip() for line in group.split("\n") if line.strip()] + if len(lines) < 2: continue + + domain_name = lines[0] + aliases = lines[1:] + + logging.info(f"Проверка домена {domain_name}...") + + # Получаем текущие записи + input_data = {**auth_data, "domains": [{"dname": domain_name}]} + params = {"input_data": json.dumps(input_data), "input_format": "json"} + + resp = requests.post("https://api.reg.ru/api/regru2/zone/get_resource_records", data=params).json() + + if resp.get("result") == "error": + return resp.get("error_code"), resp.get("error_text") + + current_rrs = resp["answer"]["domains"][0].get("rrs", []) + + for sub in aliases: + already_correct = False + outdated_records = [] + + for rr in current_rrs: + if rr.get("rectype") == "A" and rr.get("subname") == sub: + if rr.get("content") == ip: + already_correct = True + else: + outdated_records.append(rr) + + if already_correct: + logging.info(f" [{sub}.{domain_name}] Пропуск: IP уже актуален ({ip})") + else: + logging.info(f" [{sub}.{domain_name}] Обновление записи...") + # Удаляем старые + for old_rr in outdated_records: + remove_old_record(auth_data, domain_name, old_rr) + # Создаем новую + add_new_record(auth_data, domain_name, sub, ip) + + except FileNotFoundError: + logging.error("Файл domains.txt не найден!") + except Exception as e: + return "UNKNOWN_ERROR", str(e) + return True + +def remove_old_record(auth, domain, rr): + logging.info(f" Удаление старой записи: {rr['subname']} -> {rr['content']}") + data = { + **auth, + "domains": [{"dname": domain}], + "subdomain": rr["subname"], + "content": rr["content"], + "record_type": "A" + } + requests.post("https://api.reg.ru/api/regru2/zone/remove_record", data={"input_data": json.dumps(data), "input_format": "json"}) + +def add_new_record(auth, domain, sub, ip): + logging.info(f" Создание новой записи: {sub} -> {ip}") + data = { + **auth, + "domains": [{"dname": domain}], + "subdomain": sub, + "ipaddr": ip + } + res = requests.post("https://api.reg.ru/api/regru2/zone/add_alias", data={"input_data": json.dumps(data), "input_format": "json"}).json() + if res.get("result") == "error": + logging.error(f" Ошибка API при добавлении: {res.get('error_text')}") + +if __name__ == '__main__': + args = parser.parse_args() + logging.info("==========================================") + logging.info("Запуск контейнера DDNS") + logging.info("Ожидание 30 секунд (загрузка сети/роутера)...") + logging.info("==========================================") + + time.sleep(30) + + logging.info("Начинаю работу...") + + cheker() + + schedule.every(args.delay).minutes.do(cheker) + + while True: + schedule.run_pending() + time.sleep(1) diff --git a/proxy/ddns/domains.txt b/proxy/ddns/domains.txt new file mode 100644 index 0000000..9eb1f4b --- /dev/null +++ b/proxy/ddns/domains.txt @@ -0,0 +1,7 @@ +stepanovplaton.ru +@ +www +git +disk +matrix +chat diff --git a/proxy/ddns/domains.txt.example b/proxy/ddns/domains.txt.example new file mode 100644 index 0000000..8f8e037 --- /dev/null +++ b/proxy/ddns/domains.txt.example @@ -0,0 +1,10 @@ +domain.com +@ +www +subdomain1 +subdomain2 + +domain2.com +@ +www +subdomain1 diff --git a/proxy/ddns/requirements.txt b/proxy/ddns/requirements.txt new file mode 100644 index 0000000..107f8a8 --- /dev/null +++ b/proxy/ddns/requirements.txt @@ -0,0 +1,2 @@ +schedule +requests diff --git a/proxy/docker-compose.yml b/proxy/docker-compose.yml index bd406e6..f5acb22 100644 --- a/proxy/docker-compose.yml +++ b/proxy/docker-compose.yml @@ -1,17 +1,4 @@ services: - ddns-updater: - image: alpine:latest - container_name: ddns-updater - env_file: .env - volumes: - - ./update_dns.sh:/update_dns.sh:ro - entrypoint: ["/bin/sh", "-c"] - command: - - | - apk add --no-cache curl bash - /bin/bash /update_dns.sh - restart: "no" - nginx-proxy: image: nginx:alpine container_name: nginx-proxy @@ -26,6 +13,11 @@ services: - ./ssl/www:/var/www/certbot:ro # Подхватываем новые ssl сертификаты command: /bin/sh -c "while :; do sleep 24h & wait $${!}; nginx -s reload; done & nginx -g 'daemon off;'" + networks: + - proxy_network + - gitea_network + - cloud_network + - matrix_network certbot: image: certbot/certbot @@ -34,4 +26,31 @@ services: - ./ssl/conf:/etc/letsencrypt - ./ssl/www:/var/www/certbot # Проверяет сертификаты дважды в сутки. Если осталось менее 30 дней - обновляем - entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'" + entrypoint: "/bin/sh -c 'trap exit TERM; while :; sleep 30s & wait $${!}; do certbot renew; sleep 12h & wait $${!}; done;'" + networks: + - proxy_network + + ddns: + build: ./ddns + container_name: ddns + restart: always + command: ["${REGRU_LOGIN:?}", "${REGRU_PASSWORD:?}"] + volumes: + - ./ddns/domains.txt:/app/domains.txt + environment: + - TZ=Europe/Samara + networks: + - proxy_network + +networks: + proxy_network: + driver: bridge + gitea_network: + external: true + name: gitea_gitea_network + cloud_network: + external: true + name: cloud_cloud_network + matrix_network: + external: true + name: matrix_matrix_network diff --git a/proxy/init-compose.yml b/proxy/init-compose.yml index abdbc7e..39f5b12 100644 --- a/proxy/init-compose.yml +++ b/proxy/init-compose.yml @@ -1,16 +1,13 @@ services: - ddns-updater: - image: alpine:latest - container_name: ddns-updater - env_file: .env + ddns: + build: ./ddns + container_name: ddns + restart: always + command: ["${REGRU_LOGIN:?}", "${REGRU_PASSWORD:?}"] volumes: - - ./update_dns.sh:/update_dns.sh:ro - entrypoint: ["/bin/sh", "-c"] - command: - - | - apk add --no-cache curl bash - /bin/bash /update_dns.sh - restart: "no" + - ./ddns/domains.txt:/app/domains.txt + environment: + - TZ=Europe/Samara nginx-proxy: image: nginx:alpine @@ -20,4 +17,6 @@ services: - 80:80 volumes: - ./nginx/init:/etc/nginx/conf.d:ro - + # Папки для SSL сертификатов + - ./ssl/conf:/etc/letsencrypt:ro + - ./ssl/www:/var/www/certbot:ro diff --git a/proxy/nginx/conf.d/default.conf.example b/proxy/nginx/conf.d/default.conf.example index 5921cc6..b523785 100644 --- a/proxy/nginx/conf.d/default.conf.example +++ b/proxy/nginx/conf.d/default.conf.example @@ -19,7 +19,9 @@ server { ssl_certificate_key /etc/letsencrypt/live/domain.dynnamn.ru/privkey.pem; location / { - return 200 "Hello world!"; + charset utf-8; + default_type text/plain; + return 200 "Hello domain.dynnamn.ru!"; # proxy_pass http://your_app_container:port; # proxy_set_header Host $host; } diff --git a/proxy/nginx/init/default.conf.example b/proxy/nginx/init/default.conf.example deleted file mode 100644 index 8ba809d..0000000 --- a/proxy/nginx/init/default.conf.example +++ /dev/null @@ -1,12 +0,0 @@ -server { - listen 80; - server_name domain.dynnamn.ru domain2.dynnamn.ru; - - location /.well-known/acme-challenge/ { - root /var/www/certbot; - } - - location / { - return 200 "Hello HTTP!"; - } -} diff --git a/proxy/ssl/conf/.keep b/proxy/ssl/conf/.keep deleted file mode 100644 index e69de29..0000000 diff --git a/proxy/ssl/www/.keep b/proxy/ssl/www/.keep deleted file mode 100644 index e69de29..0000000 diff --git a/proxy/update_dns.sh b/proxy/update_dns.sh deleted file mode 100755 index 7171ac1..0000000 --- a/proxy/update_dns.sh +++ /dev/null @@ -1,49 +0,0 @@ -#!/bin/bash - -# Проверяем наличие необходимых переменных -if [ -z "$DDNS_TOKEN" ] || [ -z "$DDNS_DOMAINS" ]; then - echo "Ошибка: Переменные DDNS_TOKEN или DDNS_DOMAINS не заданы." - exit 1 -fi - -while true; do - echo "Определяем внешний IPv4..." - CURRENT_IP=$(curl -s https://ifconfig.me) - - if [ -z "$CURRENT_IP" ]; then - echo "Не удалось получить IP. Повтор через 30 секунд..." - sleep 30 - continue - fi - - echo "Ваш IP: $CURRENT_IP. Начинаем обновление доменов..." - - ALL_SUCCESS=true - - for DOMAIN in $DDNS_DOMAINS; do - echo "Обновляю домен: $DOMAIN" - - # Выполняем запрос PUT согласно вашему формату - RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" \ - -X 'PUT' "https://cloud.alviy.com/api/v1/ddns/domain/$DOMAIN" \ - -H 'accept: application/json' \ - -H "Authorization: Bearer $DDNS_TOKEN" \ - -H 'Content-Type: application/json' \ - -d "{ \"ipv4\": [ \"$CURRENT_IP\" ] }") - - if [ "$RESPONSE" == "200" ]; then - echo "Успешно обновлено: $DOMAIN" - else - echo "Ошибка обновления $DOMAIN. Код ответа: $RESPONSE" - ALL_SUCCESS=false - fi - done - - if [ "$ALL_SUCCESS" = true ]; then - echo "Все задачи выполнены успешно. Завершаю работу контейнера." - exit 0 - else - echo "Некоторые домены не обновились. Повторная попытка через 60 секунд..." - sleep 60 - fi -done diff --git a/transmission/docker-compose.yml b/transmission/docker-compose.yml index 5a1143e..d7c1536 100644 --- a/transmission/docker-compose.yml +++ b/transmission/docker-compose.yml @@ -2,9 +2,6 @@ services: transmission: image: lscr.io/linuxserver/transmission:latest container_name: transmission - deploy: - resources: - limits: environment: - PUID=${OWNER_USERID:?} - PGID=${OWNER_GROUPID:?}