Учёт 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:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user