a9915bcc82
- «Подготовить» / «Проверить подключение» показывают ход: подключение логином администратора, проверка, что это база DEPO, применение скрипта, вход логином bj, таблицы обмена, задание, процедуры, «Готово». Сервер отдаёт шаги строками JSON (stream=1), на ошибке — шаг с крестиком и причина, без перехода со страницы. - Блоки «Администратор SQL — нужен один раз, не сохраняется» и «Логин bj — под ним bj будет работать»: bj сохраняет и использует ровно то, что вписано; «другой пароль»; на итоге шага — каким логином bj работает. - Пауза 15 минут после неудачного входа в DEPO (логин/пароль отвергнуты) для фоновых служб: с неверным паролем они за минуты блокировали логин на сервере (CHECK_POLICY) у всех, кто им пользуется. Действия человека идут без паузы; новый пароль — сразу; системное событие в «Новостях». Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FsEKyJrYF69Cj9ZXzFLRi
131 lines
3.8 KiB
Go
131 lines
3.8 KiB
Go
package lkgateway
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"fmt"
|
|
"net/url"
|
|
"strings"
|
|
"time"
|
|
|
|
_ "github.com/jackc/pgx/v5/stdlib" // драйвер "pgx" для PostgreSQL
|
|
_ "github.com/microsoft/go-mssqldb" // драйвер "sqlserver" для MSSQL
|
|
)
|
|
|
|
// Подключение к серверу учётной базы клиента Fancy DEPO — MSSQL или PostgreSQL,
|
|
// SQL-логин. База выбирается в настройках (DepoDB).
|
|
|
|
// validDBName — безопасное имя БД/идентификатора MSSQL (буквы/цифры/_), чтобы
|
|
// не дать инъекции в CREATE DATABASE (имя БД нельзя параметризовать).
|
|
func validDBName(s string) bool {
|
|
if s == "" || len(s) > 64 {
|
|
return false
|
|
}
|
|
for i, c := range s {
|
|
ok := c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c == '_' || (i > 0 && c >= '0' && c <= '9')
|
|
if !ok {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// fancyDriver возвращает имя database/sql-драйвера по типу СУБД.
|
|
func fancyDriver(s FancyMSSQLSettings) string {
|
|
if s.IsPostgres() {
|
|
return "pgx"
|
|
}
|
|
return "sqlserver"
|
|
}
|
|
|
|
// fancyAdminDB — имя служебной БД для создания целевой: master (MSSQL) /
|
|
// postgres (PostgreSQL). Подключаемся к ней, чтобы выполнить CREATE DATABASE.
|
|
func fancyAdminDB(s FancyMSSQLSettings) string {
|
|
if s.IsPostgres() {
|
|
return "postgres"
|
|
}
|
|
return "master"
|
|
}
|
|
|
|
// fancyDSN строит строку подключения. db="" → к служебной БД (для CREATE DATABASE).
|
|
func fancyDSN(s FancyMSSQLSettings, db string) string {
|
|
port := s.DefaultPort()
|
|
if s.IsPostgres() {
|
|
ssl := s.Encrypt
|
|
switch ssl {
|
|
case "", "disable", "false":
|
|
ssl = "disable"
|
|
case "true": // в UI значение из MSSQL-набора — мапим в требование TLS
|
|
ssl = "require"
|
|
}
|
|
q := url.Values{}
|
|
q.Set("sslmode", ssl)
|
|
q.Set("connect_timeout", "10")
|
|
u := &url.URL{
|
|
Scheme: "postgres",
|
|
User: url.UserPassword(s.User, s.Password),
|
|
Host: fmt.Sprintf("%s:%d", s.Server, port),
|
|
Path: "/" + db,
|
|
RawQuery: q.Encode(),
|
|
}
|
|
return u.String()
|
|
}
|
|
// MSSQL
|
|
enc := s.Encrypt
|
|
if enc == "" {
|
|
enc = "disable" // по умолчанию без TLS (типовой LAN-сценарий)
|
|
}
|
|
q := url.Values{}
|
|
if db != "" {
|
|
q.Set("database", db)
|
|
}
|
|
q.Set("encrypt", enc)
|
|
q.Set("connection timeout", "10")
|
|
u := &url.URL{
|
|
Scheme: "sqlserver",
|
|
User: url.UserPassword(s.User, s.Password),
|
|
Host: fmt.Sprintf("%s:%d", s.Server, port),
|
|
RawQuery: q.Encode(),
|
|
}
|
|
return u.String()
|
|
}
|
|
|
|
// fancyOpen открывает пул подключений к указанной БД (или служебной при db="").
|
|
func fancyOpen(s FancyMSSQLSettings, db string) (*sql.DB, error) {
|
|
if s.IsPostgres() && db == "" {
|
|
db = fancyAdminDB(s)
|
|
}
|
|
conn, err := fancyOpenGated(s, fancyDSN(s, db)) // после неудачного входа — пауза, чтобы не заблокировать логин
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
conn.SetConnMaxLifetime(5 * time.Minute)
|
|
conn.SetMaxOpenConns(4)
|
|
return conn, nil
|
|
}
|
|
|
|
// fancyPing проверяет соединение и возвращает версию SQL Server.
|
|
func fancyPing(ctx context.Context, s FancyMSSQLSettings, db string) (string, error) {
|
|
conn, err := fancyOpen(s, db)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
defer conn.Close()
|
|
cctx, cancel := context.WithTimeout(ctx, 12*time.Second)
|
|
defer cancel()
|
|
if err := conn.PingContext(cctx); err != nil {
|
|
return "", err
|
|
}
|
|
var ver string
|
|
verQuery := "SELECT @@VERSION"
|
|
if s.IsPostgres() {
|
|
verQuery = "SELECT version()"
|
|
}
|
|
_ = conn.QueryRowContext(cctx, verQuery).Scan(&ver)
|
|
ver = strings.TrimSpace(ver)
|
|
if i := strings.IndexAny(ver, "\r\n"); i > 0 {
|
|
ver = ver[:i]
|
|
}
|
|
return ver, nil
|
|
}
|