diff --git a/README.md b/README.md
index 21c9649..927df5e 100644
--- a/README.md
+++ b/README.md
@@ -98,10 +98,10 @@ PostgreSQL: хост, порт, база, пользователь, пароль
## Установка сервера одной командой
На Linux-сервере (amd64 или arm64, systemd):
```sh
-wget -qO- https://raw.githubusercontent.com/iRedTea/lpra/main/server-install.sh | sudo sh
+wget -qO- https://gitlab.com/itzRedTea/lpra/-/raw/main/server-install.sh | sudo sh
```
Скрипт `server-install.sh`:
-- качает бинарник сервера под архитектуру машины из [последнего релиза](https://github.com/iRedTea/lpra/releases/latest)
+- качает бинарник сервера под архитектуру машины из [последнего релиза](https://gitlab.com/itzRedTea/lpra/-/releases/permalink/latest)
и папку `dist/` (бинарники агентов) из ветки `main`, кладёт всё в `/opt/lpra`;
- ставит службу systemd `lpra-server` (порт спросит, по умолчанию `8091`);
- по желанию настраивает nginx: спросит домен, создаст сайт на порт 80 (при необходимости поставит nginx),
@@ -112,7 +112,7 @@ wget -qO- https://raw.githubusercontent.com/iRedTea/lpra/main/server-install.sh
Вопросы задаются в терминале, даже когда скрипт идёт через `| sudo sh`. Без терминала ответы берутся
из переменных: `LPRA_PORT`, `LPRA_NGINX` (y/n), `LPRA_DOMAIN`, `LPRA_EMAIL`, `LPRA_CERTBOT` (y/n), `LPRA_UFW` (y/n):
```sh
-wget -qO- https://raw.githubusercontent.com/iRedTea/lpra/main/server-install.sh | sudo LPRA_NGINX=y LPRA_DOMAIN=lpra.example.com sh
+wget -qO- https://gitlab.com/itzRedTea/lpra/-/raw/main/server-install.sh | sudo LPRA_NGINX=y LPRA_DOMAIN=lpra.example.com sh
```
Повторный запуск той же командой - **обновление**: бинарник и `dist/` заменяются, база (`/opt/lpra/lpra.db`
и её ключ `lpra.db.key`), настройки службы и nginx остаются. После установки откройте `/setup` - мастер
@@ -213,10 +213,10 @@ curl -fsSL https://panel.example/install.sh | sudo HOST=https://panel.example EN
- Открытие терминала и SSH, перенос файлов на другие машины, смена пользователя пишутся в `audit`.
## Скриншоты
-
+
-
+
-
+
-
+
diff --git a/docs/gitlab-migration.md b/docs/gitlab-migration.md
new file mode 100644
index 0000000..ff157c7
--- /dev/null
+++ b/docs/gitlab-migration.md
@@ -0,0 +1,144 @@
+# Перенос проекта с GitHub на GitLab с сохранением истории и дат
+
+Инструкция для локального репозитория, когда GitHub недоступен (аккаунт заблокирован) и
+переносить приходится из своей копии. Так был перенесён `lpra`: 195 коммитов, 8 тегов,
+3 релиза - хэши и даты совпали.
+
+## Что сохраняется, а что нет
+
+| Что | Сохраняется? |
+|---|---|
+| Даты коммитов (автора и коммита) | **Да, точно.** Они хранятся внутри коммита; push той же истории не меняет ни одного хэша |
+| Ветки и теги | Да, если они есть локально (см. шаг 2) |
+| Дата создания проекта | **Нет.** На gitlab.com она всегда - день создания проекта, задним числом не ставится. Начало проекта видно по первому коммиту |
+| Релизы | Создаются заново, **с исходной датой** (`--released-at`); файлы релизов - заново собрать или взять свои копии |
+| Issues, PR, звёзды, Actions | Нет: при заблокированном GitHub их не выгрузить |
+
+**Нельзя:** `rebase`, `squash`, `filter-branch`, `filter-repo`, `commit --amend` по старым
+коммитам, «Import from GitHub» с чужой копии - всё это переписывает коммиты и их даты.
+
+## 0. Один раз: вход в GitLab
+
+```sh
+brew install glab
+glab auth login --hostname gitlab.com --web --git-protocol https
+```
+
+Вход через браузер даёт права и на push, и на релизы. Git берёт пароль у glab - для каждого
+репозитория это настраивается в шаге 4 (или один раз для всех:
+`git config --global credential.https://gitlab.com.helper '!glab auth git-credential'`).
+
+SSH-ключ тоже подойдёт, но в GitLab у ключа должен быть **Usage type: Authentication & Signing**
+(с типом «Signing» по SSH не пускает).
+
+## 1. Пустой проект в GitLab
+
+https://gitlab.com/projects/new → **Create blank project** → имя, владелец `itzRedTea`.
+**Снять галочку «Initialize repository with a README»** - иначе в проекте появится свой первый
+коммит, и историю придётся склеивать.
+
+Или из терминала:
+
+```sh
+glab repo create itzRedTea/ИМЯ --public # или --private; README не создаётся
+```
+
+## 2. Проверить, что всё есть локально
+
+```sh
+cd ~/CodeRepos/ИМЯ
+git status # незакоммиченное закоммитить или убрать
+git branch -a # ветки: свои и remotes/origin/*
+git tag -l # теги
+```
+
+- Ветки, которых нет локально, но есть `remotes/origin/X`, тоже переносятся (шаг 3).
+- **Теги, созданные на GitHub при публикации релиза, могут не быть локально** - если их не
+ делали `git fetch --tags`. Такой тег создаётся заново на нужном коммите (шаг 5). Коммит
+ ищется по дате публикации релиза: последний коммит перед ней
+ (`git log --format='%h %cd %s' --date=iso --until='ДАТА'`).
+
+Снимок до переноса - для сверки:
+
+```sh
+git log --branches --remotes=origin --tags --format='%H %aI %cI' | sort > /tmp/before.txt
+git show-ref --tags | sort > /tmp/tags-before.txt
+```
+
+## 3. Отправить историю
+
+```sh
+git remote rename origin github # GitHub остаётся под своим именем
+git remote add origin https://gitlab.com/itzRedTea/ИМЯ.git
+H='!glab auth git-credential'
+git -c credential.helper= -c "credential.helper=$H" push origin --all # все локальные ветки
+git -c credential.helper= -c "credential.helper=$H" push origin 'refs/remotes/github/*:refs/heads/*' # ветки, что были только на GitHub
+git -c credential.helper= -c "credential.helper=$H" push origin --tags
+```
+
+Вторая команда push может сказать про `HEAD` - это не ветка, сообщение можно не смотреть.
+
+## 4. Сверить и переключиться
+
+Сверка - по свежей копии из GitLab:
+
+```sh
+rm -rf /tmp/check && git clone -q https://gitlab.com/itzRedTea/ИМЯ.git /tmp/check
+cd /tmp/check && git log --remotes=origin --tags --format='%H %aI %cI' | sort > /tmp/after.txt
+git show-ref --tags | sort > /tmp/tags-after.txt
+diff /tmp/before.txt /tmp/after.txt && echo "коммиты и даты совпали"
+diff /tmp/tags-before.txt /tmp/tags-after.txt && echo "теги совпали"
+```
+
+Пустой `diff` - история перенесена один в один. Дальше работать с GitLab:
+
+```sh
+cd ~/CodeRepos/ИМЯ
+git config --local credential.https://gitlab.com.helper '!glab auth git-credential'
+git fetch origin && git branch -u origin/main main
+```
+
+## 5. Релизы с исходными датами
+
+Дата - время публикации релиза на GitHub (UTC). Файлы - свои копии или сборка из коммита
+релиза во временной папке:
+
+```sh
+git worktree add --detach /tmp/rel-1.2.0 1.2.0 # тег или хэш коммита релиза
+cd /tmp/rel-1.2.0 && ./build.sh # как проект собирается
+glab release create 1.2.0 bin/файл1 bin/файл2 \
+ -R itzRedTea/ИМЯ --name "Release 1.2.0" \
+ --released-at "2026-10-15T16:34:53Z" \
+ --notes "Перенесено с GitHub, дата публикации исходная."
+# тега нет (его делал GitHub) - добавить: --ref <полный хэш коммита>
+git worktree remove /tmp/rel-1.2.0
+```
+
+Создавать по порядку, от старых к новым. Последним GitLab считает релиз с самой поздней
+датой. Ссылки на файлы:
+
+- конкретный релиз: `https://gitlab.com/itzRedTea/ИМЯ/-/releases/ТЕГ/downloads/ФАЙЛ`
+- последний: `https://gitlab.com/itzRedTea/ИМЯ/-/releases/permalink/latest/downloads/ФАЙЛ`
+
+## 6. Ссылки в самом проекте
+
+Найти и заменить всё, что смотрит на GitHub:
+
+```sh
+grep -rn "github.com\|githubusercontent" --exclude-dir=.git .
+```
+
+| GitHub | GitLab |
+|---|---|
+| `https://raw.githubusercontent.com/USER/ИМЯ/main/ПУТЬ` | `https://gitlab.com/itzRedTea/ИМЯ/-/raw/main/ПУТЬ` |
+| `https://github.com/USER/ИМЯ/releases/latest/download/ФАЙЛ` | `https://gitlab.com/itzRedTea/ИМЯ/-/releases/permalink/latest/downloads/ФАЙЛ` |
+| `https://api.github.com/repos/USER/ИМЯ/releases/latest` (`tag_name`) | `https://gitlab.com/api/v4/projects/itzRedTea%2FИМЯ/releases/permalink/latest` (`tag_name`) |
+| `https://api.github.com/repos/USER/ИМЯ/contents/ПАПКА?ref=main` | `https://gitlab.com/api/v4/projects/itzRedTea%2FИМЯ/repository/tree?path=ПАПКА&ref=main` (поле `path`) |
+| картинки `github.com/user-attachments/...` | скачать, пока открываются, в `docs/` и ссылаться на файлы |
+
+Путь Go-модуля (`module github.com/...` в `go.mod`) можно не трогать: это только имя.
+
+## 7. После переноса
+
+- Описание проекта в GitLab: «Перенесён с GitHub, начат ДД.ММ.ГГГГ» - раз дата создания новая.
+- Токены, которые светились в чатах и логах, - отозвать и выпустить новые.
diff --git a/docs/screenshots/1.png b/docs/screenshots/1.png
new file mode 100644
index 0000000..1e58fa7
Binary files /dev/null and b/docs/screenshots/1.png differ
diff --git a/docs/screenshots/2.png b/docs/screenshots/2.png
new file mode 100644
index 0000000..6d9cd03
Binary files /dev/null and b/docs/screenshots/2.png differ
diff --git a/docs/screenshots/3.png b/docs/screenshots/3.png
new file mode 100644
index 0000000..e3537eb
Binary files /dev/null and b/docs/screenshots/3.png differ
diff --git a/docs/screenshots/4.png b/docs/screenshots/4.png
new file mode 100644
index 0000000..7bc9958
Binary files /dev/null and b/docs/screenshots/4.png differ
diff --git a/server-install.sh b/server-install.sh
index 4b4d728..b35f141 100755
--- a/server-install.sh
+++ b/server-install.sh
@@ -1,9 +1,9 @@
#!/bin/sh
# LPRA server: install or update in one command.
#
-# wget -qO- https://raw.githubusercontent.com/iRedTea/lpra/main/server-install.sh | sudo sh
+# wget -qO- https://gitlab.com/itzRedTea/lpra/-/raw/main/server-install.sh | sudo sh
#
-# Takes the server binary for this machine from the latest GitHub release and
+# Takes the server binary for this machine from the latest GitLab release and
# the agent binaries (dist/) from the main branch, puts them in /opt/lpra,
# sets up the systemd service and, if asked, nginx with a domain and a
# certificate from certbot. Run again to update: the binary and dist/ are
@@ -16,7 +16,10 @@
# and where to.
set -eu
-REPO=${LPRA_REPO:-iRedTea/lpra}
+REPO=${LPRA_REPO:-itzRedTea/lpra}
+GL=https://gitlab.com
+# The project in the API's paths: its path with "/" as %2F.
+API="$GL/api/v4/projects/$(printf '%s' "$REPO" | sed 's|/|%2F|g')"
DIR=${LPRA_DIR:-/opt/lpra}
SERVICE=lpra-server
UNIT=/etc/systemd/system/$SERVICE.service
@@ -75,14 +78,14 @@ confirm() {
TMP=$(mktemp -d)
trap 'rm -rf "$TMP"' EXIT INT TERM
-TAG=$(fetch "https://api.github.com/repos/$REPO/releases/latest" 2>/dev/null |
+TAG=$(fetch "$API/releases/permalink/latest" 2>/dev/null | tr ',' '\n' |
sed -n 's/.*"tag_name": *"\([^"]*\)".*/\1/p' | head -n 1) || TAG=
if [ -n "$TAG" ]; then
say "последний релиз: $TAG"
- BIN_URL="https://github.com/$REPO/releases/download/$TAG/lpra-server-linux-$ARCH"
+ BIN_URL="$GL/$REPO/-/releases/$TAG/downloads/lpra-server-linux-$ARCH"
else
- warn "не узнать номер релиза у GitHub API - беру latest"
- BIN_URL="https://github.com/$REPO/releases/latest/download/lpra-server-linux-$ARCH"
+ warn "не узнать номер релиза у GitLab API - беру latest"
+ BIN_URL="$GL/$REPO/-/releases/permalink/latest/downloads/lpra-server-linux-$ARCH"
fi
say "скачиваю сервер (linux/$ARCH)"
dl "$BIN_URL" "$TMP/lpra-server" || die "не скачался $BIN_URL"
@@ -90,12 +93,13 @@ chmod +x "$TMP/lpra-server"
say "скачиваю dist/ из ветки main"
mkdir -p "$TMP/dist"
-URLS=$(fetch "https://api.github.com/repos/$REPO/contents/dist?ref=main" 2>/dev/null |
- sed -n 's/.*"download_url": *"\([^"]*\)".*/\1/p') || URLS=
+# The files of dist/ on main, by the API's list of the folder.
+URLS=$(fetch "$API/repository/tree?path=dist&ref=main&per_page=100" 2>/dev/null | tr '{},' '\n\n\n' |
+ sed -n 's|^"path": *"\(dist/[^"]*\)"$|'"$GL/$REPO"'/-/raw/main/\1|p') || URLS=
if [ -z "$URLS" ]; then
- warn "не получить список dist/ у GitHub API - беру агентов linux amd64 и arm64"
- URLS="https://raw.githubusercontent.com/$REPO/main/dist/agent-linux-amd64
-https://raw.githubusercontent.com/$REPO/main/dist/agent-linux-arm64"
+ warn "не получить список dist/ у GitLab API - беру агентов linux amd64 и arm64"
+ URLS="$GL/$REPO/-/raw/main/dist/agent-linux-amd64
+$GL/$REPO/-/raw/main/dist/agent-linux-arm64"
fi
for u in $URLS; do
f=${u##*/}