From 6712d050a5593deb193f59e980a2bd832470b77a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A2=D0=B8=D0=BC=D1=83=D1=80=20=D0=93=D0=BE=D1=80=D0=BE?= =?UTF-8?q?=D0=B4=D0=B5=D1=86=D0=BA=D0=B8=D0=B9?= Date: Fri, 25 Sep 2026 10:08:08 +0500 Subject: [PATCH] =?UTF-8?q?feat:=20=D0=B2=D0=B5=D0=B1=D1=85=D1=83=D0=BA-?= =?UTF-8?q?=D0=BF=D0=BE=D0=B4=D0=BF=D0=B8=D1=81=D0=BA=D0=B0=20=D0=BD=D0=BE?= =?UTF-8?q?=D0=B2=D1=8B=D1=85=20=D1=81=D0=BF=D0=B8=D1=81=D0=BA=D0=BE=D0=B2?= =?UTF-8?q?=20=D0=B2=20=D0=BF=D1=80=D0=BE=D0=B2=D0=B8=D0=B7=D0=B8=D0=BE?= =?UTF-8?q?=D0=BD=D0=B5=D1=80=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- provisioning/Provision-Lists.ps1 | 61 +++++++++++++++++++++++++++++++- provisioning/README.md | 17 ++++++++- 2 files changed, 76 insertions(+), 2 deletions(-) diff --git a/provisioning/Provision-Lists.ps1 b/provisioning/Provision-Lists.ps1 index 103a3f2..eb91e87 100644 --- a/provisioning/Provision-Lists.ps1 +++ b/provisioning/Provision-Lists.ps1 @@ -61,6 +61,15 @@ .PARAMETER SkipRename Не выполнять миграцию имён существующих списков (только создание/колонки). +.PARAMETER WebhookToken + Bearer-токен Microsoft (JWT, без слова «Bearer») для api.tnsu.kz. Если задан — на + КАЖДЫЙ НОВЫЙ список (и «— данные», и «— согласование») оформляется вебхук-подписка + через POST https://api.tnsu.kz/Webhook/Subscribe: бэкенд подписывается на изменения + в списке, уведомления идут на https://api.tnsu.kz/Webhook/SharePoint. Уже + существующие списки не подписываются. Не задан — списки создаются как раньше, по + каждому новому выводится предупреждение (подписать вручную). Токен живёт ~1 час — + берите свежий перед запуском. В коде и репозитории токен не хранится. + .EXAMPLE ./Provision-Lists.ps1 -SiteUrl "https://tnsukz.sharepoint.com/sites/requests" ` -ClientId "7459c965-5ed4-4673-8140-0ab8faf9db64" -Tenant "tnsukz.onmicrosoft.com" ` @@ -70,6 +79,11 @@ # Следующая пара списков под архивацию, без миграции имён. ./Provision-Lists.ps1 -SiteUrl "..." -ClientId "..." -Tenant "..." -Thumbprint "..." ` -ListNumber 2 + +.EXAMPLE + # С вебхук-подпиской новых списков. + ./Provision-Lists.ps1 -SiteUrl "..." -ClientId "..." -Tenant "..." -Thumbprint "..." ` + -WebhookToken "eyJ0eXAiOiJKV1Qi..." #> param( [Parameter(Mandatory = $true)][string]$SiteUrl, @@ -78,11 +92,16 @@ param( [Parameter(Mandatory = $true)][string]$Thumbprint, [string]$FormsRoot = "$PSScriptRoot/../src/webparts/requestPortal/forms", [ValidateRange(1, 999)][int]$ListNumber = 1, - [switch]$SkipRename + [switch]$SkipRename, + [string]$WebhookToken ) $ErrorActionPreference = 'Stop' +# Вебхук-подписка новых списков (см. .PARAMETER WebhookToken). +$WEBHOOK_SUBSCRIBE_URL = 'https://api.tnsu.kz/Webhook/Subscribe' +$WEBHOOK_NOTIFICATION_URL = 'https://api.tnsu.kz/Webhook/SharePoint' + # Экранирование значений, попадающих в XML полей (Lookup/Number с точностью). function ConvertTo-XmlSafe { param([string]$Value) @@ -306,6 +325,34 @@ function Add-ColumnToList { } } +# Оформляет вебхук-подписку на список через бэкенд api.tnsu.kz. Ошибка подписки +# провизионинг НЕ останавливает: список уже создан, подписать можно и вручную. +function Register-ListWebhook { + param([string]$ListTitle) + + if (-not $WebhookToken) { + Write-Warning " [!] вебхук: «$ListTitle» не подписан — не передан -WebhookToken, подпишите список вручную" + return + } + + # Токен могли вставить вместе со словом «Bearer» — убираем, иначе заголовок сломается. + $token = ($WebhookToken -replace '^\s*Bearer\s+', '').Trim() + $query = @( + "listName=$([uri]::EscapeDataString($ListTitle))", + "url=$([uri]::EscapeDataString($SiteUrl.TrimEnd('/')))", + "notificationUrl=$([uri]::EscapeDataString($WEBHOOK_NOTIFICATION_URL))" + ) -join '&' + + try { + Invoke-RestMethod -Method Post -Uri "$($WEBHOOK_SUBSCRIBE_URL)?$query" ` + -Headers @{ Authorization = "Bearer $token"; Accept = '*/*' } | Out-Null + Write-Host " [+] вебхук: подписан «$ListTitle»" -ForegroundColor Green + } + catch { + Write-Warning " [!] вебхук: «$ListTitle» не подписан — $($_.Exception.Message) (подпишите список вручную)" + } +} + Write-Host "Подключение к $SiteUrl (app-only) ..." -ForegroundColor Cyan Connect-PnPOnline -Url $SiteUrl -ClientId $ClientId -Tenant $Tenant -Thumbprint $Thumbprint @@ -357,12 +404,15 @@ if ($ListNumber -eq 1 -and -not $SkipRename) { # сначала все списки, потом все колонки: к моменту разрешения любого Lookup'а # список-источник уже существует. Write-Host "`n=== Списки ===" -ForegroundColor Cyan +# Созданные В ЭТОМ запуске — только их подписываем на вебхук (см. конец скрипта). +$createdLists = [System.Collections.Generic.List[string]]::new() foreach ($manifest in $manifests) { $def = Get-Content -Path $manifest.FullName -Raw -Encoding UTF8 | ConvertFrom-Json $listTitle = Get-NumberedListTitle -Title $def.dataListTitle -Number $ListNumber if (-not (Get-PnPList -Identity $listTitle -ErrorAction SilentlyContinue)) { New-PnPList -Title $listTitle -Template GenericList -EnableVersioning | Out-Null + $createdLists.Add($listTitle) Write-Host " [+] создан список «$listTitle»" -ForegroundColor Green } else { @@ -393,5 +443,14 @@ foreach ($manifest in $manifests) { } } +# Проход 3: вебхуки — ПОСЛЕ колонок, чтобы бэкенд подписывался на уже готовый список. +# Только новые списки: существующие подписаны раньше (или вручную). +if ($createdLists.Count -gt 0) { + Write-Host "`n=== Вебхуки новых списков ===" -ForegroundColor Cyan + foreach ($listTitle in $createdLists) { + Register-ListWebhook -ListTitle $listTitle + } +} + Write-Host "`nГотово." -ForegroundColor Cyan Disconnect-PnPOnline diff --git a/provisioning/README.md b/provisioning/README.md index 52d1048..e5dfecf 100644 --- a/provisioning/README.md +++ b/provisioning/README.md @@ -21,7 +21,7 @@ | `Text` | однострочный текст | — | | `Note` | многострочный текст| — | | `Number` | число | `decimals` (знаков после запятой) | -| `Choice` | выбор из вариантов | `choices` (массив) | +| `Choice` | выбор из вариантов | `choices` (массив), `allowMultiple` (множественный выбор — колонка MultiChoice; тип существующей колонки скрипт не меняет) | | `DateTime` | дата/время | — | | `Boolean` | да/нет | — | | `User` | пользователь | — | @@ -72,6 +72,21 @@ cd provisioning Лог: `[+]` — создано, `[=]` — уже существовало, `[~]` — переименовано, `[!]` — предупреждение. +### Вебхук-подписка новых списков + +Если передать `-WebhookToken ""` (bearer-токен Microsoft для api.tnsu.kz, без +слова «Bearer»), то на каждый список, СОЗДАННЫЙ в этом запуске («— данные» и +«— согласование»), скрипт в конце отправит: + +``` +POST https://api.tnsu.kz/Webhook/Subscribe?listName=<список>&url=<сайт>¬ificationUrl=https://api.tnsu.kz/Webhook/SharePoint +``` + +Бэкенд подписывается на изменения в списке, уведомления приходят на +`/Webhook/SharePoint`. Существующие списки не подписываются. Без токена или при +ошибке запроса — предупреждение `[!] вебхук: … не подписан` (подписать вручную), +провизионинг при этом не останавливается. Токен живёт ~1 час — берите свежий. + ## Важно про имена колонок В манифесте у колонки два имени: