Учёт DEPO в мастере: поле базы, логин и пароль bj на экране, скрипт показывается на странице

База DEPO — видимое поле (необязательно: пусто — найдётся по учётным таблицам), порт заполнен
по умолчанию. «Отдать скрипт администратору»: логин и пароль bj видны и меняются, «Показать
скрипт» выводит его на странице с кнопкой «Скопировать». В начале скрипта — блок «Проверьте
перед запуском»: @db и @pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014FsEKyJrYF69Cj9ZXzFLRi
This commit is contained in:
zuevav
2026-10-06 13:35:01 +03:00
parent 46cbebaff7
commit be83b86747
6 changed files with 155 additions and 35 deletions
+25 -12
View File
@@ -57,19 +57,30 @@ func depoSetupScript(f FancyMSSQLSettings, db, login, pass string, agentJob bool
return depoSetupScriptPG(f, db, login, pass)
}
var b strings.Builder
job := "обработку вызывает сам bj"
job := "вызывает сам bj"
if agentJob {
job = "задание SQL Server Agent «" + depoJobName + "» для обработки обмена"
job = "задание SQL Server Agent «" + depoJobName + "»"
}
fmt.Fprintf(&b, `-- Bridge&Join: подготовка базы учёта DEPO для bj. Microsoft SQL Server.
-- Один скрипт: находит базу DEPO на сервере, создаёт логин bj (%[1]s), таблицы обмена FTV_M2M*, выдаёт
-- права (чтение учёта, запись только в таблицы обмена) и %[2]s.
-- Учётные данные DEPO скрипт не меняет. Запускать в SSMS или sqlcmd под логином с правами sysadmin.
-- Можно запускать повторно: уже сделанное не повторяется. Скрипт содержит пароль логина bj — после
-- выполнения файл удалите.
SET NOCOUNT ON;
DECLARE @db sysname = N'%[3]s'; -- база DEPO; пусто — найти на сервере по таблицам OD_FACES и OD_RESTS
-- ===== Проверьте перед запуском ==========================================
-- База DEPO: пусто — скрипт найдёт её сам по таблицам OD_FACES и OD_RESTS.
DECLARE @db sysname = N'%[3]s';
-- Пароль логина bj (%[1]s). Меняете — впишите тот же пароль в bj, шаг «Учёт DEPO».
DECLARE @pass nvarchar(128) = N'%[4]s';
-- =========================================================================
-- Что делает: находит базу DEPO, создаёт логин bj и таблицы обмена FTV_M2M*, выдаёт права
-- (чтение учёта, запись только в таблицы обмена). Обработка обмена: %[2]s.
-- Учётные данные DEPO не меняет. Запускать в SSMS или sqlcmd под логином с правами sysadmin.
-- Можно запускать повторно. В скрипте пароль логина bj — после выполнения файл удалите.
IF @pass = N''
BEGIN
RAISERROR(N'Впишите пароль логина bj в @pass в начале скрипта и запустите снова.', 16, 1);
RETURN;
END
DECLARE @found int;
IF @db = N''
BEGIN
@@ -89,12 +100,12 @@ DECLARE @in nvarchar(300) = N'USE ' + QUOTENAME(@db) + N'; ';
-- 1. Логин bj
IF SUSER_ID(N'%[1]s') IS NULL
EXEC(N'CREATE LOGIN [%[1]s] WITH PASSWORD = N''%[4]s'', CHECK_POLICY = ON, DEFAULT_DATABASE = ' + QUOTENAME(@db))
EXEC(N'CREATE LOGIN [%[1]s] WITH PASSWORD = N''' + REPLACE(@pass, N'''', N'''''') + N''', CHECK_POLICY = ON, DEFAULT_DATABASE = ' + QUOTENAME(@db))
ELSE
EXEC(N'ALTER LOGIN [%[1]s] WITH PASSWORD = N''%[4]s''');
EXEC(N'ALTER LOGIN [%[1]s] WITH PASSWORD = N''' + REPLACE(@pass, N'''', N'''''') + N'''');
-- 2. Таблицы обмена FTV_M2M* (только недостающие таблицы, колонки и индексы — данные не меняются)
`, login, job, sqlStr(db), sqlStr(sqlStr(pass)))
`, login, job, sqlStr(db), sqlStr(pass))
for _, s := range depoTablesDDL(false) {
fmt.Fprintf(&b, "EXEC(@in + N'%s');\n", sqlStr(s))
}
@@ -137,6 +148,8 @@ func depoSetupScriptPG(f FancyMSSQLSettings, db, login, pass string) string {
-- Создаёт роль bj (%[2]s), таблицы обмена FTV_M2M* и права (чтение учёта, запись только в таблицы обмена).
-- Обработку обмена вызывает сам bj. Учётные данные скрипт не меняет. Можно запускать повторно.
-- Скрипт содержит пароль роли bj — после выполнения файл удалите.
-- Проверьте перед запуском: база DEPO — "%[1]s", роль bj — "%[2]s", её пароль — в строках PASSWORD ниже
-- (меняете — впишите тот же пароль в bj, шаг «Учёт DEPO»).
\set ON_ERROR_STOP on
DO $$ BEGIN
+1 -1
View File
@@ -83,7 +83,7 @@ func letterFor(s Settings, key string) (Letter, bool) {
}
return Letter{Key: key, Title: "Письмо администратору базы DEPO", To: "Администратор СУБД учёта DEPO",
Subject: "Bridge&Join: подготовка базы DEPO на сервере " + orBracket(f.Server, "сервер DEPO"),
Body: fmt.Sprintf("Коллеги, добрый день!\n\nДля подключения системы Bridge&Join к учёту DEPO нужно на сервере %s выполнить один скрипт %s %s (файл прилагается — он скачивается в мастере настройки bj). Скрипт:\n— сам находит базу DEPO на сервере (по учётным таблицам);\n— создаёт логин bj: права — чтение учётных таблиц DEPO и запись только в таблицы обмена FTV_M2M*;\n— создаёт таблицы обмена FTV_M2M*; учётные данные он не меняет;\n%s\nСкрипт можно запускать повторно. В нём пароль логина bj — после выполнения файл удалите.\n\nТакже нужно разрешить подключение к серверу базы с сервера Bridge&Join (адрес %s). Процедуры обработки обмена FTV_M2M_* поставляются отдельно.%s",
Body: fmt.Sprintf("Коллеги, добрый день!\n\nДля подключения системы Bridge&Join к учёту DEPO нужно на сервере %s выполнить один скрипт %s %s (файл прилагается — он скачивается в мастере настройки bj). Скрипт:\n— в начале — имя базы DEPO (пусто — скрипт найдёт её сам по учётным таблицам) и пароль логина bj: их можно поправить;\n— создаёт логин bj: права — чтение учётных таблиц DEPO и запись только в таблицы обмена FTV_M2M*;\n— создаёт таблицы обмена FTV_M2M*; учётные данные он не меняет;\n%s\nСкрипт можно запускать повторно. В нём пароль логина bj — после выполнения файл удалите.\n\nТакже нужно разрешить подключение к серверу базы с сервера Bridge&Join (адрес %s). Процедуры обработки обмена FTV_M2M_* поставляются отдельно.%s",
orBracket(f.Server, "сервер DEPO"), depoSetupFileName(f), run, what, orBracket(ip, "IP-адрес сервера bj"), sign)}, true
}
return Letter{}, false
+39 -1
View File
@@ -351,7 +351,7 @@ func TestFromLocalBundle(t *testing.T) {
func TestDepoSetupScript(t *testing.T) {
f := FancyMSSQLSettings{}
s := depoSetupScript(f, "", "bj_user", "Pass1word", true)
for _, want := range []string{"DECLARE @db sysname = N'';", "OD_FACES", "CREATE LOGIN [bj_user] WITH PASSWORD = N''Pass1word''", "FTV_M2M_EVENTS", "db_datareader", "sp_add_job @job_name = N'bj2depo'", "@database_name = @db", "SQLAgentReaderRole"} {
for _, want := range []string{"DECLARE @db sysname = N'';", "DECLARE @pass nvarchar(128) = N'Pass1word';", "OD_FACES", "CREATE LOGIN [bj_user] WITH PASSWORD = N''' + REPLACE(@pass, N'''', N'''''')", "FTV_M2M_EVENTS", "db_datareader", "sp_add_job @job_name = N'bj2depo'", "@database_name = @db", "SQLAgentReaderRole"} {
if !strings.Contains(s, want) {
t.Fatalf("в скрипте нет %q", want)
}
@@ -443,6 +443,7 @@ func TestDumpOnboardingPages(t *testing.T) {
t.Fatal(err)
}
_ = os.WriteFile(filepath.Join(dir, "prepare.html"), []byte(b.String()), 0o644)
_ = os.WriteFile(filepath.Join(dir, "depo-script.sql"), []byte(depoSetupScript(FancyMSSQLSettings{}, "", "bj_user", "Xk7pQ2mN9vR4tL8wZ3hB6cJd", true)), 0o644)
}
// Первая страница (администратор) — для снимка в TestDumpOnboardingPages.
@@ -457,3 +458,40 @@ func TestDumpFirstRunPage(t *testing.T) {
}
_ = os.WriteFile(filepath.Join(dir, "first-run.html"), []byte(b.String()), 0o644)
}
// «Отдать скрипт администратору»: скрипт показывается на странице с базой и паролем с экрана, пароль
// запоминается (с ним bj потом проверяет подключение); короткий пароль не принимается.
func TestDepoScriptView(t *testing.T) {
priv := testLicenseKey(t)
rc := testRC(t)
now := time.Now().UTC()
tok := signTestLicense(t, priv, &license.License{Schema: 1, ID: "lic-1", Tenant: "Тест", Product: "bj-server", Plan: license.PlanPro,
IssuedAt: now, ExpiresAt: now.Add(30 * 24 * time.Hour), ContractUntil: now.AddDate(1, 0, 0), Features: []string{"updates", "quik-mnp"}})
if err := rc.SaveLicense(LicenseSettings{Key: tok}); err != nil {
t.Fatal(err)
}
old := globalRC
globalRC = rc
defer func() { globalRC = old }()
h := &setupHandlers{rc: rc}
post := func(form url.Values) *httptest.ResponseRecorder {
r := httptest.NewRequest(http.MethodPost, "/admin/setup/wizard-depo/script.sql", strings.NewReader(form.Encode()))
r.Header.Set("Content-Type", "application/x-www-form-urlencoded")
w := httptest.NewRecorder()
h.wizardDepoScript(w, r)
return w
}
form := url.Values{"server": {"10.0.0.5"}, "port": {"1433"}, "db_type": {"mssql"}, "depo_database": {"DEPO_X"},
"mode": {"script"}, "bj_user": {"bj_user"}, "bj_password": {"Secret123x"}, "depo_job": {"agent"}, "view": {"1"}}
w := post(form)
if w.Code != http.StatusOK || !strings.Contains(w.Body.String(), "DECLARE @db sysname = N'DEPO_X';") || !strings.Contains(w.Body.String(), "N'Secret123x';") {
t.Fatalf("показ скрипта: %d %s", w.Code, w.Body.String()[:min(300, w.Body.Len())])
}
if f := rc.Snapshot().Fancy; f.User != "bj_user" || f.Password != "Secret123x" || f.DepoTablesMode != "script" {
t.Fatalf("логин и пароль bj не запомнились: %+v", f.User)
}
form.Set("bj_password", "short")
if w := post(form); w.Code != http.StatusBadRequest || !strings.Contains(w.Body.String(), "8 знаков") {
t.Fatalf("короткий пароль: %d %s", w.Code, w.Body.String())
}
}
+11
View File
@@ -175,6 +175,9 @@ type WizardData struct {
Fancy *SetupData // шаг 3: общий блок «Обмен Fancy» (тот же, что в настройках)
Sub string // экран шага 3 (учёт DEPO): intro|conn|tables|job|done
DepoCands []string // шаг 3: найдено несколько баз DEPO — выбор
DepoUser string // шаг 3, «скрипт администратору»: логин bj, который создаст скрипт
DepoPass string // и его пароль (сохранённый или придуманный сейчас — сохраняется со скриптом)
DepoMode string // выбранный способ подготовки базы: auto|script|existing
ValidataVer string // версия установленной «Валидаты Клиент L» (пусто — не установлена)
CryptoSvc string // состояние службы bj-crypto (systemctl is-active)
PGSummary string // куда подключена база bj, без пароля
@@ -322,6 +325,14 @@ func (h *setupHandlers) renderWizard(w http.ResponseWriter, r *http.Request) {
if step == 3 {
d.Sub = depoWizardSub(r.URL.Query().Get("sub"))
d.DepoCands = depoCands(r.URL.Query().Get("cands"))
d.DepoUser, d.DepoPass = depoSetupLogin(s.Fancy), s.Fancy.Password
if s.Fancy.User != d.DepoUser || d.DepoPass == "" {
d.DepoPass = depoGenPassword()
}
d.DepoMode = "auto"
if s.Fancy.DepoTablesMode == "script" {
d.DepoMode = "script"
}
if r.URL.Query().Get("sub") == "" && d.Done.Depo {
d.Sub = "done"
}
@@ -132,27 +132,36 @@
{{else}}
{{$sub := .Sub}}
{{if eq $sub "setup"}}
<p style="margin:2px 0 0">bj сверяет каждый перевод с учётом DEPO и сам создаёт в нём поручения. Нужен только адрес сервера — базу DEPO bj найдёт сам.</p>
<p>bj сверяет каждый перевод с учётом DEPO и сам создаёт в нём поручения. Укажите сервер DEPO; имя базы можно не писать — bj найдёт её по учётным таблицам.</p>
{{$mode := .Settings.Fancy.DepoJobMode}}
<form method="post" action="/admin/setup/wizard-depo/setup" id="wiz-depo" style="display:grid;gap:12px">
<div style="display:grid;grid-template-columns:2.2fr .8fr 1.1fr;gap:8px">
<label style="margin:0">Сервер DEPO<input type="text" name="server" value="{{.Settings.Fancy.Server}}" placeholder="адрес или имя\экземпляр" required autofocus style="width:100%;margin-top:4px"></label>
<label style="margin:0">Порт<input type="text" name="port" value="{{if .Settings.Fancy.Port}}{{.Settings.Fancy.Port}}{{end}}" placeholder="1433" style="width:100%;margin-top:4px"></label>
<label style="margin:0">Порт<input type="text" name="port" value="{{if .Settings.Fancy.Port}}{{.Settings.Fancy.Port}}{{else if eq .Settings.Fancy.DBType "postgres"}}5432{{else}}1433{{end}}" inputmode="numeric" style="width:100%;margin-top:4px"></label>
<label style="margin:0">СУБД<select name="db_type" onchange="wizDepo()" style="width:100%;margin-top:4px"><option value="mssql" {{if ne .Settings.Fancy.DBType "postgres"}}selected{{end}}>SQL Server</option><option value="postgres" {{if eq .Settings.Fancy.DBType "postgres"}}selected{{end}}>PostgreSQL</option></select></label>
</div>
{{if .DepoCands}}<label style="margin:0">На сервере несколько баз DEPO — выберите нужную<select name="depo_database" style="width:100%;margin-top:4px">{{range .DepoCands}}<option>{{.}}</option>{{end}}</select></label>{{end}}
<div id="wiz-depo-pgdb" style="display:none"><label style="margin:0">База DEPO <span class="muted" style="font-weight:400">(для PostgreSQL указывается вручную)</span><input type="text" name="depo_database" value="{{.Settings.Fancy.DepoDatabase}}" pattern="[A-Za-z][A-Za-z0-9_]*" style="width:100%;margin-top:4px"></label></div>
{{if .DepoCands}}<label style="margin:0">База DEPO <span class="muted" style="font-weight:400">— на сервере их несколько, выберите нужную</span><select name="depo_database" style="width:100%;max-width:420px;display:block;margin-top:4px">{{range .DepoCands}}<option>{{.}}</option>{{end}}</select></label>
{{else}}<label style="margin:0">База DEPO <span class="muted" style="font-weight:400" id="wiz-depo-dbhint">— необязательно</span><input type="text" name="depo_database" value="{{.Settings.Fancy.DepoDatabase}}" placeholder="bj найдёт сам" pattern="[A-Za-z][A-Za-z0-9_]*" title="латинские буквы, цифры и _, начинается с буквы" style="width:100%;max-width:420px;display:block;margin-top:4px"></label>{{end}}
<div style="border-top:1px solid var(--border);padding-top:10px"><div style="font-weight:600;margin-bottom:6px">Как подготовить базу</div>
<label class="wiz-opt"><input type="radio" name="mode" value="auto" checked onchange="wizDepo()"> <span><b>bj сделает всё сам</b> <span class="muted">— нужен логин администратора SQL на один раз, bj его не сохраняет</span></span></label>
<label class="wiz-opt"><input type="radio" name="mode" value="auto" {{if ne .DepoMode "script"}}checked{{end}} onchange="wizDepo()"> <span><b>bj сделает всё сам</b> <span class="muted">— нужен логин администратора SQL на один раз, bj его не сохраняет</span></span></label>
<div id="wiz-depo-auto" style="display:grid;grid-template-columns:1fr 1fr;gap:8px;margin:0 0 8px 26px">
<input type="text" name="admin_user" autocomplete="off" placeholder="логин администратора, например sa" style="width:100%">
<input type="password" name="admin_password" autocomplete="new-password" placeholder="пароль" style="width:100%">
</div>
<label class="wiz-opt"><input type="radio" name="mode" value="script" onchange="wizDepo()"> <span><b>Отдать скрипт администратору</b> <span class="muted">— если логин администратора не дают: один файл сделает то же самое</span></span></label>
<label class="wiz-opt"><input type="radio" name="mode" value="script" {{if eq .DepoMode "script"}}checked{{end}} onchange="wizDepo()"> <span><b>Отдать скрипт администратору</b> <span class="muted">— если логин администратора не дают: один скрипт сделает то же самое</span></span></label>
<div id="wiz-depo-script" style="display:none;margin:0 0 8px 26px">
<button type="submit" class="btn" formaction="/admin/setup/wizard-depo/script.sql" style="background:var(--card);color:var(--text);border:1px solid var(--border-strong,#c9cdd8)">Скачать скрипт</button>
<span class="muted" style="font-size:12.5px">Пароль для bj уже вписан в скрипт. Администратор выполняет файл в SSMS — потом нажмите «Проверить».</span>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:8px">
<label style="margin:0">Логин bj<input type="text" name="bj_user" value="{{.DepoUser}}" autocomplete="off" spellcheck="false" style="width:100%;margin-top:4px"></label>
<label style="margin:0">Пароль bj<input type="text" name="bj_password" value="{{.DepoPass}}" autocomplete="off" spellcheck="false" style="width:100%;margin-top:4px;font-family:var(--mono)"></label>
</div>
<p class="muted" style="font-size:12.5px;margin:6px 0 10px">Скрипт создаст этот логин на сервере DEPO — bj будет входить с ним. Пароль bj придумал сам, можно вписать свой. База DEPO и пароль стоят в начале скрипта — администратор может их поправить.</p>
<div style="display:flex;gap:8px;flex-wrap:wrap">
<button type="button" class="btn" id="wiz-depo-show">Показать скрипт</button>
<button type="submit" class="btn" formaction="/admin/setup/wizard-depo/script.sql" style="background:var(--card);color:var(--text);border:1px solid var(--border-strong,#c9cdd8)">Скачать файлом</button>
</div>
<div id="wiz-depo-sqlbox" hidden style="margin-top:10px"><pre class="copyable" id="wiz-depo-sql" style="max-height:380px;font-size:12px"></pre></div>
<p class="muted" style="font-size:12.5px;margin:8px 0 0">Администратор выполняет скрипт в SSMS под логином с правами sysadmin — потом нажмите «Проверить подключение».</p>
{{template "letterCard" (letter "dba")}}
</div>
<label class="wiz-opt"><input type="radio" name="mode" value="existing" onchange="wizDepo()"> <span><b>Логин для bj уже есть</b> <span class="muted">— база подготовлена раньше, только проверить</span></span></label>
@@ -165,21 +174,28 @@
<label class="wiz-opt"><input type="radio" name="depo_job" value="agent" {{if ne $mode "bj"}}checked{{end}}> <span><b>SQL Server Agent</b> <span class="muted">— рекомендуется: работает, даже когда bj выключен</span></span></label>
<label class="wiz-opt"><input type="radio" name="depo_job" value="bj" {{if eq $mode "bj"}}checked{{end}}> <span><b>Сервер bj</b> <span class="muted">— если Agent нет (например, SQL Server Express)</span></span></label>
</div>
{{if not .DepoCands}}<details style="font-size:13px"><summary style="cursor:pointer;color:var(--accent)">Указать базу DEPO вручную (обычно не нужно)</summary>
<input type="text" name="depo_database" id="wiz-depo-dbman" value="" placeholder="имя базы DEPO на сервере" pattern="[A-Za-z][A-Za-z0-9_]*" style="width:100%;margin-top:6px;max-width:320px"></details>{{end}}
<div><button type="submit" class="btn" id="wiz-depo-btn">Подготовить и проверить</button></div>
</form>
<script>function wizDepo(){var f=document.getElementById('wiz-depo'),pg=f.db_type.value==='postgres',m=f.mode.value;
document.getElementById('wiz-depo-pgdb').style.display=pg?'block':'none';
var man=document.getElementById('wiz-depo-dbman'); if(man) man.disabled=pg;
f.querySelector('#wiz-depo-pgdb input').disabled=!pg;
var db=f.querySelector('input[name=depo_database]'), hint=document.getElementById('wiz-depo-dbhint');
if(db){ db.required=pg; db.placeholder=pg?'имя базы DEPO':'bj найдёт сам'; if(hint) hint.textContent=pg?'— для PostgreSQL обязательно':'— необязательно'; }
if(pg&&f.port.value==='1433') f.port.value='5432'; else if(!pg&&f.port.value==='5432') f.port.value='1433';
document.getElementById('wiz-depo-job').style.display=pg?'none':'block';
if(!f.port.value) f.port.placeholder=pg?'5432':'1433';
document.getElementById('wiz-depo-auto').style.display=m==='auto'?'grid':'none';
document.getElementById('wiz-depo-script').style.display=m==='script'?'block':'none';
document.getElementById('wiz-depo-existing').style.display=m==='existing'?'grid':'none';
document.getElementById('wiz-depo-btn').textContent=m==='auto'?'Подготовить и проверить':'Проверить';}
wizDepo();</script>
document.getElementById('wiz-depo-btn').textContent=m==='auto'?'Подготовить и проверить':'Проверить подключение';}
wizDepo();
document.getElementById('wiz-depo-show').onclick=function(){ // скрипт — прямо на странице, с текущими полями
var f=document.getElementById('wiz-depo'), box=document.getElementById('wiz-depo-sqlbox'), pre=document.getElementById('wiz-depo-sql'), b=this;
if(!f.reportValidity()) return;
var fd=new FormData(f); fd.set('view','1'); b.disabled=true;
fetch('/admin/setup/wizard-depo/script.sql',{method:'POST',body:new URLSearchParams(fd),cache:'no-store'})
.then(function(r){return r.text().then(function(t){return {ok:r.ok,t:t}})})
.then(function(x){ pre.textContent=x.ok?x.t:('✗ '+x.t.trim()); box.hidden=false; b.textContent='Обновить скрипт'; })
.catch(function(){ pre.textContent='✗ Не получилось — обновите страницу и попробуйте ещё раз.'; box.hidden=false; })
.then(function(){ b.disabled=false; });
};</script>
{{else if eq $sub "done"}}
<table style="margin:6px 0 10px">
+47 -5
View File
@@ -286,27 +286,64 @@ func depoEnsureLogin(s *FancyMSSQLSettings) {
}
}
// wizardDepoScript — POST /admin/setup/wizard-depo/script.sql: один скрипт подготовки базы файлом.
// depoScriptCreds — логин и пароль bj с экрана «Отдать скрипт администратору» (поля видны и меняются;
// пустой пароль — bj придумает сам). Возвращает текст ошибки.
func depoScriptCreds(r *http.Request, s *FancyMSSQLSettings) string {
if u := strings.TrimSpace(r.FormValue("bj_user")); u != "" {
if !depoLoginRe.MatchString(u) {
return "логин bj — латинские буквы, цифры, точка, дефис и _"
}
if u != s.User {
s.User, s.Password = u, ""
}
}
if p := r.FormValue("bj_password"); p != "" {
if len(p) < 8 {
return "пароль bj — не короче 8 знаков (SQL Server не примет короче)"
}
s.Password = p
}
return ""
}
// wizardDepoScript — POST /admin/setup/wizard-depo/script.sql: один скрипт подготовки базы файлом
// (view=1 — текстом для показа на странице).
func (h *setupHandlers) wizardDepoScript(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost || !requireDepo(w, r) {
return
}
view := r.FormValue("view") != ""
fail := func(msg string) {
if view {
http.Error(w, msg, http.StatusBadRequest)
return
}
wizDepoGo(w, r, "setup", msg, false)
}
s, db, agent, bad := h.depoSetupForm(r)
if bad == "" {
bad = depoScriptCreds(r, &s)
}
if bad != "" {
wizDepoGo(w, r, "setup", bad, false)
fail(bad)
return
}
depoEnsureLogin(&s)
s.DepoTablesMode = "script"
if err := h.rc.UpdateFancy(s); err != nil {
wizDepoGo(w, r, "setup", "не сохранилось: "+err.Error(), false)
fail("не сохранилось: " + err.Error())
return
}
body := depoSetupScript(s, db, s.User, s.Password, agent)
w.Header().Set("Cache-Control", "no-store")
if view {
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
_, _ = w.Write([]byte(body))
return
}
if !s.IsPostgres() {
body = "\uFEFF" + body // SSMS без BOM читает кириллицу в комментариях как ANSI
}
w.Header().Set("Cache-Control", "no-store")
w.Header().Set("Content-Type", "application/sql; charset=utf-8")
w.Header().Set("Content-Disposition", `attachment; filename="`+depoSetupFileName(s)+`"`)
_, _ = w.Write([]byte(body))
@@ -354,7 +391,12 @@ func (h *setupHandlers) wizardDepoSetup(w http.ResponseWriter, r *http.Request)
if u != s.User || r.FormValue("password") != "" {
s.User, s.Password = u, r.FormValue("password")
}
default: // script — администратор выполнил скрипт: логин и пароль bj уже в настройках
default: // script — администратор выполнил скрипт: логин и пароль bj — с экрана (они же в настройках)
if bad := depoScriptCreds(r, &s); bad != "" {
wizDepoGo(w, r, "setup", bad, false)
return
}
s.DepoTablesMode = "script"
if s.User == "" || s.Password == "" {
wizDepoGo(w, r, "setup", "сначала скачайте скрипт и передайте администратору SQL", false)
return