diff --git a/deploy/depo/fancy-m2m-firebird/README.md b/deploy/depo/fancy-m2m-firebird/README.md index 8422687..ce8fa2d 100644 --- a/deploy/depo/fancy-m2m-firebird/README.md +++ b/deploy/depo/fancy-m2m-firebird/README.md @@ -13,7 +13,10 @@ ## Установка из bj Мастер настройки bj, шаг «Учёт DEPO» → СУБД «Firebird» (27.10.2026): адрес сервера, порт 3050, путь к файлу базы -на сервере (как в подключении Fancy) или псевдоним. «Дать логин администратора» — SYSDBA, разово, не сохраняется: +или псевдоним. База обычно на другом компьютере: адрес — компьютер, где работает сервер Firebird (bj подключается +к нему по сети), путь — на его диске. Можно вставить строку подключения Fancy целиком — `10.0.0.5:D:\Fancy\DEPO.GDB`, +`сервер/3050:…`, `\\сервер\D:\…`, `\\сервер\D$\…`, `inet://сервер:порт/…`: адрес и порт bj возьмёт из неё. +Сетевую папку (`\\сервер\папка\…`) Firebird как базу не открывает — bj подскажет, что указать. «Дать логин администратора» — SYSDBA, разово, не сохраняется: bj проверит базу и функции Fancy (UDF), создаст логин bj `SYSDBA_BJ`, таблицы обмена, оператора Fancy для bj, кодировщик M2M, определит привязки `FTV_M2M_SETTINGS`, поставит процедуры и права и проверит обработку (`BJ_PING`). «Получить скрипт» — то же одним файлом для isql (WIN1251). Встроенные в bj копии этих файлов — diff --git a/internal/lkgateway/fancydb_handlers.go b/internal/lkgateway/fancydb_handlers.go index 4afaa0a..9cbf07d 100644 --- a/internal/lkgateway/fancydb_handlers.go +++ b/internal/lkgateway/fancydb_handlers.go @@ -77,7 +77,25 @@ func (h *setupHandlers) saveFancyDepoTables(w http.ResponseWriter, r *http.Reque } s := h.rc.Snapshot().Fancy db := strings.TrimSpace(r.FormValue("depo_database")) - if db != "" && !validDBName(db) { + if s.IsFirebird() && db != "" { // путь к базе на сервере Firebird или строка подключения Fancy целиком + host, p, path, err := fbSplitConn(db) + if err != nil { + setupFlash(w, r, "Таблицы DEPO: "+err.Error()) + return + } + if !validFBPath(path) { + setupFlash(w, r, "Таблицы DEPO: путь к базе — без кавычек, ; ? и #") + return + } + if host != "" && (host != s.Server || (p != 0 && p != s.Port)) { + s.Server = host + if p != 0 { + s.Port = p + } + s.DepoJobCheck, s.DepoJobCheckOK = "", false + } + db = path + } else if db != "" && !validDBName(db) { setupFlash(w, r, "Таблицы DEPO: имя базы — только латинские буквы/цифры/_, начинать с буквы") return } diff --git a/internal/lkgateway/fancyfb.go b/internal/lkgateway/fancyfb.go index ef32d7d..632a4b0 100644 --- a/internal/lkgateway/fancyfb.go +++ b/internal/lkgateway/fancyfb.go @@ -468,3 +468,50 @@ func fbErr(orig, sent string, err error) error { } return err } + +var ( + fbDriveRe = regexp.MustCompile(`^[A-Za-z]:[\\/]`) + fbHostRe = regexp.MustCompile(`^([A-Za-z0-9._\-]{2,}|\[[0-9A-Fa-f:]+\])(?:/(\d{1,5}))?:(.+)$`) + fbUNCRe = regexp.MustCompile(`^\\\\([^\\/]+)[\\/](.+)$`) + fbAdmShRe = regexp.MustCompile(`^([A-Za-z])\$[\\/](.*)$`) + fbInetURLRe = regexp.MustCompile(`(?i)^inet[46]?://([^/:]+)(?::(\d{1,5}))?/(.+)$`) +) + +// fbSplitConn — строка подключения Fancy к базе Firebird целиком: «сервер:путь», «сервер/порт:путь», +// «\\сервер\D:\путь», «\\сервер\D$\путь», «inet://сервер:порт/путь». База обычно на другом компьютере — на +// том, где работает сервер Firebird; bj подключается к нему по сети, а путь — на диске этого компьютера. +// host="" — указан только путь (сервер — в отдельном поле). Сетевую папку (\\сервер\папка\…) Firebird не +// открывает — ошибка с подсказкой. +func fbSplitConn(s string) (host string, port int, path string, err error) { + s = strings.TrimSpace(s) + switch { + case fbDriveRe.MatchString(s) || strings.HasPrefix(s, "/") || !strings.ContainsAny(s, `:\/`): + return "", 0, s, nil // путь на сервере или псевдоним + case fbInetURLRe.MatchString(s): + m := fbInetURLRe.FindStringSubmatch(s) + port, _ = strconv.Atoi(m[2]) + path = m[3] + if !fbDriveRe.MatchString(path) && !strings.ContainsAny(path, `\/`) { // inet://host/alias + return m[1], port, path, nil + } + if !fbDriveRe.MatchString(path) { + path = "/" + path + } + return m[1], port, path, nil + case fbUNCRe.MatchString(s): + m := fbUNCRe.FindStringSubmatch(s) + rest := m[2] + if fbDriveRe.MatchString(rest) { // \\сервер\D:\путь — так Fancy пишет подключение по именованным каналам + return m[1], 0, rest, nil + } + if a := fbAdmShRe.FindStringSubmatch(rest); a != nil { // \\сервер\D$\путь — административный общий ресурс + return m[1], 0, strings.ToUpper(a[1]) + `:\` + a[2], nil + } + return "", 0, "", fmt.Errorf("%s — сетевая папка: Firebird не открывает базы по сети как файл. Укажите компьютер %s в поле «Адрес сервера» (на нём должен работать сервер Firebird), а путь — на его диске, например D:\\Fancy\\DEPO.GDB, — как в подключении Fancy", s, m[1]) + case fbHostRe.MatchString(s): + m := fbHostRe.FindStringSubmatch(s) + port, _ = strconv.Atoi(m[2]) + return strings.Trim(m[1], "[]"), port, m[3], nil + } + return "", 0, s, nil +} diff --git a/internal/lkgateway/fancyfb_test.go b/internal/lkgateway/fancyfb_test.go index 9b0cd07..914d0b2 100644 --- a/internal/lkgateway/fancyfb_test.go +++ b/internal/lkgateway/fancyfb_test.go @@ -115,3 +115,31 @@ func TestFBScriptsMatchDeploy(t *testing.T) { } } } + +func TestFBSplitConn(t *testing.T) { + cases := []struct { + in, host string + port int + path string + bad bool + }{ + {`D:\Fancy\Bases\DEPO.GDB`, "", 0, `D:\Fancy\Bases\DEPO.GDB`, false}, + {`/var/db/depo.fdb`, "", 0, `/var/db/depo.fdb`, false}, + {`DEPO`, "", 0, `DEPO`, false}, + {`10.0.0.5:D:\Fancy\Bases\DEPO.GDB`, "10.0.0.5", 0, `D:\Fancy\Bases\DEPO.GDB`, false}, + {`srv-depo/3051:D:\Show Depo\Bases\SPI_DEPO.GDB`, "srv-depo", 3051, `D:\Show Depo\Bases\SPI_DEPO.GDB`, false}, + {`fbsrv:/var/db/depo.fdb`, "fbsrv", 0, `/var/db/depo.fdb`, false}, + {`fbsrv:DEPO`, "fbsrv", 0, `DEPO`, false}, + {`\\SRV-DEPO\D:\Fancy\DEPO.GDB`, "SRV-DEPO", 0, `D:\Fancy\DEPO.GDB`, false}, + {`\\SRV-DEPO\d$\Fancy\DEPO.GDB`, "SRV-DEPO", 0, `D:\Fancy\DEPO.GDB`, false}, + {`inet://10.0.0.5:3050/D:\Fancy\DEPO.GDB`, "10.0.0.5", 3050, `D:\Fancy\DEPO.GDB`, false}, + {`inet://10.0.0.5/depo`, "10.0.0.5", 0, `depo`, false}, + {`\\SRV-DEPO\Bases\DEPO.GDB`, "", 0, "", true}, + } + for _, c := range cases { + h, p, path, err := fbSplitConn(c.in) + if (err != nil) != c.bad || h != c.host || p != c.port || path != c.path { + t.Errorf("fbSplitConn(%q) = %q, %d, %q, %v", c.in, h, p, path, err) + } + } +} diff --git a/internal/lkgateway/igate.go b/internal/lkgateway/igate.go index e5e0767..ffef20c 100644 --- a/internal/lkgateway/igate.go +++ b/internal/lkgateway/igate.go @@ -308,7 +308,7 @@ func registerIgateAdmin(mux *http.ServeMux, rc *RuntimeConfig) { return } depoDB := strings.TrimSpace(r.FormValue("depo_db")) - if depoDB != "" && !validDBName(depoDB) { + if depoDB != "" && !validDBName(depoDB) && !(s.Fancy.IsFirebird() && validFBPath(depoDB)) { // у Firebird — путь к базе jsonOut(w, map[string]any{"ok": false, "error": "имя базы DEPO — латинские буквы, цифры и _"}) return } diff --git a/internal/lkgateway/web/templates/admin_wizard.html b/internal/lkgateway/web/templates/admin_wizard.html index 8dd2476..258e7be 100644 --- a/internal/lkgateway/web/templates/admin_wizard.html +++ b/internal/lkgateway/web/templates/admin_wizard.html @@ -156,10 +156,10 @@
Точное имя базы учёта DEPO — bj будет работать только с ней. Перед подготовкой bj проверит, что в ней есть учётные таблицы DEPO, и в чужую базу ничего не запишет.
-Путь к файлу базы учёта на сервере Firebird — как в подключении Fancy (например, D:\Fancy\Bases\DEPO.GDB), или её псевдоним из aliases.conf. Перед подготовкой bj проверит, что это база DEPO и функции Fancy на сервере доступны.
+База обычно на другом компьютере: «Адрес сервера» — компьютер, где работает сервер Firebird, путь — на его диске (например, D:\Fancy\Bases\DEPO.GDB) или псевдоним из aliases.conf. Можно вставить строку подключения из настройки Fancy целиком — 10.0.0.5:D:\Fancy\Bases\DEPO.GDB, адрес и порт bj возьмёт из неё. Сетевую папку (\\компьютер\папка) Firebird не открывает. Перед подготовкой bj проверит, что это база DEPO и функции Fancy на сервере доступны.