chore(github): generic example.com in docs, remove mirror scripts
Replace sac.kalinamall.ru with sac.example.com in public docs/deploy. Remove Push-Mirror and Rewrite-GitHostUrls scripts from repo. Simplify workspace-three-repos.md for GitHub-only workflow. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+1
-1
@@ -7,7 +7,7 @@ POSTGRES_PASSWORD=change-me-long-random
|
||||
POSTGRES_DB=sac
|
||||
|
||||
# API (публичный URL для ссылок в ответах ingest)
|
||||
SAC_PUBLIC_URL=https://sac.kalinamall.ru
|
||||
SAC_PUBLIC_URL=https://sac.example.com
|
||||
SAC_API_PORT=8000
|
||||
|
||||
# JWT (UI — позже)
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
1. [docs/install-ubuntu-24.04-native.md](../docs/install-ubuntu-24.04-native.md)
|
||||
2. `deploy/env.native.example` → `/opt/security-alert-center/config/sac-api.env`
|
||||
3. `deploy/systemd/sac-api.service` → `/etc/systemd/system/`
|
||||
4. `deploy/nginx/sac.conf.tls.example` → `/etc/nginx/sites-available/sac` (wildcard TLS; только `sac.kalinamall.ru`, чужие Host → 444)
|
||||
4. `deploy/nginx/sac.conf.tls.example` → `/etc/nginx/sites-available/sac` (wildcard TLS; только `sac.example.com`, чужие Host → 444)
|
||||
или `sac.conf.example` — только HTTP / отладка
|
||||
5. `deploy/sac-deploy.sh` → `/opt/sac-deploy.sh` — полное обновление после `git pull`
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
DATABASE_URL=postgresql+psycopg2://sac:CHANGE_ME_POSTGRES_PASSWORD@127.0.0.1:5432/sac
|
||||
|
||||
SAC_PUBLIC_URL=https://sac.kalinamall.ru
|
||||
SAC_PUBLIC_URL=https://sac.example.com
|
||||
JWT_SECRET=CHANGE_ME_openssl_rand_hex_32
|
||||
|
||||
# API key для агентов: Authorization: Bearer <ключ>
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
# /etc/nginx/sites-available/sac
|
||||
# Production FQDN: sac.kalinamall.ru
|
||||
# Production FQDN: sac.example.com
|
||||
# API: 127.0.0.1:8000 (systemd sac-api)
|
||||
#
|
||||
# Hostname: только sac.kalinamall.ru; остальные Host → 444 (см. default_server ниже).
|
||||
# Hostname: только sac.example.com; остальные Host → 444 (см. default_server ниже).
|
||||
#
|
||||
# Порядок: 1) этот конфиг (только :80) → nginx -t → reload
|
||||
# 2) certbot --nginx -d sac.kalinamall.ru (добавит :443 и сертификаты)
|
||||
# 2) certbot --nginx -d sac.example.com (добавит :443 и сертификаты)
|
||||
|
||||
upstream sac_api {
|
||||
server 127.0.0.1:8000;
|
||||
keepalive 8;
|
||||
}
|
||||
|
||||
# --- default: отклонить чужие Host (не sac.kalinamall.ru) ---
|
||||
# --- default: отклонить чужие Host (не sac.example.com) ---
|
||||
server {
|
||||
listen 80 default_server;
|
||||
listen [::]:80 default_server;
|
||||
@@ -23,7 +23,7 @@ server {
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name sac.kalinamall.ru;
|
||||
server_name sac.example.com;
|
||||
|
||||
location /.well-known/acme-challenge/ {
|
||||
root /var/www/html;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# /etc/nginx/sites-available/sac
|
||||
# HTTPS с корпоративным wildcard-сертификатом (например VeriSign / DigiCert).
|
||||
# Покрывает sac.kalinamall.ru при CN/SAN *.kalinamall.ru или sac.kalinamall.ru
|
||||
# Покрывает sac.example.com при CN/SAN *.example.com или sac.example.com
|
||||
#
|
||||
# Hostname: только sac.kalinamall.ru. Любой другой Host (например ext.kalinamall.ru
|
||||
# Hostname: только sac.example.com. Любой другой Host (например ext.example.com
|
||||
# при ошибке DNS/маршрутизации на IP SAC) получает 444 / reject handshake —
|
||||
# форма входа SAC не отдаётся на «чужом» имени.
|
||||
#
|
||||
@@ -32,18 +32,18 @@ server {
|
||||
ssl_reject_handshake on;
|
||||
}
|
||||
|
||||
# Редирект HTTP → HTTPS (только sac.kalinamall.ru)
|
||||
# Редирект HTTP → HTTPS (только sac.example.com)
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name sac.kalinamall.ru;
|
||||
server_name sac.example.com;
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
listen [::]:443 ssl http2;
|
||||
server_name sac.kalinamall.ru;
|
||||
server_name sac.example.com;
|
||||
|
||||
# --- замените пути на ваши файлы wildcard ---
|
||||
ssl_certificate /etc/ssl/sac/fullchain.pem;
|
||||
|
||||
@@ -40,7 +40,7 @@ else
|
||||
if [ -z "${UPSTREAM}" ]; then
|
||||
UPSTREAM="origin/main"
|
||||
fi
|
||||
log "WARN: fast-forward невозможен (часто после force-push на kalinamall) — reset --hard ${UPSTREAM}"
|
||||
log "WARN: fast-forward невозможен (часто после force-push на upstream) — reset --hard ${UPSTREAM}"
|
||||
sudo -u "${APP_USER}" git -C "${APP_ROOT}" reset --hard "${UPSTREAM}"
|
||||
fi
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
```ini
|
||||
# off | exclusive | dual | fallback
|
||||
UseSAC="exclusive"
|
||||
SAC_URL="https://sac.kalinamall.ru/api/v1/events"
|
||||
SAC_URL="https://sac.example.com/api/v1/events"
|
||||
SAC_API_KEY="sac_xxxxxxxx"
|
||||
SAC_SPOOL_DIR="/var/lib/ssh-monitor/sac-spool"
|
||||
SAC_SEND_HEARTBEAT="1"
|
||||
@@ -26,7 +26,7 @@ SAC_TIMEOUT_SEC="12"
|
||||
|
||||
```powershell
|
||||
$UseSAC = "exclusive" # off | exclusive | dual | fallback
|
||||
$SacUrl = "https://sac.kalinamall.ru/api/v1/events"
|
||||
$SacUrl = "https://sac.example.com/api/v1/events"
|
||||
$SacApiKey = "sac_xxxxxxxx"
|
||||
$SacSpoolDir = "D:\Soft\Logs\sac-spool"
|
||||
```
|
||||
@@ -74,7 +74,7 @@ UI **Настройки** (`/settings`) — правило + параметры
|
||||
|
||||
### 1.4. Версии и доставка обновлений
|
||||
|
||||
При **любом** изменении агента, влияющем на SAC или поведение на хосте, поднимайте версию и пушьте в **GitHub** (`github.com/PTah`); для зеркала kalinamall — `.\scripts\Push-Mirror.ps1 kalinamall`:
|
||||
При **любом** изменении агента, влияющем на SAC или поведение на хосте, поднимайте версию и пушьте в **GitHub** (`github.com/PTah`):
|
||||
|
||||
| Агент | Маркер версии | Как хост узнаёт о новой версии |
|
||||
|-------|---------------|--------------------------------|
|
||||
@@ -113,7 +113,7 @@ UI **Настройки** (`/settings`) — правило + параметры
|
||||
|
||||
```http
|
||||
POST /api/v1/events HTTP/1.1
|
||||
Host: sac.kalinamall.ru
|
||||
Host: sac.example.com
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer sac_xxxxxxxx
|
||||
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
|
||||
@@ -136,7 +136,7 @@ Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
|
||||
"event_id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"created": true,
|
||||
"sac_event_url": "https://sac.example.com/api/v1/events/12345",
|
||||
"problem_id": null
|
||||
"problem_id": null
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
+3
-3
@@ -21,7 +21,7 @@ deploy/
|
||||
sac-deploy.sh → /opt/sac-deploy.sh (полное обновление)
|
||||
```
|
||||
|
||||
**Production URL:** `https://sac.kalinamall.ru`
|
||||
**Production URL:** `https://sac.example.com`
|
||||
**Конфиг:** `/opt/security-alert-center/config/sac-api.env` (владелец `sac:sac`, chmod 600).
|
||||
**UI:** статика `frontend/dist`, раздаётся через FastAPI (после `npm run build`).
|
||||
|
||||
@@ -42,7 +42,7 @@ deploy/
|
||||
|
||||
## 2. Сетевые правила
|
||||
|
||||
**Агенты →** `https://sac.kalinamall.ru/api/v1/events`
|
||||
**Агенты →** `https://sac.example.com/api/v1/events`
|
||||
**SAC →** Telegram, SMTP (исходящие)
|
||||
**Админы →** 443 (UI/API через nginx)
|
||||
|
||||
@@ -66,7 +66,7 @@ deploy/
|
||||
```bash
|
||||
systemctl status sac-api
|
||||
curl -sS http://127.0.0.1:8000/health
|
||||
curl -sS https://sac.kalinamall.ru/health
|
||||
curl -sS https://sac.example.com/health
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
| Параметр | Пример |
|
||||
|----------|--------|
|
||||
| FQDN | `sac.kalinamall.ru` |
|
||||
| FQDN | `sac.example.com` |
|
||||
| IP | `10.0.0.50` |
|
||||
| Каталог | `/opt/security-alert-center` |
|
||||
| Пользователь сервиса | `sac` |
|
||||
@@ -144,7 +144,7 @@ sudo chmod 600 /opt/security-alert-center/config/sac-api.env
|
||||
|
||||
- `DATABASE_URL` — с паролем PostgreSQL из шага 4 (если в пароле есть `#`, `@`, `%` — возьмите значение в **кавычки**: `DATABASE_URL="postgresql+psycopg2://..."`)
|
||||
- `JWT_SECRET` — `openssl rand -hex 32`
|
||||
- `SAC_PUBLIC_URL` — `https://sac.kalinamall.ru`
|
||||
- `SAC_PUBLIC_URL` — `https://sac.example.com`
|
||||
- `SAC_BOOTSTRAP_API_KEY` — `python3.12 -c "import secrets; print('sac_'+secrets.token_urlsafe(32))"`
|
||||
- `SAC_ADMIN_PASSWORD` — пароль входа в веб-UI (отдельно от API key агентов)
|
||||
- `EVENT_SCHEMA_PATH=/opt/security-alert-center/schemas/event-schema-v1.json`
|
||||
@@ -195,7 +195,7 @@ journalctl -u sac-api -f
|
||||
## 9. nginx и TLS
|
||||
|
||||
Для KalinaMall: **корпоративный wildcard VeriSign** (или DigiCert), не Let's Encrypt.
|
||||
Хост `sac.kalinamall.ru` должен попадать под `*.kalinamall.ru`.
|
||||
Хост `sac.example.com` должен попадать под `*.example.com`.
|
||||
|
||||
**Подробно:** форматы, scp с Windows/Linux, `.pfx` → PEM, права — **[ssl-certificate.md](ssl-certificate.md)**.
|
||||
|
||||
@@ -223,7 +223,7 @@ sudo usermod -aG ssl-cert www-data
|
||||
|
||||
```bash
|
||||
# с вашего компьютера:
|
||||
scp fullchain.pem privkey.pem papatramp@<IP_SAC>:/tmp/
|
||||
scp fullchain.pem privkey.pem deploy@<IP_SAC>:/tmp/
|
||||
|
||||
# на сервере:
|
||||
sudo mv /tmp/fullchain.pem /tmp/privkey.pem /etc/ssl/sac/
|
||||
@@ -263,28 +263,28 @@ sudo nginx -t
|
||||
sudo systemctl reload nginx
|
||||
```
|
||||
|
||||
Шаблон `sac.conf.tls.example` содержит **default_server** с `return 444` (:80) и `ssl_reject_handshake` (:443) для любого Host, кроме `sac.kalinamall.ru`. Это защищает от ситуации, когда на IP SAC (например `192.168.160.145`) по ошибке DNS попадает `ext.kalinamall.ru` и открывается форма входа SAC.
|
||||
Шаблон `sac.conf.tls.example` содержит **default_server** с `return 444` (:80) и `ssl_reject_handshake` (:443) для любого Host, кроме `sac.example.com`. Это защищает от ситуации, когда на IP SAC (например `<SAC_SERVER_IP>`) по ошибке DNS попадает `ext.example.com` и открывается форма входа SAC.
|
||||
|
||||
Проверка после reload:
|
||||
|
||||
```bash
|
||||
curl -sS -o /dev/null -w '%{http_code}\n' --resolve sac.kalinamall.ru:443:127.0.0.1 https://sac.kalinamall.ru/health
|
||||
curl -sS -o /dev/null -w '%{http_code}\n' --resolve sac.example.com:443:127.0.0.1 https://sac.example.com/health
|
||||
# ожидается 200
|
||||
|
||||
curl -sS -o /dev/null -w '%{http_code}\n' --resolve ext.kalinamall.ru:443:127.0.0.1 https://ext.kalinamall.ru/ 2>/dev/null || echo "connection closed (444/reject OK)"
|
||||
curl -sS -o /dev/null -w '%{http_code}\n' --resolve ext.example.com:443:127.0.0.1 https://ext.example.com/ 2>/dev/null || echo "connection closed (444/reject OK)"
|
||||
```
|
||||
|
||||
Проверка:
|
||||
|
||||
```bash
|
||||
curl -sS https://sac.kalinamall.ru/health | jq .
|
||||
curl -sS https://sac.example.com/health | jq .
|
||||
```
|
||||
|
||||
В `config/sac-api.env` должно быть: `SAC_PUBLIC_URL=https://sac.kalinamall.ru`
|
||||
В `config/sac-api.env` должно быть: `SAC_PUBLIC_URL=https://sac.example.com`
|
||||
|
||||
### 9.3. Агенты (ssh-monitor, RDP) и доверие к CA
|
||||
|
||||
- Агенты ходят на `https://sac.kalinamall.ru/api/v1/events`.
|
||||
- Агенты ходят на `https://sac.example.com/api/v1/events`.
|
||||
- Если используется **внутренний корневой CA**, а не публичный VeriSign/DigiCert в системном store — на Linux/Windows может понадобиться установка корневого сертификата в доверенные (или корпоративный прокси с подменой TLS).
|
||||
- Для публичного коммерческого wildcard от известного УЦ обычно **дополнительных действий на агентах не нужно**.
|
||||
|
||||
@@ -299,7 +299,7 @@ curl -sS https://sac.kalinamall.ru/health | jq .
|
||||
```bash
|
||||
sudo cp deploy/nginx/sac.conf.example /etc/nginx/sites-available/sac
|
||||
# ... nginx -t, reload, затем:
|
||||
sudo certbot --nginx -d sac.kalinamall.ru
|
||||
sudo certbot --nginx -d sac.example.com
|
||||
```
|
||||
|
||||
---
|
||||
@@ -317,7 +317,7 @@ ls -la dist/index.html
|
||||
sudo systemctl restart sac-api
|
||||
```
|
||||
|
||||
В `config/sac-api.env` задайте `SAC_ADMIN_PASSWORD`, затем откройте `https://sac.kalinamall.ru/` (логин `SAC_ADMIN_USERNAME`, по умолчанию `admin`).
|
||||
В `config/sac-api.env` задайте `SAC_ADMIN_PASSWORD`, затем откройте `https://sac.example.com/` (логин `SAC_ADMIN_USERNAME`, по умолчанию `admin`).
|
||||
|
||||
После обновления с multi-user (миграция `009_sac_users`): выполните `alembic upgrade head` в каталоге `backend/`. При первом старте API создаст пользователя **admin** из `SAC_ADMIN_*`, если таблица `sac_users` пуста.
|
||||
|
||||
@@ -343,7 +343,7 @@ curl -sS http://127.0.0.1:8000/health | jq .
|
||||
Через nginx (после TLS):
|
||||
|
||||
```bash
|
||||
curl -sS https://sac.kalinamall.ru/health | jq .
|
||||
curl -sS https://sac.example.com/health | jq .
|
||||
```
|
||||
|
||||
Тест ingest (подставьте `SAC_BOOTSTRAP_API_KEY`):
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# SAC production — текущий статус (KalinaMall)
|
||||
# SAC production — пример статуса развёртывания
|
||||
|
||||
Ориентир для продолжения работ. Обновляйте при смене этапа.
|
||||
|
||||
**Сервер:** Ubuntu 24.04, FQDN `sac.kalinamall.ru`
|
||||
**Сервер:** Ubuntu 24.04, FQDN `sac.example.com`
|
||||
**Каталог:** `/opt/security-alert-center`
|
||||
**Пользователь сервиса:** `sac` (git, venv, npm — только от него)
|
||||
**Конфиг:** `/opt/security-alert-center/config/sac-api.env`
|
||||
|
||||
@@ -17,7 +17,7 @@ sudo /opt/scripts/update_ssh_monitor.sh
|
||||
|
||||
```ini
|
||||
UseSAC=exclusive
|
||||
SAC_URL=https://sac.kalinamall.ru
|
||||
SAC_URL=https://sac.example.com
|
||||
SAC_API_KEY=sac_xxxxxxxx
|
||||
SAC_TLS_SKIP_VERIFY=0
|
||||
SAC_SPOOL_DIR=/var/lib/ssh-monitor/sac-spool
|
||||
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
# Runbook: эксплуатация SAC
|
||||
|
||||
Краткие процедуры для prod (`sac.kalinamall.ru`, native install в `/opt/security-alert-center`).
|
||||
Краткие процедуры для prod (`sac.example.com`, native install в `/opt/security-alert-center`).
|
||||
|
||||
## Деплой приложения
|
||||
|
||||
@@ -13,7 +13,7 @@ sudo /opt/sac-deploy.sh
|
||||
Проверка:
|
||||
|
||||
```bash
|
||||
curl -sS https://sac.kalinamall.ru/health | jq .
|
||||
curl -sS https://sac.example.com/health | jq .
|
||||
```
|
||||
|
||||
Ожидается `status: ok`, `database: ok`. При устаревших heartbeat агентов — `status: degraded`, поле `hosts_stale` > 0.
|
||||
@@ -101,4 +101,4 @@ sudo -u sac bash -lc 'cd /opt/security-alert-center/backend && .venv/bin/python
|
||||
|
||||
**Устаревший heartbeat** — увеличить `SAC_HEARTBEAT_STALE_MINUTES` или проверить `agent.heartbeat` на хосте.
|
||||
|
||||
**Чужой Host на IP SAC (форма SAC на ext.kalinamall.ru)** — в nginx только `server_name sac.kalinamall.ru`; default_server → 444 / `ssl_reject_handshake`. Обновить конфиг из `deploy/nginx/sac.conf.tls.example`, `sudo nginx -t && sudo systemctl reload nginx`. HAProxy на этом не решается — правка на **192.168.160.145** (nginx SAC).
|
||||
**Чужой Host на IP SAC (форма SAC на ext.example.com)** — в nginx только `server_name sac.example.com`; default_server → 444 / `ssl_reject_handshake`. Обновить конфиг из `deploy/nginx/sac.conf.tls.example`, `sudo nginx -t && sudo systemctl reload nginx`. HAProxy на этом не решается — правка на **<SAC_SERVER_IP>** (nginx SAC).
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
| Способ входа | **Логин и пароль + код** — в приложении нужны учётные данные SAC; **Только код** — пароль не нужен, пользователь должен быть выбран в форме |
|
||||
| Срок (часов) | Код перестаёт принимать **новые** привязки; уже подключённые телефоны не отключаются |
|
||||
|
||||
Код показывается **один раз** после «Выдать код». Передайте оператору: URL сервера (**`https://sac-api.kalinamall.ru`** для Seaca; веб-админка — `https://sac.kalinamall.ru`), код `sacmob_…`, при режиме password — логин/пароль SAC.
|
||||
Код показывается **один раз** после «Выдать код». Передайте оператору: URL сервера (**`https://sac-api.example.com`** для Seaca; веб-админка — `https://sac.example.com`), код `sacmob_…`, при режиме password — логин/пароль SAC.
|
||||
|
||||
### Подключённые устройства
|
||||
|
||||
@@ -81,10 +81,10 @@ SAC_FCM_SERVICE_ACCOUNT_JSON=/etc/security-alert-center/fcm-service-account.json
|
||||
|
||||
| Hostname | Clients | IP allowlist |
|
||||
|----------|---------|--------------|
|
||||
| `sac.kalinamall.ru` | Web UI | yes (`git-sac-allowed`) |
|
||||
| `sac-api.kalinamall.ru` | Seaca, `/api/v1/mobile/*` | no (enroll + JWT) |
|
||||
| `sac.example.com` | Web UI | yes (`git-sac-allowed`) |
|
||||
| `sac-api.example.com` | Seaca, `/api/v1/mobile/*` | no (enroll + JWT) |
|
||||
|
||||
Both resolve to the same SAC server (**192.168.160.145**). nginx must accept both names in `server_name`. See [reverse-proxy/docs/sac-access.md](https://github.com/PTah/reverse-proxy/blob/main/docs/sac-access.md).
|
||||
Both resolve to the same SAC server (**<SAC_SERVER_IP>**). nginx must accept both names in `server_name`. See [reverse-proxy/docs/sac-access.md](https://github.com/PTah/reverse-proxy/blob/main/docs/sac-access.md).
|
||||
|
||||
---
|
||||
|
||||
|
||||
+20
-20
@@ -1,6 +1,6 @@
|
||||
# TLS-сертификат для SAC (wildcard VeriSign / DigiCert)
|
||||
|
||||
Куда положить файлы, в каком формате, как скопировать на сервер `sac.kalinamall.ru`.
|
||||
Куда положить файлы, в каком формате, как скопировать на сервер `sac.example.com`.
|
||||
|
||||
Связано: [install-ubuntu-24.04-native.md](install-ubuntu-24.04-native.md) §9, шаблон `deploy/nginx/sac.conf.tls.example`.
|
||||
|
||||
@@ -17,7 +17,7 @@ nginx читает **два PEM-файла** (текст, начинается
|
||||
|
||||
Имена файлов можно другие, если пути совпадают с `ssl_certificate` / `ssl_certificate_key` в `/etc/nginx/sites-available/sac`.
|
||||
|
||||
**Покрытие имени:** в сертификате должен быть CN или SAN вида `*.kalinamall.ru` (wildcard) или `sac.kalinamall.ru`.
|
||||
**Покрытие имени:** в сертификате должен быть CN или SAN вида `*.example.com` (wildcard) или `sac.example.com`.
|
||||
|
||||
---
|
||||
|
||||
@@ -85,7 +85,7 @@ sudo -u www-data test -r /etc/ssl/sac/privkey.pem && echo "key OK" || echo "key
|
||||
Если уже есть `fullchain.pem` и `privkey.pem` на ПК:
|
||||
|
||||
```bash
|
||||
scp fullchain.pem privkey.pem papatramp@SAC_SERVER:/tmp/
|
||||
scp fullchain.pem privkey.pem deploy@SAC_SERVER:/tmp/
|
||||
```
|
||||
|
||||
На сервере:
|
||||
@@ -103,11 +103,11 @@ sudo rm -f /tmp/fullchain.pem /tmp/privkey.pem
|
||||
|
||||
### 4.2. С Windows (PowerShell / pscp)
|
||||
|
||||
Файлы, например, в `D:\Certs\kalinamall\`:
|
||||
Файлы, например, в `D:\Certs\example\`:
|
||||
|
||||
```powershell
|
||||
scp D:\Certs\kalinamall\fullchain.pem papatramp@SAC_SERVER:/tmp/
|
||||
scp D:\Certs\kalinamall\privkey.pem papatramp@SAC_SERVER:/tmp/
|
||||
scp D:\Certs\example\fullchain.pem deploy@SAC_SERVER:/tmp/
|
||||
scp D:\Certs\example\privkey.pem deploy@SAC_SERVER:/tmp/
|
||||
```
|
||||
|
||||
Дальше на сервере — те же `sudo mv` и `chown`, что в §4.1.
|
||||
@@ -115,7 +115,7 @@ scp D:\Certs\kalinamall\privkey.pem papatramp@SAC_SERVER:/tmp/
|
||||
Если есть только **`wildcard.pfx`**:
|
||||
|
||||
```powershell
|
||||
scp D:\Certs\kalinamall\wildcard.pfx papatramp@SAC_SERVER:/tmp/
|
||||
scp D:\Certs\example\wildcard.pfx deploy@SAC_SERVER:/tmp/
|
||||
```
|
||||
|
||||
Распаковка на **сервере** (§4.3).
|
||||
@@ -128,19 +128,19 @@ scp D:\Certs\kalinamall\wildcard.pfx papatramp@SAC_SERVER:/tmp/
|
||||
mkdir -p /tmp/certs
|
||||
|
||||
# один файл
|
||||
scp user@certs-host.example.com:/path/to/wildcard_kalinamall_ru.pfx /tmp/certs/
|
||||
scp user@certs-host.example.com:/path/to/wildcard_example_com.pfx /tmp/certs/
|
||||
|
||||
# несколько файлов
|
||||
scp user@certs-host:/path/to/fullchain.pem user@certs-host:/path/to/privkey.pem /tmp/certs/
|
||||
|
||||
# нестандартный SSH-порт (у scp заглавная P)
|
||||
scp -P 2222 user@certs-host:/path/to/wildcard_kalinamall_ru.pfx /tmp/certs/
|
||||
scp -P 2222 user@certs-host:/path/to/wildcard_example_com.pfx /tmp/certs/
|
||||
```
|
||||
|
||||
С другого сервера **отдать** на SAC (push — команда на сервере-источнике):
|
||||
|
||||
```bash
|
||||
scp /path/to/wildcard_kalinamall_ru.pfx papatramp@<IP_SAC>:/tmp/certs/
|
||||
scp /path/to/wildcard_example_com.pfx deploy@<IP_SAC>:/tmp/certs/
|
||||
```
|
||||
|
||||
Нужен SSH-доступ и право **читать** файл на источнике.
|
||||
@@ -162,13 +162,13 @@ sudo mkdir -p /tmp/certs
|
||||
cd /tmp/certs
|
||||
# замените имя PFX; Enter Import Password: — пароль от PFX (не sudo)
|
||||
|
||||
sudo openssl pkcs12 -legacy -in wildcard_kalinamall_ru.pfx -nocerts -nodes \
|
||||
sudo openssl pkcs12 -legacy -in wildcard_example_com.pfx -nocerts -nodes \
|
||||
-out privkey.pem
|
||||
|
||||
sudo openssl pkcs12 -legacy -in wildcard_kalinamall_ru.pfx -clcerts -nokeys \
|
||||
sudo openssl pkcs12 -legacy -in wildcard_example_com.pfx -clcerts -nokeys \
|
||||
-out server.crt
|
||||
|
||||
sudo openssl pkcs12 -legacy -in wildcard_kalinamall_ru.pfx -cacerts -nokeys \
|
||||
sudo openssl pkcs12 -legacy -in wildcard_example_com.pfx -cacerts -nokeys \
|
||||
-out ca-chain.crt
|
||||
|
||||
# fullchain = сервер + промежуточные (порядок важен)
|
||||
@@ -183,20 +183,20 @@ sudo chmod 750 /etc/ssl/sac
|
||||
|
||||
# очистка временных файлов
|
||||
sudo shred -u server.crt ca-chain.crt privkey.pem 2>/dev/null
|
||||
sudo rm -f /tmp/certs/wildcard_kalinamall_ru.pfx
|
||||
sudo rm -f /tmp/certs/wildcard_example_com.pfx
|
||||
```
|
||||
|
||||
Если `-legacy` недостаточно:
|
||||
|
||||
```bash
|
||||
sudo openssl pkcs12 -provider legacy -provider default \
|
||||
-in wildcard_kalinamall_ru.pfx -nocerts -nodes -out privkey.pem
|
||||
-in wildcard_example_com.pfx -nocerts -nodes -out privkey.pem
|
||||
```
|
||||
|
||||
Проверить содержимое PFX:
|
||||
|
||||
```bash
|
||||
sudo openssl pkcs12 -legacy -in wildcard_kalinamall_ru.pfx -info -noout
|
||||
sudo openssl pkcs12 -legacy -in wildcard_example_com.pfx -info -noout
|
||||
```
|
||||
|
||||
Пароль PFX вводится интерактивно (`Enter Import Password`). **Не** коммитьте PFX и ключ в git.
|
||||
@@ -207,13 +207,13 @@ sudo openssl pkcs12 -legacy -in wildcard_kalinamall_ru.pfx -info -noout
|
||||
|
||||
Типичный набор от VeriSign / DigiCert:
|
||||
|
||||
- `star_kalinamall_ru.crt` — ваш wildcard
|
||||
- `star_example_com.crt` — ваш wildcard
|
||||
- `DigiCertCA.crt` / `intermediate.crt` — промежуточный
|
||||
- (иногда) `TrustedRoot.crt` — корневой **не** добавляйте в fullchain для nginx
|
||||
|
||||
```bash
|
||||
# на ПК или на сервере, в каталоге с исходниками:
|
||||
cat star_kalinamall_ru.crt intermediate.crt > fullchain.pem
|
||||
cat star_example_com.crt intermediate.crt > fullchain.pem
|
||||
# ключ переименовать:
|
||||
cp private.key privkey.pem
|
||||
```
|
||||
@@ -269,13 +269,13 @@ ssl_certificate_key /etc/ssl/sac/privkey.pem;
|
||||
sudo ln -sf /etc/nginx/sites-available/sac /etc/nginx/sites-enabled/sac
|
||||
sudo nginx -t
|
||||
sudo systemctl reload nginx
|
||||
curl -sS https://sac.kalinamall.ru/health | jq .
|
||||
curl -sS https://sac.example.com/health | jq .
|
||||
```
|
||||
|
||||
В приложении SAC (`/opt/security-alert-center/config/sac-api.env`):
|
||||
|
||||
```ini
|
||||
SAC_PUBLIC_URL=https://sac.kalinamall.ru
|
||||
SAC_PUBLIC_URL=https://sac.example.com
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
|
||||
```bash
|
||||
API_KEY="sac_…"
|
||||
BASE="https://sac.kalinamall.ru"
|
||||
BASE="https://sac.example.com"
|
||||
|
||||
# warning → должен уйти в TG при min=warning
|
||||
curl -sS -X POST "$BASE/api/v1/events" \
|
||||
|
||||
+4
-4
@@ -9,7 +9,7 @@
|
||||
| # | Задача | Статус |
|
||||
|---|--------|--------|
|
||||
| 0.1–0.3 | ТЗ, архитектура, схема, deployment | ✅ |
|
||||
| 0.5 | Репозиторий GitHub + зеркало kalinamall | ✅ |
|
||||
| 0.5 | Репозиторий GitHub + prod deployment | ✅ |
|
||||
| 0.6 | Multi-root workspace | ✅ |
|
||||
|
||||
---
|
||||
@@ -37,7 +37,7 @@
|
||||
|---|--------|--------|
|
||||
| 1B.1 | Scaffold backend FastAPI | ✅ |
|
||||
| 1B.2 | PostgreSQL + Alembic (hosts, events, api_keys) | ✅ |
|
||||
| 1B.3 | `POST /api/v1/events`, `GET /health` | ✅ prod `sac.kalinamall.ru` |
|
||||
| 1B.3 | `POST /api/v1/events`, `GET /health` | ✅ prod `sac.example.com` |
|
||||
| 1B.4 | systemd + nginx + TLS | ✅ |
|
||||
| 1B.5 | Валидация JSON Schema v1 | ✅ |
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
- [x] `d2-1` UI Problems: список, фильтры, `Ack/Resolve`, карточка с таймлайном
|
||||
- [x] `d2-2` Dashboard: top hosts/types, `open vs resolved 24h`, drill-down
|
||||
- [x] `d2-3` Ops: retention, health checks, runbook backup/restore/deploy
|
||||
- [x] `dod` DoD: нет дублей `event_id`, Problems e2e, 3 правила, UI MVP, docs, push в kalinamall
|
||||
- [x] `dod` DoD: нет дублей `event_id`, Problems e2e, 3 правила, UI MVP, docs, push в prod
|
||||
|
||||
### Отображение хостов (`display_name`, как `SERVER_DISPLAY_NAME` у ssh)
|
||||
|
||||
@@ -122,7 +122,7 @@
|
||||
- [x] `14:30–16:00` Dashboard: `open/resolved 24h` + drill-down
|
||||
- [x] `16:00–17:00` retention job (`events 30–90d`, `problems 180d+`)
|
||||
- [x] `17:00–18:00` health checks (DB/worker/heartbeat stale)
|
||||
- [x] `18:00–19:00` runbook + финальный push в kalinamall + freeze dev
|
||||
- [x] `18:00–19:00` runbook + финальный push в prod + freeze dev
|
||||
|
||||
### Неделя после freeze (только тестирование)
|
||||
|
||||
|
||||
@@ -6,26 +6,22 @@
|
||||
|
||||
## 1. Расположение репозиториев (пример)
|
||||
|
||||
| Имя в workspace | Путь (пример Windows) | Публичный remote |
|
||||
|-----------------|----------------------|------------------|
|
||||
| `ssh-monitor` | `D:\Soft\Git\ssh-monitor` | github.com/PTah/ssh-monitor |
|
||||
| `rdp-monitor` | `D:\Soft\Git\RDP-login-monitor` | github.com/PTah/RDP-login-monitor |
|
||||
| `security-alert-center` | `D:\Soft\Git\security-alert-center` | github.com/PTah/security-alert-center |
|
||||
|
||||
Пути подставьте свои. Закрытый инстанс **git.kalinamall.ru** — зеркало с теми же репозиториями; ссылки в документации на каждом хосте должны вести **на этот же хост** (см. ниже).
|
||||
| Имя в workspace | Публичный remote |
|
||||
|-----------------|------------------|
|
||||
| `ssh-monitor` | github.com/PTah/ssh-monitor |
|
||||
| `rdp-monitor` | github.com/PTah/RDP-login-monitor |
|
||||
| `security-alert-center` | github.com/PTah/security-alert-center |
|
||||
|
||||
---
|
||||
|
||||
## 2. Файл workspace
|
||||
|
||||
Создайте `security-monitors.code-workspace`:
|
||||
|
||||
```json
|
||||
{
|
||||
"folders": [
|
||||
{ "path": "D:/Soft/Git/ssh-monitor", "name": "ssh-monitor" },
|
||||
{ "path": "D:/Soft/Git/RDP-login-monitor", "name": "rdp-monitor" },
|
||||
{ "path": "D:/Soft/Git/security-alert-center", "name": "security-alert-center" }
|
||||
{ "path": "ssh-monitor", "name": "ssh-monitor" },
|
||||
{ "path": "RDP-login-monitor", "name": "rdp-monitor" },
|
||||
{ "path": "security-alert-center", "name": "security-alert-center" }
|
||||
],
|
||||
"settings": {
|
||||
"files.eol": "\n"
|
||||
@@ -33,61 +29,22 @@
|
||||
}
|
||||
```
|
||||
|
||||
Открыть: **File → Open Workspace from File**.
|
||||
|
||||
---
|
||||
|
||||
## 3. Порядок работ по фазам
|
||||
|
||||
| Фаза | Где править |
|
||||
|------|-------------|
|
||||
| 0 — ТЗ | `security-alert-center/docs/` |
|
||||
| 1 — MVP SAC | `security-alert-center/backend`, `frontend` |
|
||||
| 2 — UseSAC | `ssh-monitor`, `RDP-login-monitor` + мелкие правки SAC при необходимости |
|
||||
|
||||
---
|
||||
|
||||
## 4. Соглашения
|
||||
## 3. Соглашения
|
||||
|
||||
- Контракт событий: `security-alert-center/schemas/event-schema-v1.json` — **единственный источник истины**.
|
||||
- Изменение схемы → обновить ТЗ + версию `schema_version` + оба агента.
|
||||
- Коммиты в три репо **раздельные**; связь через теги/версии в README (см. [roadmap.md](roadmap.md)).
|
||||
|
||||
### 4.1. Git: зеркала и ссылки в документации
|
||||
|
||||
В **main** ссылки между репозиториями указывают на **GitHub** (`https://github.com/PTah/...`).
|
||||
|
||||
Перед push на другой хост (kalinamall, papatramp) используйте скрипт зеркалирования — он подменяет URL в docs, пушит и **не оставляет** чужие ссылки в локальной ветке:
|
||||
|
||||
```powershell
|
||||
# security-alert-center (аналогичные скрипты — в соседних репо при необходимости)
|
||||
.\scripts\Rewrite-GitHostUrls.ps1 kalinamall # только проверка diff
|
||||
.\scripts\Push-Mirror.ps1 kalinamall # commit → push kalinamall → reset main
|
||||
.\scripts\Push-Mirror.ps1 github # push на GitHub после обычного commit
|
||||
```
|
||||
|
||||
Обычный цикл разработки:
|
||||
- Коммиты в три репо **раздельные**; связь через теги/версии в README.
|
||||
|
||||
```bash
|
||||
git add …
|
||||
git commit -m "…"
|
||||
git push github main
|
||||
# при необходимости синхронизировать kalinamall:
|
||||
# PowerShell: .\scripts\Push-Mirror.ps1 kalinamall
|
||||
git clone https://github.com/PTah/security-alert-center.git
|
||||
git clone https://github.com/PTah/ssh-monitor.git
|
||||
git clone https://github.com/PTah/RDP-login-monitor.git
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. URL репозиториев (GitHub)
|
||||
|
||||
| Проект | URL |
|
||||
|--------|-----|
|
||||
| security-alert-center | `https://github.com/PTah/security-alert-center.git` |
|
||||
| ssh-monitor | `https://github.com/PTah/ssh-monitor.git` |
|
||||
| RDP-login-monitor | `https://github.com/PTah/RDP-login-monitor.git` |
|
||||
|
||||
---
|
||||
|
||||
## См. также
|
||||
|
||||
- [work-plan.md](work-plan.md) фаза 0.6
|
||||
- [work-plan.md](work-plan.md)
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
# Push main to a mirror remote with host-specific doc URLs, without leaving URL churn on main.
|
||||
# Usage: .\scripts\Push-Mirror.ps1 github|kalinamall|papatramp
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[ValidateSet('github', 'kalinamall', 'papatramp')]
|
||||
[string]$Target
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$Root = Split-Path -Parent $PSScriptRoot
|
||||
Set-Location $Root
|
||||
|
||||
$remote = switch ($Target) {
|
||||
'github' { 'github' }
|
||||
'kalinamall' { 'kalinamall' }
|
||||
'papatramp' { 'papatramp' }
|
||||
}
|
||||
|
||||
git remote get-url $remote 2>$null | Out-Null
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "remote not configured: $remote"
|
||||
}
|
||||
|
||||
if ((git status --porcelain -uno)) {
|
||||
throw 'working tree has uncommitted tracked changes; commit or stash first'
|
||||
}
|
||||
|
||||
$before = (git rev-parse HEAD).Trim()
|
||||
& "$PSScriptRoot\Rewrite-GitHostUrls.ps1" -Target $Target
|
||||
|
||||
if (-not (git status --porcelain)) {
|
||||
Write-Output "no URL changes for $Target; pushing as-is"
|
||||
git push $remote main
|
||||
exit 0
|
||||
}
|
||||
|
||||
git add -A
|
||||
git commit -m "chore(docs): sync repository URLs for ${Target} mirror"
|
||||
git push --force-with-lease $remote main
|
||||
git reset --hard $before
|
||||
Write-Output "pushed $remote with ${Target} URLs; local main reset to $before"
|
||||
@@ -1,57 +0,0 @@
|
||||
# Rewrite cross-repo URLs in tracked docs/config for the target Git host.
|
||||
# Usage: .\scripts\Rewrite-GitHostUrls.ps1 github|kalinamall|papatramp
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[ValidateSet('github', 'kalinamall', 'papatramp')]
|
||||
[string]$Target
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$Root = Split-Path -Parent $PSScriptRoot
|
||||
Set-Location $Root
|
||||
|
||||
switch ($Target) {
|
||||
'github' {
|
||||
$Base = 'https://github.com/PTah'
|
||||
$BlobSuffix = '/blob/main'
|
||||
}
|
||||
'kalinamall' {
|
||||
$Base = 'https://git.kalinamall.ru/PapaTramp'
|
||||
$BlobSuffix = '/src/branch/main'
|
||||
}
|
||||
'papatramp' {
|
||||
$Base = 'https://git.papatramp.ru/PTah'
|
||||
$BlobSuffix = '/src/branch/main'
|
||||
}
|
||||
}
|
||||
|
||||
$BaseHost = $Base -replace '^https://', ''
|
||||
|
||||
$patterns = @(
|
||||
@{ From = 'https://github.com/PTah/([^)/''"\s]+)/blob/main/'; To = "$Base/`$1$BlobSuffix/" }
|
||||
@{ From = 'https://git.kalinamall.ru/PapaTramp/([^)/''"\s]+)/src/branch/main/'; To = "$Base/`$1$BlobSuffix/" }
|
||||
@{ From = 'https://git.papatramp.ru/PTah/([^)/''"\s]+)/src/branch/main/'; To = "$Base/`$1$BlobSuffix/" }
|
||||
@{ From = 'https://github.com/PTah/'; To = "$Base/" }
|
||||
@{ From = 'https://git.kalinamall.ru/PapaTramp/'; To = "$Base/" }
|
||||
@{ From = 'https://git.papatramp.ru/PTah/'; To = "$Base/" }
|
||||
@{ From = 'github.com/PTah/'; To = "$BaseHost/" }
|
||||
@{ From = 'git.kalinamall.ru/PapaTramp/'; To = "$BaseHost/" }
|
||||
@{ From = 'git.papatramp.ru/PTah/'; To = "$BaseHost/" }
|
||||
)
|
||||
|
||||
$extensions = @('*.md', '*.json', '*.service', '*.example', '*.sh', '*.ps1', '*.yml', '*.yaml')
|
||||
$files = git ls-files $extensions 2>$null | Where-Object { $_ -and (Test-Path $_) }
|
||||
|
||||
foreach ($file in $files) {
|
||||
$content = [System.IO.File]::ReadAllText((Join-Path $Root $file))
|
||||
$updated = $content
|
||||
foreach ($p in $patterns) {
|
||||
$updated = [regex]::Replace($updated, $p.From, $p.To)
|
||||
}
|
||||
if ($updated -ne $content) {
|
||||
[System.IO.File]::WriteAllText((Join-Path $Root $file), $updated, [System.Text.UTF8Encoding]::new($false))
|
||||
Write-Output "updated: $file"
|
||||
}
|
||||
}
|
||||
|
||||
Write-Output "Rewrite-GitHostUrls: target=$Target base=$Base"
|
||||
@@ -1,43 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Push main to a mirror remote with host-specific doc URLs, without leaving URL churn on main.
|
||||
# Usage: push-mirror.sh github|kalinamall|papatramp
|
||||
set -euo pipefail
|
||||
|
||||
TARGET="${1:?usage: push-mirror.sh github|kalinamall|papatramp}"
|
||||
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
cd "$ROOT"
|
||||
|
||||
case "$TARGET" in
|
||||
github) REMOTE=github ;;
|
||||
kalinamall) REMOTE=kalinamall ;;
|
||||
papatramp) REMOTE=papatramp ;;
|
||||
*)
|
||||
echo "unknown target: $TARGET" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
if ! git remote get-url "$REMOTE" >/dev/null 2>&1; then
|
||||
echo "remote not configured: $REMOTE" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! git diff --quiet || ! git diff --cached --quiet; then
|
||||
echo "working tree not clean; commit or stash first" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
BEFORE="$(git rev-parse HEAD)"
|
||||
bash "$ROOT/scripts/rewrite-git-host-urls.sh" "$TARGET"
|
||||
|
||||
if git diff --quiet; then
|
||||
echo "no URL changes for $TARGET; pushing as-is"
|
||||
git push "$REMOTE" main
|
||||
exit 0
|
||||
fi
|
||||
|
||||
git add -A
|
||||
git commit -m "chore(docs): sync repository URLs for ${TARGET} mirror"
|
||||
git push "$REMOTE" main
|
||||
git reset --hard "$BEFORE"
|
||||
echo "pushed $REMOTE with ${TARGET} URLs; local main reset to $BEFORE"
|
||||
@@ -1,67 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Rewrite cross-repo URLs in tracked docs/config for the target Git host.
|
||||
# Usage: rewrite-git-host-urls.sh github|kalinamall|papatramp
|
||||
set -euo pipefail
|
||||
|
||||
TARGET="${1:?usage: rewrite-git-host-urls.sh github|kalinamall|papatramp}"
|
||||
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
cd "$ROOT"
|
||||
|
||||
case "$TARGET" in
|
||||
github)
|
||||
BASE='https://github.com/PTah'
|
||||
BLOB_SUFFIX='/blob/main'
|
||||
;;
|
||||
kalinamall)
|
||||
BASE='https://git.kalinamall.ru/PapaTramp'
|
||||
BLOB_SUFFIX='/src/branch/main'
|
||||
;;
|
||||
papatramp)
|
||||
BASE='https://git.papatramp.ru/PTah'
|
||||
BLOB_SUFFIX='/src/branch/main'
|
||||
;;
|
||||
*)
|
||||
echo "unknown target: $TARGET" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# Order matters: longer patterns first.
|
||||
PATTERNS=(
|
||||
's|https://github.com/PTah/\([^)/"'"'"' ]*\)/blob/main/|'"${BASE}"'/\1'"${BLOB_SUFFIX}"'/|g'
|
||||
's|https://git.kalinamall.ru/PapaTramp/\([^)/"'"'"' ]*\)/src/branch/main/|'"${BASE}"'/\1'"${BLOB_SUFFIX}"'/|g'
|
||||
's|https://git.papatramp.ru/PTah/\([^)/"'"'"' ]*\)/src/branch/main/|'"${BASE}"'/\1'"${BLOB_SUFFIX}"'/|g'
|
||||
's|https://github.com/PTah/|'"${BASE}"'/|g'
|
||||
's|https://git.kalinamall.ru/PapaTramp/|'"${BASE}"'/|g'
|
||||
's|https://git.papatramp.ru/PTah/|'"${BASE}"'/|g'
|
||||
's|github.com/PTah/|'"${BASE#https://}"'/|g'
|
||||
's|git.kalinamall.ru/PapaTramp/|'"${BASE#https://}"'/|g'
|
||||
's|git.papatramp.ru/PTah/|'"${BASE#https://}"'/|g'
|
||||
)
|
||||
|
||||
FILES=()
|
||||
while IFS= read -r -d '' f; do
|
||||
FILES+=("$f")
|
||||
done < <(git ls-files '*.md' '*.json' '*.service' '*.example' '*.sh' '*.ps1' '*.yml' '*.yaml' 2>/dev/null | tr '\n' '\0')
|
||||
|
||||
if [ "${#FILES[@]}" -eq 0 ]; then
|
||||
echo "no files to rewrite" >&2
|
||||
exit 0
|
||||
fi
|
||||
|
||||
for f in "${FILES[@]}"; do
|
||||
[ -f "$f" ] || continue
|
||||
tmp="$(mktemp)"
|
||||
cp "$f" "$tmp"
|
||||
for pat in "${PATTERNS[@]}"; do
|
||||
sed -i "$pat" "$tmp" 2>/dev/null || sed -i '' "$pat" "$tmp"
|
||||
done
|
||||
if ! cmp -s "$f" "$tmp"; then
|
||||
mv "$tmp" "$f"
|
||||
echo "updated: $f"
|
||||
else
|
||||
rm -f "$tmp"
|
||||
fi
|
||||
done
|
||||
|
||||
echo "rewrite-git-host-urls: target=$TARGET base=$BASE"
|
||||
Reference in New Issue
Block a user