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 на сервере доступны.

@@ -247,7 +247,7 @@ if(['1433','5432','3050'].indexOf(f.port.value)>=0) f.port.value={mssql:'1433',postgres:'5432',firebird:'3050'}[t]; f.admin_user.placeholder=pg?'postgres':fb?'SYSDBA':'sa'; var dbi=f.depo_database; // Firebird: путь к файлу базы или псевдоним, а не имя - if(fb){ dbi.removeAttribute('pattern'); dbi.placeholder='D:\\Fancy\\DEPO.GDB или псевдоним'; } + if(fb){ dbi.removeAttribute('pattern'); dbi.placeholder='D:\\Fancy\\DEPO.GDB или 10.0.0.5:D:\\Fancy\\DEPO.GDB'; } else { dbi.setAttribute('pattern','[A-Za-z][A-Za-z0-9_]*'); dbi.placeholder='как называется на сервере'; } if(fb&&f.bj_user.value==='bj_user') f.bj_user.value='SYSDBA_BJ'; else if(!fb&&f.bj_user.value==='SYSDBA_BJ') f.bj_user.value='bj_user'; var jn=f.depo_job_name, dbv=(f.depo_database.value||'').trim(); // имя задания — по базе, пока не поправили руками @@ -270,6 +270,14 @@ else { pre.textContent=''; pre.dataset.msg=x.t.trim().replace(/^./,function(c){return c.toUpperCase()})+' — скрипт появится здесь.'; } }) .catch(function(){ if(my!==seq) return; last=''; pre.classList.remove('dq-busy'); pre.textContent=''; pre.dataset.msg='Не получилось собрать скрипт — обновите страницу.'; }); } + function splitFB(){ // строка подключения Fancy целиком: «сервер[/порт]:путь», «\\сервер\D:\путь», «\\сервер\D$\путь» + if(f.db_type.value!=='firebird') return; + var v=f.depo_database.value.trim(), m; + if(/^[A-Za-z]:[\\/]/.test(v)) return; + if((m=/^\\\\([^\\/]+)[\\/]([A-Za-z]):?\$?[\\/](.*)$/.exec(v))){ f.server.value=m[1]; f.depo_database.value=m[2].toUpperCase()+':\\'+m[3]; return; } + if((m=/^([A-Za-z0-9._-]{2,})(?:\/(\d{1,5}))?:(.+)$/.exec(v))){ f.server.value=m[1]; if(m[2]) f.port.value=m[2]; f.depo_database.value=m[3]; } + } + f.depo_database.addEventListener('change', function(){ splitFB(); changed(); }); function changed(){ sync(); clearTimeout(timer); timer=setTimeout(load, 350); } f.depo_job_name.addEventListener('input', function(){ this.dataset.auto=this.value.trim()?'0':'1'; }); f.addEventListener('input', changed); f.addEventListener('change', changed); diff --git a/internal/lkgateway/web/templates/part_fancy.html b/internal/lkgateway/web/templates/part_fancy.html index 36f5559..0d274e5 100644 --- a/internal/lkgateway/web/templates/part_fancy.html +++ b/internal/lkgateway/web/templates/part_fancy.html @@ -35,7 +35,7 @@
+ diff --git a/internal/lkgateway/wizard_depo.go b/internal/lkgateway/wizard_depo.go index 9ccdebd..ab30514 100644 --- a/internal/lkgateway/wizard_depo.go +++ b/internal/lkgateway/wizard_depo.go @@ -248,10 +248,20 @@ func (h *setupHandlers) depoSetupForm(r *http.Request) (FancyMSSQLSettings, stri return s, "", agent, "укажите имя базы DEPO — как она называется на сервере" } if dbType == "firebird" { - if !validFBPath(db) { + host, p, path, err := fbSplitConn(db) // строка подключения Fancy целиком: «сервер:путь» — сервер из неё + if err != nil { + return s, "", agent, err.Error() + } + if host != "" { + s.Server = host + if p != 0 { + s.Port = p + } + } + if !validFBPath(path) { return s, "", agent, "путь к базе DEPO — без кавычек, ; ? и #" } - return s, db, agent, "" + return s, path, agent, "" } if !validDBName(db) { return s, "", agent, "имя базы DEPO — только латинские буквы, цифры и _, начинается с буквы"