diff --git a/nas/docs/network-scheme.md b/nas/docs/network-scheme.md new file mode 100644 index 0000000..e69de29 diff --git a/nas/gitea/app.ini b/nas/gitea/app.ini new file mode 100644 index 0000000..6a9955b --- /dev/null +++ b/nas/gitea/app.ini @@ -0,0 +1,71 @@ +APP_NAME = Hurricane Git +RUN_USER = git +WORK_PATH = /data/gitea +RUN_MODE = prod + +[database] +DB_TYPE = sqlite3 +HOST = 127.0.0.1:3306 +#NAME = gitea +#USER = gitea +PASSWD = +SCHEMA = +SSL_MODE = disable +PATH = /data/gitea/data/gitea.db +LOG_SQL = false + +[repository] +ROOT = /data/git + +[server] +SSH_DOMAIN = git.zod2.com +DOMAIN = git.zod2.com +HTTP_PORT = 3000 +ROOT_URL = https://git.zod2.com/ +APP_DATA_PATH = /data/gitea/data +DISABLE_SSH = true +LFS_START_SERVER = false + +[mailer] +ENABLED = false + +[service] +REGISTER_EMAIL_CONFIRM = false +ENABLE_NOTIFY_MAIL = false +DISABLE_REGISTRATION = true +ALLOW_ONLY_EXTERNAL_REGISTRATION = false +ENABLE_CAPTCHA = false +REQUIRE_SIGNIN_VIEW = false +DEFAULT_KEEP_EMAIL_PRIVATE = false +DEFAULT_ALLOW_CREATE_ORGANIZATION = true +DEFAULT_ENABLE_TIMETRACKING = true +NO_REPLY_ADDRESS = noreply.localhost + +[openid] +ENABLE_OPENID_SIGNIN = false +ENABLE_OPENID_SIGNUP = false + +[cron.update_checker] +ENABLED = false + +[session] +PROVIDER = file + +[log] +MODE = console +LEVEL = info +ROOT_PATH = /data/gitea/log + +[repository.pull-request] +DEFAULT_MERGE_STYLE = merge + +[repository.signing] +DEFAULT_TRUST_MODEL = committer + +[security] +INSTALL_LOCK = true +INTERNAL_TOKEN = CHANGEME +PASSWORD_HASH_ALGO = pbkdf2 + +[oauth2] +JWT_SECRET = CHANGEME diff --git a/nas/nfs/exports b/nas/nfs/exports new file mode 100644 index 0000000..81649d8 --- /dev/null +++ b/nas/nfs/exports @@ -0,0 +1,18 @@ +# /etc/exports: the access control list for filesystems which may be exported +# to NFS clients. See exports(5). +# +# Example for NFSv2 and NFSv3: +# /srv/homes hostname1(rw,sync,no_subtree_check) hostname2(ro,sync,no_subtree_check) +# +# Example for NFSv4: +# /srv/nfs4 gss/krb5i(rw,sync,fsid=0,crossmnt,no_subtree_check) +# /srv/nfs4/homes gss/krb5i(rw,sync,no_subtree_check) +# + +#/data 192.168.88.0/24(rw,sync,no_root_squash,no_subtree_check) +# /data 172.16.0.99(rw,sync,no_root_squash,no_subtree_check,crossmnt) +/data 10.10.10.0/24(rw,sync,no_root_squash,no_subtree_check) +#/data/personal/xalex/downloads/music 172.16.0.99(rw,sync,no_root_squash,no_subtree_check,fsid=10) + +/data/personal/xalex/downloads/music 172.16.0.99(rw,sync,no_subtree_check,all_squash,anonuid=1000,anongid=1006) +/data 192.168.88.0/24(rw,sync,no_subtree_check,all_squash,anonuid=1000,anongid=1006) diff --git a/nas/samba/smb.conf b/nas/samba/smb.conf new file mode 100644 index 0000000..916848c --- /dev/null +++ b/nas/samba/smb.conf @@ -0,0 +1,46 @@ +[global] + workgroup = WORKGROUP + server string = Hurricane NAS + security = user + map to guest = bad user + dns proxy = no + + # Логи + log file = /var/log/samba/samba.log + max log size = 50 + log level = 1 + +[personal] + comment = Personal Folder + path = /data/personal/%U + writable = yes + guest ok = no + valid users = xalex statefaults99 yogik-kaktyc sweety mammy jury + force group = nasusers + create mask = 0660 + directory mask = 2770 + inherit acls = yes + acl group control = yes + browsable = no + +[media] + comment = Media Folder + path = /data/media + writeable = yes + guest ok = yes + guest only = yes + force user = nobody + force create mode = 0777 + force directory mode = 0777 + browsable = no + +[torrents] +comment = torrents +path = /data/torrents/ +force user = nobody +writeable = yes +guest ok = yes +guest only = yes +force create mode = 777 +force directory mode = 777 +browsable = no diff --git a/nas/samba/smb.conf.backup b/nas/samba/smb.conf.backup new file mode 100644 index 0000000..cf32812 --- /dev/null +++ b/nas/samba/smb.conf.backup @@ -0,0 +1,198 @@ +# +# Sample configuration file for the Samba suite for Debian GNU/Linux. +# +# +# This is the main Samba configuration file. You should read the +# smb.conf(5) manual page in order to understand the options listed +# here. Samba has a huge number of configurable options most of which +# are not shown in this example +# +# Some options that are often worth tuning have been included as +# commented-out examples in this file. +# - When such options are commented with ";", the proposed setting +# differs from the default Samba behaviour +# - When commented with "#", the proposed setting is the default +# behaviour of Samba but the option is considered important +# enough to be mentioned here +# +# NOTE: Whenever you modify this file you should run the command +# "testparm" to check that you have not made any basic syntactic +# errors. + +#======================= Global Settings ======================= + +[global] + +## Browsing/Identification ### + +# Change this to the workgroup/NT-domain name your Samba server will part of + workgroup = WORKGROUP + +#### Networking #### + +# The specific set of interfaces / networks to bind to +# This can be either the interface name or an IP address/netmask; +# interface names are normally preferred +; interfaces = 127.0.0.0/8 eth0 + +# Only bind to the named interfaces and/or networks; you must use the +# 'interfaces' option above to use this. +# It is recommended that you enable this feature if your Samba machine is +# not protected by a firewall or is a firewall itself. However, this +# option cannot handle dynamic or non-broadcast interfaces correctly. +; bind interfaces only = yes + + + +#### Debugging/Accounting #### + +# This tells Samba to use a separate log file for each machine +# that connects + log file = /var/log/samba/log.%m + +# Cap the size of the individual log files (in KiB). + max log size = 1000 + +# We want Samba to only log to /var/log/samba/log.{smbd,nmbd}. +# Append syslog@1 if you want important messages to be sent to syslog too. + logging = file + +# Do something sensible when Samba crashes: mail the admin a backtrace + panic action = /usr/share/samba/panic-action %d + + +####### Authentication ####### + +# Server role. Defines in which mode Samba will operate. Possible +# values are "standalone server", "member server", "classic primary +# domain controller", "classic backup domain controller", "active +# directory domain controller". +# +# Most people will want "standalone server" or "member server". +# Running as "active directory domain controller" will require first +# running "samba-tool domain provision" to wipe databases and create a +# new domain. + server role = standalone server + + obey pam restrictions = yes + +# This boolean parameter controls whether Samba attempts to sync the Unix +# password with the SMB password when the encrypted SMB password in the +# passdb is changed. + unix password sync = yes + +# For Unix password sync to work on a Debian GNU/Linux system, the following +# parameters must be set (thanks to Ian Kahan < for +# sending the correct chat script for the passwd program in Debian Sarge). + passwd program = /usr/bin/passwd %u + passwd chat = *Enter\snew\s*\spassword:* %n\n *Retype\snew\s*\spassword:* %n\n *password\supdated\ssuccessfully* . + +# This boolean controls whether PAM will be used for password changes +# when requested by an SMB client instead of the program listed in +# 'passwd program'. The default is 'no'. + pam password change = yes + +# This option controls how unsuccessful authentication attempts are mapped +# to anonymous connections + map to guest = bad user + +########## Domains ########### + +# +# The following settings only takes effect if 'server role = classic +# primary domain controller', 'server role = classic backup domain controller' +# or 'domain logons' is set +# + +# It specifies the location of the user's +# profile directory from the client point of view) The following +# required a [profiles] share to be setup on the samba server (see +# below) +; logon path = \\%N\profiles\%U +# Another common choice is storing the profile in the user's home directory +# (this is Samba's default) +# logon path = \\%N\%U\profile + +# The following setting only takes effect if 'domain logons' is set +# It specifies the location of a user's home directory (from the client +# point of view) +; logon drive = H: +# logon home = \\%N\%U + +# The following setting only takes effect if 'domain logons' is set +# It specifies the script to run during logon. The script must be stored +# in the [netlogon] share +# NOTE: Must be store in 'DOS' file format convention +; logon script = logon.cmd + +# This allows Unix users to be created on the domain controller via the SAMR +# RPC pipe. The example command creates a user account with a disabled Unix +# password; please adapt to your needs +; add user script = /usr/sbin/adduser --quiet --disabled-password --gecos "" %u + +# This allows machine accounts to be created on the domain controller via the +# SAMR RPC pipe. +# The following assumes a "machines" group exists on the system +; add machine script = /usr/sbin/useradd -g machines -c "%u machine account" -d /var/lib/samba -s /bin/false %u + +# This allows Unix groups to be created on the domain controller via the SAMR +# RPC pipe. +; add group script = /usr/sbin/addgroup --force-badname %g + +############ Misc ############ + +# Using the following line enables you to customise your configuration +# on a per machine basis. The %m gets replaced with the netbios name +# of the machine that is connecting +; include = /home/samba/etc/smb.conf.%m + +# Some defaults for winbind (make sure you're not using the ranges +# for something else.) +; idmap config * : backend = tdb +; idmap config * : range = 3000-7999 +; idmap config YOURDOMAINHERE : backend = tdb +; idmap config YOURDOMAINHERE : range = 100000-999999 +; template shell = /bin/bash + +# Setup usershare options to enable non-root users to share folders +# with the net usershare command. + +# Maximum number of usershare. 0 means that usershare is disabled. +# usershare max shares = 100 + +# Allow users who've been granted usershare privileges to create +# public shares, not just authenticated ones + usershare allow guests = yes + +#======================= Share Definitions ======================= +[media] +comment = Public Folder +path = /data/media +writeable = yes +guest ok = yes +guest only = yes +force user = nobody +force create mode = 777 +force directory mode = 777 + +[personal] + comment = Personal Folder + path = /data/personal + writable = yes + guest ok = no + valid users = @smbshare + force create mode = 750 + force directory mode = 750 + inherit permissions = yes + browsable = no + +[torrents] +comment = torrents +path = /data/torrents/ +force user = nobody +writeable = yes +guest ok = yes +guest only = yes +force create mode = 777 +force directory mode = 777 +browsable = no diff --git a/nas/scripts/addfbuser b/nas/scripts/addfbuser new file mode 100755 index 0000000..f10bf5a --- /dev/null +++ b/nas/scripts/addfbuser @@ -0,0 +1,52 @@ +#!/bin/bash +set -e + +DB="/home/filebrowser/filebrowser.db" +BASE_DIR="/data/personal" +FB_USER="filebrowser" +MEDIA_GROUP="nasusers" +DMN="filebrowser.service" +DRY_RUN=false + +if [ -z "$1" ] || [ -z "$2" ]; then + echo "Использование: $0 " + exit 1 +fi + +USERNAME="$1" +PASSWORD="$2" +USER_DIR="$BASE_DIR/$USERNAME" + +run() { + if [ "$DRY_RUN" = true ]; then + echo "[DRY-RUN] $*" + else + "$@" + fi +} + +echo ">>> Останавливаем демон" +run systemctl stop $DMN + +echo ">>> Создаём пользователя в filebrowser" +run filebrowser users add "$USERNAME" "$PASSWORD" \ + --scope "$USERNAME" \ + --perm.create \ + --perm.modify \ + --perm.delete \ + --perm.rename \ + --perm.download \ + --perm.share \ + -d "$DB" + +echo ">>> Создаём каталог и выставляем права" +run mkdir -p "$USER_DIR" +run chown root:"$MEDIA_GROUP" "$USER_DIR" +run chmod 2770 "$USER_DIR" +run setfacl -d -m g:"$MEDIA_GROUP":rwx "$USER_DIR" +run setfacl -d -m o::0 "$USER_DIR" + +echo ">>> Запускаемся обратно" +run systemctl start $DMN + +echo ">>> Готово! Пользователь $USERNAME создан, папка: $USER_DIR" diff --git a/nas/scripts/bandcamp-dl b/nas/scripts/bandcamp-dl new file mode 100755 index 0000000..4e4811f --- /dev/null +++ b/nas/scripts/bandcamp-dl @@ -0,0 +1,8 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from bandcamp_dl.__main__ import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/nas/scripts/fixperms b/nas/scripts/fixperms new file mode 100755 index 0000000..8f4ca0a --- /dev/null +++ b/nas/scripts/fixperms @@ -0,0 +1,24 @@ +#!/bin/bash + +TARGET="$1" + +if [ -z "$TARGET" ]; then + echo "Использование: fixperms <путь>" + exit 1 +fi + +if [ ! -e "$TARGET" ]; then + echo "Ошибка: $TARGET не существует" + exit 1 +fi + +echo ">>> Фиксим права на $TARGET" + +chown -R root:nasusers "$TARGET" +chmod -R 2770 "$TARGET" +setfacl -R -m g:nasusers:rwx "$TARGET" +setfacl -R -d -m g:nasusers:rwx "$TARGET" +setfacl -R -m o::0 "$TARGET" +setfacl -R -d -m o::0 "$TARGET" + +echo ">>> Готово!" diff --git a/nas/scripts/hlab b/nas/scripts/hlab new file mode 100755 index 0000000..4a196a5 --- /dev/null +++ b/nas/scripts/hlab @@ -0,0 +1,292 @@ +#!/bin/bash + +CONFIG="/home/filebrowser/config" +source "$CONFIG" + +# ==================== SHOW ==================== + +show_system() { + echo "=== Системные пользователи ===" + getent passwd | awk -F: '$3 >= 1000 && $3 < 65534 {print $1}' +} + +show_samba() { + echo "=== Samba ===" + pdbedit -L | cut -d: -f1 +} + +show_filebrowser() { + echo "=== Filebrowser ===" + systemctl stop filebrowser.service + filebrowser users ls -d "$FB_DB" 2>/dev/null | tail -n +3 | awk '{print $2}' + systemctl start filebrowser.service +} + +show_jellyfin() { + echo "=== Jellyfin ===" + curl -s "$JF_URL/Users" \ + -H "X-Emby-Token: $JF_API_KEY" | \ + python3 -c "import sys,json; [print(u['Name']) for u in json.load(sys.stdin)]" +} + +show_all() { + show_system + echo "" + show_samba + echo "" + show_filebrowser + echo "" + show_jellyfin +} + +# ==================== PASSWORD ==================== + +get_password() { + local pass1 pass2 + while true; do + read -s -p "Новый пароль: " pass1 >&2 + echo "" >&2 + read -s -p "Повтори пароль: " pass2 >&2 + echo "" >&2 + if [ "$pass1" = "$pass2" ]; then + printf '%s' "$pass1" + return + fi + echo "Пароли не совпадают, попробуй ещё раз" >&2 + done +} + +passwd_samba() { + local username="$1" + local password="$2" + echo "$password" | smbpasswd -s -a "$username" + smbpasswd -e "$username" +} + +passwd_filebrowser() { + local username="$1" + local password="$2" + systemctl stop filebrowser.service + filebrowser users update "$username" --password "$password" -d "$FB_DB" + systemctl start filebrowser.service +} + +passwd_jellyfin() { + local username="$1" + local password="$2" + local user_id + user_id=$(curl -s "$JF_URL/Users" \ + -H "X-Emby-Token: $JF_API_KEY" | \ + python3 -c "import sys,json; users=json.load(sys.stdin); [print(u['Id']) for u in users if u['Name']=='$username']") + if [ -z "$user_id" ]; then + echo "Пользователь $username не найден в Jellyfin" + return 1 + fi + curl -s -X POST "$JF_URL/Users/$user_id/Password" \ + -H "X-Emby-Token: $JF_API_KEY" \ + -H "Content-Type: application/json" \ + -d "{\"NewPw\": \"$password\"}" + echo "Пароль в Jellyfin обновлён" +} + +edit_user() { + local service="$1" + local username="$2" + local password + password=$(get_password) + case "$service" in + smb) passwd_samba "$username" "$password" ;; + fb) passwd_filebrowser "$username" "$password" ;; + jf) passwd_jellyfin "$username" "$password" ;; + *) + echo "Неизвестный сервис: $service" + exit 1 + ;; + esac +} + +# ==================== ADD USER ==================== + +get_jf_libraries() { + curl -s "$JF_URL/Library/VirtualFolders" \ + -H "X-Emby-Token: $JF_API_KEY" | \ + python3 -c "import sys,json; [print(f['ItemId']) for f in json.load(sys.stdin)]" +} + +add_to_system() { + local username="$1" + if id "$username" &>/dev/null; then + echo ">>> Системный пользователь $username уже существует, пропускаем" + else + useradd -M -s /sbin/nologin "$username" + usermod -aG nasusers "$username" + echo ">>> Системный пользователь $username создан" + fi +} + +add_to_samba() { +# local username="$1" +# local password="$2" +# if pdbedit -L | grep -q "^$username:"; then +# echo ">>> Пользователь $username уже есть в Samba, пропускаем" +# else +# echo "$password" | smbpasswd -s -a "$username" +# smbpasswd -e "$username" +# echo ">>> Пользователь $username добавлен в Samba" +# fi + local username="$1" + local password="$2" + if pdbedit -L | grep -q "^$username:"; then + echo ">>> Пользователь $username уже есть в Samba, пропускаем" + else + # Сначала создаём системную запись в passdb + pdbedit -a -u "$username" -t << EOF > /dev/null 2>&1 +$password +$password +EOF + smbpasswd -e "$username" + echo ">>> Пользователь $username добавлен в Samba" + fi +} + +add_to_filebrowser() { + local username="$1" + local password="$2" + systemctl stop filebrowser.service + if filebrowser users ls -d "$FB_DB" 2>/dev/null | grep -q "$username"; then + echo ">>> Пользователь $username уже есть в Filebrowser, пропускаем" + else + filebrowser users add "$username" "$password" \ + --scope "$username" \ + --perm.create \ + --perm.modify \ + --perm.delete \ + --perm.rename \ + --perm.download \ + --perm.share \ + -d "$FB_DB" + echo ">>> Пользователь $username добавлен в Filebrowser" + fi + systemctl start filebrowser.service +} + +add_to_jellyfin() { + local username="$1" + local password="$2" + + local exists + exists=$(curl -s "$JF_URL/Users" \ + -H "X-Emby-Token: $JF_API_KEY" | \ + python3 -c "import sys,json; users=json.load(sys.stdin); print(any(u['Name']=='$username' for u in users))") + + if [ "$exists" = "True" ]; then + echo ">>> Пользователь $username уже есть в Jellyfin, пропускаем" + return + fi + + local result + result=$(curl -s -X POST "$JF_URL/Users/New" \ + -H "X-Emby-Token: $JF_API_KEY" \ + -H "Content-Type: application/json" \ + -d "{\"Name\": \"$username\", \"Password\": \"$password\"}") + + local user_id + user_id=$(echo "$result" | python3 -c "import sys,json; print(json.load(sys.stdin)['Id'])") + + if [ -z "$user_id" ]; then + echo ">>> Ошибка создания пользователя $username в Jellyfin" + return 1 + fi + + echo ">>> Пользователь $username добавлен в Jellyfin (id: $user_id)" +} + +create_homedir() { + local username="$1" + local user_dir="$BASE_DIR/$username" + + if [ -d "$user_dir" ]; then + echo ">>> Каталог $user_dir уже существует, пропускаем" + else + mkdir -p "$user_dir" + chown root:nasusers "$user_dir" + chmod 2770 "$user_dir" + setfacl -d -m g:nasusers:rwx "$user_dir" + setfacl -d -m o::0 "$user_dir" + echo ">>> Каталог $user_dir создан с ACL" + fi +} + +add_user() { + local username="$1" + local password + password=$(get_password) + + echo "" + echo ">>> Добавляем пользователя $username..." + echo "" + + add_to_system "$username" + create_homedir "$username" + add_to_samba "$username" "$password" + add_to_filebrowser "$username" "$password" + add_to_jellyfin "$username" "$password" + + echo "" + echo ">>> Готово! Пользователь $username создан во всех системах" +} + +# ==================== USAGE ==================== + +usage() { + echo "Использование: $0 <команда> [аргументы]" + echo "" + echo "Команды:" + echo " list [сервис] показать пользователей (system|smb|fb|jf|all)" + echo " passwd <сервис> сменить пароль пользователя" + echo " add добавить пользователя во все системы" + echo "" + echo "Примеры:" + echo " $0 list" + echo " $0 list jf" + echo " $0 passwd smb xalex" + echo " $0 passwd fb mammy" + echo " $0 add newuser" +} + +# ==================== MAIN ==================== + +case "${1}" in + list) + case "${2:-all}" in + system) show_system ;; + smb) show_samba ;; + fb) show_filebrowser ;; + jf) show_jellyfin ;; + all) show_all ;; + *) echo "Неизвестный сервис: $2"; usage; exit 1 ;; + esac + ;; + passwd) + if [ -z "$2" ] || [ -z "$3" ]; then + echo "Использование: $0 passwd <сервис> " + exit 1 + fi + edit_user "$2" "$3" + ;; + add) + if [ -z "$2" ]; then + echo "Использование: $0 add " + exit 1 + fi + add_user "$2" + ;; + -h|--help) + usage + ;; + *) + echo "Неизвестная команда: $1" + usage + exit 1 + ;; +esac diff --git a/nas/scripts/plinkmedia b/nas/scripts/plinkmedia new file mode 100755 index 0000000..993b535 --- /dev/null +++ b/nas/scripts/plinkmedia @@ -0,0 +1,114 @@ +#!/usr/bin/env python3 + +import sys +import os +import shutil +from pathlib import Path +from guessit import guessit + +MEDIA = Path("/data/media") +JULY = Path("/data/july") +DRY_RUN = "--dry-run" in sys.argv + +VIDEO_EXTENSIONS = {'.mkv', '.mp4', '.avi', '.mov', '.wmv', '.m4v', '.ts', '.m2ts'} + +def log(action, src, dst): + if DRY_RUN: + print(f"[DRY-RUN] {action}: {src} → {dst}") + else: + print(f"[{action}] {src} → {dst}") + +def hardlink_tree(src: Path, dst: Path): + dst.mkdir(parents=True, exist_ok=True) + if src.is_file(): + target = dst / src.name + if not target.exists(): + if not DRY_RUN: + os.link(src, target) + else: + print(f"[SKIP] уже существует: {target}") + elif src.is_dir(): + for f in src.iterdir(): + if f.is_file(): + target = dst / f.name + if not target.exists(): + if not DRY_RUN: + os.link(f, target) + else: + print(f"[SKIP] уже существует: {target}") + +def process(src: Path): + name = src.name + # Пропускаем не видео + if src.is_file() and src.suffix.lower() not in VIDEO_EXTENSIONS: + print(f"[SKIP] не видео: {name}") + return + + # Для папок — проверяем есть ли внутри видео файлы + if src.is_dir(): + has_video = any( + f.suffix.lower() in VIDEO_EXTENSIONS + for f in src.rglob("*") + if f.is_file() + ) + if not has_video: + print(f"[SKIP] нет видео файлов: {name}") + return + + info = guessit(name) + + + info = guessit(name) + + kind = info.get("type") + title = info.get("title", name) + year = info.get("year") + season = info.get("season") + + if kind == "movie": + folder_name = f"{title} ({year})" if year else title + dst = JULY / "Фильмы" / folder_name + log("MOVIE", src, dst) + hardlink_tree(src, dst) + + elif kind == "episode": + season_str = f"Season {season:02d}" if season else "Season 01" + dst = JULY / "Сериалы" / title / season_str + log("SERIES", src, dst) + hardlink_tree(src, dst) + + else: + print(f"[SKIP] не распознан: {name}") + +def main(): + args = [a for a in sys.argv[1:] if not a.startswith("--")] + + if len(args) == 0: + # Автоскан всего /data/media + for item in sorted(MEDIA.iterdir()): + process(item) + + elif len(args) == 1: + # Один элемент — полный путь или имя + src = Path(args[0]) + if not src.is_absolute(): + src = MEDIA / src + process(src) + + elif len(args) == 2: + # Вручную: источник + категория/название + src = Path(args[0]) + if not src.is_absolute(): + src = MEDIA / src + dst = JULY / args[1] + log("MANUAL", src, dst) + hardlink_tree(src, dst) + + else: + print("Использование:") + print(" linkmedia [--dry-run] # автоскан") + print(" linkmedia [--dry-run] # один элемент") + print(" linkmedia [--dry-run] <Категория/папка> # вручную") + +if __name__ == "__main__": + main() diff --git a/nas/scripts/transmission-postscript.sh b/nas/scripts/transmission-postscript.sh new file mode 100755 index 0000000..edaa36c --- /dev/null +++ b/nas/scripts/transmission-postscript.sh @@ -0,0 +1,18 @@ +#!/bin/bash + + +rm -rf /data/torrents/*.added + +# /usr/local/bin/plinkmedia "$TR_TORRENT_DIR/$TR_TORRENT_NAME" + +#dnlds='/data/downloads' +#files=$(ls $dnlds) + + +#for i in $files ; do +# ext="${i##*.}" +# ##### MOVING MOVIES TO MEDIA +# if [[ " ${movies[@]} " =~ " ${ext} " ]]; then +# mv "$dnlds/$i" /data/media/ +# fi +#done diff --git a/nas/systemd/filebrowser.service b/nas/systemd/filebrowser.service new file mode 100644 index 0000000..e1dd223 --- /dev/null +++ b/nas/systemd/filebrowser.service @@ -0,0 +1,14 @@ +[Unit] +Description=FileBrowser +After=network.target + +[Service] +User=filebrowser +Group=filebrowser +ExecStart=/usr/local/bin/filebrowser -c /etc/filebrowser/settings.json +Restart=always +RestartSec=3 +LimitNOFILE=4096 + +[Install] +WantedBy=multi-user.target diff --git a/nas/systemd/gitea.service b/nas/systemd/gitea.service new file mode 100644 index 0000000..763a0db --- /dev/null +++ b/nas/systemd/gitea.service @@ -0,0 +1,23 @@ +[Unit] +Description=Gitea +After=network.target + +[Service] +User=git +Group=git + +WorkingDirectory=/data/gitea + +ExecStart=/usr/local/bin/gitea web --config /etc/gitea/app.ini + +Restart=always +RestartSec=5 + +Environment=USER=git +Environment=HOME=/data/gitea +Environment=GITEA_WORK_DIR=/data/gitea + +LimitNOFILE=65535 + +[Install] +WantedBy=multi-user.target diff --git a/nas/systemd/jellyfin-proxy.service b/nas/systemd/jellyfin-proxy.service new file mode 100644 index 0000000..dcc7ef2 --- /dev/null +++ b/nas/systemd/jellyfin-proxy.service @@ -0,0 +1,18 @@ +[Unit] +Description=SSH SOCKS5 Proxy for jellyfin +After=network.target +Wants=network.target + +[Service] +Type=simple +ExecStart=/usr/bin/ssh -N -D 2281 -p 2288 \ + -o ServerAliveInterval=60 \ + -o ServerAliveCountMax=3 \ + -o ExitOnForwardFailure=yes \ + -o StrictHostKeyChecking=no \ + xalex@awg2 +Restart=always +RestartSec=10 + +[Install] +WantedBy=multi-user.target diff --git a/nas/transmission/README.json b/nas/transmission/README.json new file mode 100644 index 0000000..06a28b6 --- /dev/null +++ b/nas/transmission/README.json @@ -0,0 +1,8 @@ +Currently transmission overwrites the settings.json configuration file +on exit. The daemon can be instructed to reload the config with a SIGHUP, +or + # invoke-rc.d transmission-daemon reload + +Otherwise changes made manually to the file while the daemon is running +will be silently overwritten on exit. + diff --git a/nas/transmission/settings.json b/nas/transmission/settings.json new file mode 100644 index 0000000..7cf6395 --- /dev/null +++ b/nas/transmission/settings.json @@ -0,0 +1,75 @@ +{ + "alt-speed-down": 7000, + "alt-speed-enabled": false, + "alt-speed-time-begin": 540, + "alt-speed-time-day": 127, + "alt-speed-time-enabled": false, + "alt-speed-time-end": 1020, + "alt-speed-up": 7000, + "bind-address-ipv4": "0.0.0.0", + "bind-address-ipv6": "::", + "blocklist-enabled": false, + "blocklist-url": "http://www.example.com/blocklist", + "cache-size-mb": 4, + "dht-enabled": true, + "download-dir": "/data/media/", + "download-limit": 100, + "download-limit-enabled": 0, + "download-queue-enabled": true, + "download-queue-size": 5, + "encryption": 1, + "idle-seeding-limit": 30, + "idle-seeding-limit-enabled": false, + "incomplete-dir": "/data/media/", + "incomplete-dir-enabled": true, + "lpd-enabled": false, + "max-peers-global": 200, + "message-level": 1, + "peer-congestion-algorithm": "", + "peer-id-ttl-hours": 6, + "peer-limit-global": 200, + "peer-limit-per-torrent": 50, + "peer-port": 51413, + "peer-port-random-high": 65535, + "peer-port-random-low": 49152, + "peer-port-random-on-start": false, + "peer-socket-tos": "default", + "pex-enabled": true, + "port-forwarding-enabled": false, + "preallocation": 1, + "prefetch-enabled": true, + "queue-stalled-enabled": true, + "queue-stalled-minutes": 30, + "ratio-limit": 2, + "ratio-limit-enabled": false, + "rename-partial-files": true, + "rpc-authentication-required": true, + "rpc-bind-address": "0.0.0.0", + "rpc-enabled": true, + "rpc-host-whitelist": "10.10.10.0/24, 127.0.0.1, 192.168.88.0/24", + "rpc-host-whitelist-enabled": true, + "rpc-password": "{f02939d7b722620da9dbfbc66fb1f8449799deb6ZGI4fxdd", + "rpc-port": 9091, + "rpc-url": "/transmission/", + "rpc-username": "transmission", + "rpc-whitelist": "127.0.0.1, 192.168.88.*, 10.10.10.*", + "rpc-whitelist-enabled": true, + "scrape-paused-torrents-enabled": true, + "script-torrent-done-enabled": true, + "script-torrent-done-filename": "/usr/local/bin/transmission-postscript.sh", + "seed-queue-enabled": false, + "seed-queue-size": 10, + "speed-limit-down": 750, + "speed-limit-down-enabled": false, + "speed-limit-up": 100, + "speed-limit-up-enabled": false, + "start-added-torrents": true, + "trash-original-torrent-files": false, + "umask": 18, + "upload-limit": 100, + "upload-limit-enabled": 0, + "upload-slots-per-torrent": 14, + "utp-enabled": true, + "watch-dir": "/data/torrents/", + "watch-dir-enabled": true +} diff --git a/nas/transmission/settings.json.backup b/nas/transmission/settings.json.backup new file mode 100644 index 0000000..4ca0da3 --- /dev/null +++ b/nas/transmission/settings.json.backup @@ -0,0 +1,75 @@ +{ + "alt-speed-down": 7000, + "alt-speed-enabled": false, + "alt-speed-time-begin": 540, + "alt-speed-time-day": 127, + "alt-speed-time-enabled": false, + "alt-speed-time-end": 1020, + "alt-speed-up": 7000, + "bind-address-ipv4": "0.0.0.0", + "bind-address-ipv6": "::", + "blocklist-enabled": false, + "blocklist-url": "http://www.example.com/blocklist", + "cache-size-mb": 4, + "dht-enabled": true, + "download-dir": "/data/media/", + "download-limit": 100, + "download-limit-enabled": 0, + "download-queue-enabled": true, + "download-queue-size": 5, + "encryption": 1, + "idle-seeding-limit": 30, + "idle-seeding-limit-enabled": false, + "incomplete-dir": "/data/media/", + "incomplete-dir-enabled": true, + "lpd-enabled": false, + "max-peers-global": 200, + "message-level": 1, + "peer-congestion-algorithm": "", + "peer-id-ttl-hours": 6, + "peer-limit-global": 200, + "peer-limit-per-torrent": 50, + "peer-port": 51413, + "peer-port-random-high": 65535, + "peer-port-random-low": 49152, + "peer-port-random-on-start": false, + "peer-socket-tos": "default", + "pex-enabled": true, + "port-forwarding-enabled": false, + "preallocation": 1, + "prefetch-enabled": true, + "queue-stalled-enabled": true, + "queue-stalled-minutes": 30, + "ratio-limit": 2, + "ratio-limit-enabled": false, + "rename-partial-files": true, + "rpc-authentication-required": true, + "rpc-bind-address": "192.168.88.105", + "rpc-enabled": true, + "rpc-host-whitelist": "", + "rpc-host-whitelist-enabled": true, + "rpc-password": "{f02939d7b722620da9dbfbc66fb1f8449799deb6ZGI4fxdd", + "rpc-port": 9091, + "rpc-url": "/transmission/", + "rpc-username": "transmission", + "rpc-whitelist": "127.0.0.1, 192.168.88.*", + "rpc-whitelist-enabled": true, + "scrape-paused-torrents-enabled": true, + "script-torrent-done-enabled": true, + "script-torrent-done-filename": "/usr/local/bin/transmission-postscript.sh", + "seed-queue-enabled": false, + "seed-queue-size": 10, + "speed-limit-down": 750, + "speed-limit-down-enabled": false, + "speed-limit-up": 100, + "speed-limit-up-enabled": false, + "start-added-torrents": true, + "trash-original-torrent-files": false, + "umask": 18, + "upload-limit": 100, + "upload-limit-enabled": 0, + "upload-slots-per-torrent": 14, + "utp-enabled": true, + "watch-dir": "/data/torrents/", + "watch-dir-enabled": true +} diff --git a/nas/transmission/settings.json.backup_05032023 b/nas/transmission/settings.json.backup_05032023 new file mode 100644 index 0000000..0f8a15a --- /dev/null +++ b/nas/transmission/settings.json.backup_05032023 @@ -0,0 +1,73 @@ +{ + "alt-speed-down": 50, + "alt-speed-enabled": false, + "alt-speed-time-begin": 540, + "alt-speed-time-day": 127, + "alt-speed-time-enabled": false, + "alt-speed-time-end": 1020, + "alt-speed-up": 50, + "bind-address-ipv4": "0.0.0.0", + "bind-address-ipv6": "::", + "blocklist-enabled": false, + "blocklist-url": "http://www.example.com/blocklist", + "cache-size-mb": 4, + "dht-enabled": true, + "download-dir": "/var/lib/transmission-daemon/downloads", + "download-limit": 100, + "download-limit-enabled": 0, + "download-queue-enabled": true, + "download-queue-size": 5, + "encryption": 1, + "idle-seeding-limit": 30, + "idle-seeding-limit-enabled": false, + "incomplete-dir": "/var/lib/transmission-daemon/Downloads", + "incomplete-dir-enabled": false, + "lpd-enabled": false, + "max-peers-global": 200, + "message-level": 1, + "peer-congestion-algorithm": "", + "peer-id-ttl-hours": 6, + "peer-limit-global": 200, + "peer-limit-per-torrent": 50, + "peer-port": 51413, + "peer-port-random-high": 65535, + "peer-port-random-low": 49152, + "peer-port-random-on-start": false, + "peer-socket-tos": "default", + "pex-enabled": true, + "port-forwarding-enabled": false, + "preallocation": 1, + "prefetch-enabled": true, + "queue-stalled-enabled": true, + "queue-stalled-minutes": 30, + "ratio-limit": 2, + "ratio-limit-enabled": false, + "rename-partial-files": true, + "rpc-authentication-required": true, + "rpc-bind-address": "0.0.0.0", + "rpc-enabled": true, + "rpc-host-whitelist": "", + "rpc-host-whitelist-enabled": true, + "rpc-password": "{810f687a0316ab13bebab0be0652dd53b1e81a0cr51x0FHn", + "rpc-port": 9091, + "rpc-url": "/transmission/", + "rpc-username": "transmission", + "rpc-whitelist": "127.0.0.1", + "rpc-whitelist-enabled": true, + "scrape-paused-torrents-enabled": true, + "script-torrent-done-enabled": false, + "script-torrent-done-filename": "", + "seed-queue-enabled": false, + "seed-queue-size": 10, + "speed-limit-down": 100, + "speed-limit-down-enabled": false, + "speed-limit-up": 100, + "speed-limit-up-enabled": false, + "start-added-torrents": true, + "trash-original-torrent-files": false, + "umask": 18, + "upload-limit": 100, + "upload-limit-enabled": 0, + "upload-slots-per-torrent": 14, + "utp-enabled": true +} diff --git a/nas/transmission/settings.json.bakcup_08032023 b/nas/transmission/settings.json.bakcup_08032023 new file mode 100644 index 0000000..b1867f4 --- /dev/null +++ b/nas/transmission/settings.json.bakcup_08032023 @@ -0,0 +1,75 @@ +{ + "alt-speed-down": 50, + "alt-speed-enabled": false, + "alt-speed-time-begin": 540, + "alt-speed-time-day": 127, + "alt-speed-time-enabled": false, + "alt-speed-time-end": 1020, + "alt-speed-up": 50, + "bind-address-ipv4": "0.0.0.0", + "bind-address-ipv6": "::", + "blocklist-enabled": false, + "blocklist-url": "http://www.example.com/blocklist", + "cache-size-mb": 4, + "dht-enabled": true, + "download-dir": "/data/media/", + "download-limit": 100, + "download-limit-enabled": 0, + "download-queue-enabled": true, + "download-queue-size": 5, + "encryption": 1, + "idle-seeding-limit": 30, + "idle-seeding-limit-enabled": false, + "incomplete-dir": "/data/media/", + "incomplete-dir-enabled": true, + "lpd-enabled": false, + "max-peers-global": 200, + "message-level": 1, + "peer-congestion-algorithm": "", + "peer-id-ttl-hours": 6, + "peer-limit-global": 200, + "peer-limit-per-torrent": 50, + "peer-port": 51413, + "peer-port-random-high": 65535, + "peer-port-random-low": 49152, + "peer-port-random-on-start": false, + "peer-socket-tos": "default", + "pex-enabled": true, + "port-forwarding-enabled": false, + "preallocation": 1, + "prefetch-enabled": true, + "queue-stalled-enabled": true, + "queue-stalled-minutes": 30, + "ratio-limit": 2, + "ratio-limit-enabled": false, + "rename-partial-files": true, + "rpc-authentication-required": true, + "rpc-bind-address": "192.168.88.105", + "rpc-enabled": true, + "rpc-host-whitelist": "", + "rpc-host-whitelist-enabled": true, + "rpc-password": "{f02939d7b722620da9dbfbc66fb1f8449799deb6ZGI4fxdd", + "rpc-port": 9091, + "rpc-url": "/transmission/", + "rpc-username": "transmission", + "rpc-whitelist": "127.0.0.1, 192.168.88.*", + "rpc-whitelist-enabled": true, + "scrape-paused-torrents-enabled": true, + "script-torrent-done-enabled": true, + "script-torrent-done-filename": "/usr/local/bin/transmission-postscript.sh", + "seed-queue-enabled": false, + "seed-queue-size": 10, + "speed-limit-down": 100, + "speed-limit-down-enabled": false, + "speed-limit-up": 100, + "speed-limit-up-enabled": false, + "start-added-torrents": true, + "trash-original-torrent-files": false, + "umask": 18, + "upload-limit": 100, + "upload-limit-enabled": 0, + "upload-slots-per-torrent": 14, + "utp-enabled": true, + "watch-dir": "/data/torrents/", + "watch-dir-enabled": true +} diff --git a/nas/wireguard/wg0.conf b/nas/wireguard/wg0.conf new file mode 100644 index 0000000..98e5512 --- /dev/null +++ b/nas/wireguard/wg0.conf @@ -0,0 +1,10 @@ +[Interface] +PrivateKey = CHANGEME +Address = 10.10.10.2/24 +MTU = 1420 + +[Peer] +PublicKey = CHANGEME +AllowedIPs = 10.10.10.1/32 +Endpoint = zod2.com:50001 +PersistentKeepalive = 25