Первичная настройка: ключ спрашивается один раз, затем обновления, модули и «Время и сеть»; DEPO одним шагом
- Первая страница — только администратор (и восстановление из копии); ключ продукта — после создания администратора, на шаге «Лицензия». Консоль первой загрузки ключ больше не спрашивает (для образа с комплектом bj). - «Подготовка модулей» начинает с проверки обновлений: есть — ставит (bj перезапускается, страница ждёт и продолжает), нет — дальше; выпуск, который уже ставили, по кругу не ставится. Затем модули лицензии и переход на «Время и сеть» (шаг 12) — часовой пояс клиент выбирает сам, шаг больше не проскакивает. - Учёт DEPO одним шагом: bj сам находит базу Fancy DEPO на сервере (по таблицам OD_FACES/OD_RESTS), имя базы не спрашивается. Один скрипт создаёт логин bj, таблицы обмена и права; по выбору — задание обработки в SQL Agent или обработка на сервере bj. Выполнить сразу (логин администратора СУБД не сохраняется), скачать скрипт для администратора или подключиться готовым логином. - Восстановление из копии — только на первой странице, в мастере его больше нет. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FsEKyJrYF69Cj9ZXzFLRi
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
package lkgateway
|
||||
|
||||
// Поиск базы DEPO на сервере (05.10.2026). Клиент не знает и не должен знать, как называется база учёта у
|
||||
// него на сервере: на шаге «Подключение» он вводит только адрес, логин и пароль, а bj сам находит базы с
|
||||
// учётными таблицами Fancy DEPO (лица OD_FACES и остатки OD_RESTS), к которым у этого логина есть доступ.
|
||||
// Одна база — выбирается сразу, несколько — мастер предлагает выбрать, ни одной — понятная ошибка.
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// depoMarkerTables — по каким таблицам база опознаётся как DEPO (Fancy).
|
||||
var depoMarkerTables = []string{"OD_FACES", "OD_RESTS"}
|
||||
|
||||
// depoDiscoverMax — сколько баз сервера просматривать (на сервере могут быть десятки баз).
|
||||
const depoDiscoverMax = 60
|
||||
|
||||
// depoDiscover — базы DEPO на сервере, доступные этому логину.
|
||||
func depoDiscover(ctx context.Context, s FancyMSSQLSettings) ([]string, error) {
|
||||
cctx, cancel := context.WithTimeout(ctx, 30*time.Second)
|
||||
defer cancel()
|
||||
if s.IsPostgres() {
|
||||
return depoDiscoverPG(cctx, s)
|
||||
}
|
||||
conn, err := fancyOpen(s, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer conn.Close()
|
||||
rows, err := conn.QueryContext(cctx, `SELECT name FROM sys.databases
|
||||
WHERE state = 0 AND database_id > 4 AND HAS_DBACCESS(name) = 1 ORDER BY name`)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("список баз сервера: %w", err)
|
||||
}
|
||||
var names []string
|
||||
for rows.Next() {
|
||||
var n string
|
||||
if rows.Scan(&n) == nil && validDBName(n) && len(names) < depoDiscoverMax {
|
||||
names = append(names, n)
|
||||
}
|
||||
}
|
||||
rows.Close()
|
||||
var found []string
|
||||
for _, n := range names {
|
||||
// имя проверено validDBName — подставлять в [ ] безопасно
|
||||
q := "SELECT CASE WHEN " + strings.Join(func() []string {
|
||||
var c []string
|
||||
for _, t := range depoMarkerTables {
|
||||
c = append(c, "OBJECT_ID(N'["+n+"].dbo."+t+"') IS NOT NULL")
|
||||
}
|
||||
return c
|
||||
}(), " AND ") + " THEN 1 ELSE 0 END"
|
||||
var ok int
|
||||
if conn.QueryRowContext(cctx, q).Scan(&ok) == nil && ok == 1 {
|
||||
found = append(found, n)
|
||||
}
|
||||
}
|
||||
return found, nil
|
||||
}
|
||||
|
||||
func depoDiscoverPG(ctx context.Context, s FancyMSSQLSettings) ([]string, error) {
|
||||
conn, err := fancyOpen(s, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer conn.Close()
|
||||
rows, err := conn.QueryContext(ctx, `SELECT datname FROM pg_database
|
||||
WHERE datallowconn AND NOT datistemplate AND has_database_privilege(datname, 'CONNECT') ORDER BY datname`)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("список баз сервера: %w", err)
|
||||
}
|
||||
var names []string
|
||||
for rows.Next() {
|
||||
var n string
|
||||
if rows.Scan(&n) == nil && validDBName(n) && n != "postgres" && len(names) < depoDiscoverMax {
|
||||
names = append(names, n)
|
||||
}
|
||||
}
|
||||
rows.Close()
|
||||
var found []string
|
||||
for _, n := range names {
|
||||
db, err := fancyOpen(s, n)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
var cnt int
|
||||
err = db.QueryRowContext(ctx, `SELECT count(DISTINCT lower(table_name)) FROM information_schema.tables
|
||||
WHERE lower(table_name) = ANY($1::text[])`, "{"+strings.ToLower(strings.Join(depoMarkerTables, ","))+"}").Scan(&cnt)
|
||||
db.Close()
|
||||
if err == nil && cnt == len(depoMarkerTables) {
|
||||
found = append(found, n)
|
||||
}
|
||||
}
|
||||
return found, nil
|
||||
}
|
||||
Reference in New Issue
Block a user