Справка: технические страницы только в нашей сборке (build-tag internaldocs)

Из клиентской сборки убрана проектная документация, читавшаяся прямым `strings`:
архитектура обмена с НРД, протокол событий bj↔bj2depo (FTV_M2M, 41 КБ), схема
базы, криптография, QUIK МНП. Это самая крупная и самая дешёвая утечка знаний —
её не нужно было даже декомпилировать.

Файлы перенесены в web/templates/internal/ (выпадают из общего embed-глоба) и
встраиваются только при `go build -tags internaldocs` (internaldocs_on.go).
По умолчанию (клиентский релиз, build-release.sh) страницы заменяются заглушкой
«доступно на портале» (internaldocs_off.go); ссылки на них в индексе справки
показываются под {{if internalDocs}}. Операторские страницы (ручной ввод, робот,
внешние системы, API ЛК) остаются — они нужны на месте.

Клиентская сборка: -27 тыс. символов внутренней документации, «Согласовано
10.09», «Для программиста Fancy», «как устроен обмен» → 0 совпадений.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4YVo4UMopqT5DzicqpPjL
This commit is contained in:
zuevav
2026-09-18 20:05:06 +03:00
parent e186ff7e76
commit fcded5fe17
10 changed files with 84 additions and 25 deletions
+16 -15
View File
@@ -49,16 +49,17 @@ var templateFuncs = template.FuncMap{
return template.URL(uri) return template.URL(uri)
}, },
// m2mCode — человекочитаемое название кода M2M: «Текст (M2Mxx)». // m2mCode — человекочитаемое название кода M2M: «Текст (M2Mxx)».
"m2mCode": m2mCodeLabel, "m2mCode": m2mCodeLabel,
"quikCodeMap": quikClientCodeMapText, "quikCodeMap": quikClientCodeMapText,
"curUser": authTemplateFuncs["curUser"], "curUser": authTemplateFuncs["curUser"],
"can": authTemplateFuncs["can"], "can": authTemplateFuncs["can"],
"devOn": authTemplateFuncs["devOn"], "devOn": authTemplateFuncs["devOn"],
"devLicensed": authTemplateFuncs["devLicensed"], "devLicensed": authTemplateFuncs["devLicensed"],
"authOpen": authOpen, "authOpen": authOpen,
"roleTitle": roleTitle, "internalDocs": func() bool { return internalDocsBuilt },
"stageCls": func(r depoQueueRow) string { c, _ := r.StageRu(); return c }, "roleTitle": roleTitle,
"stageText": func(r depoQueueRow) string { _, t := r.StageRu(); return t }, "stageCls": func(r depoQueueRow) string { c, _ := r.StageRu(); return c },
"stageText": func(r depoQueueRow) string { _, t := r.StageRu(); return t },
"deref": func(v *float64) float64 { "deref": func(v *float64) float64 {
if v == nil { if v == nil {
return 0 return 0
@@ -240,7 +241,7 @@ func newAdmin() (*admin, error) {
if err != nil { if err != nil {
return nil, fmt.Errorf("parse admin_help: %w", err) return nil, fmt.Errorf("parse admin_help: %w", err)
} }
helpDB, err := parse("admin_help_database.html") helpDB, err := parseInternalDoc("admin_help_database.html")
if err != nil { if err != nil {
return nil, fmt.Errorf("parse admin_help_database: %w", err) return nil, fmt.Errorf("parse admin_help_database: %w", err)
} }
@@ -248,7 +249,7 @@ func newAdmin() (*admin, error) {
if err != nil { if err != nil {
return nil, fmt.Errorf("parse admin_help_lk: %w", err) return nil, fmt.Errorf("parse admin_help_lk: %w", err)
} }
helpCrypto, err := parse("admin_help_crypto.html") helpCrypto, err := parseInternalDoc("admin_help_crypto.html")
if err != nil { if err != nil {
return nil, fmt.Errorf("parse admin_help_crypto: %w", err) return nil, fmt.Errorf("parse admin_help_crypto: %w", err)
} }
@@ -268,15 +269,15 @@ func newAdmin() (*admin, error) {
if err != nil { if err != nil {
return nil, fmt.Errorf("parse admin_help_robot: %w", err) return nil, fmt.Errorf("parse admin_help_robot: %w", err)
} }
helpArch, err := parse("admin_help_architecture.html") helpArch, err := parseInternalDoc("admin_help_architecture.html")
if err != nil { if err != nil {
return nil, fmt.Errorf("parse admin_help_architecture: %w", err) return nil, fmt.Errorf("parse admin_help_architecture: %w", err)
} }
helpQuik, err := parse("admin_help_quik.html") helpQuik, err := parseInternalDoc("admin_help_quik.html")
if err != nil { if err != nil {
return nil, fmt.Errorf("parse admin_help_quik: %w", err) return nil, fmt.Errorf("parse admin_help_quik: %w", err)
} }
helpFancyEvents, err := parse("admin_help_fancy_events.html") helpFancyEvents, err := parseInternalDoc("admin_help_fancy_events.html")
if err != nil { if err != nil {
return nil, fmt.Errorf("parse admin_help_fancy_events: %w", err) return nil, fmt.Errorf("parse admin_help_fancy_events: %w", err)
} }
+28
View File
@@ -0,0 +1,28 @@
//go:build !internaldocs
package lkgateway
// internaldocs_off.go — КЛИЕНТСКАЯ сборка (по умолчанию, без флага internaldocs):
// технические страницы справки (архитектура обмена с НРД, протокол событий
// bj↔bj2depo, схема базы, криптография, QUIK МНП) в бинарь НЕ попадают. Это
// проектная документация, из которой видно, как устроен продукт; операторам
// депозитария она не нужна, а конкуренту дала бы готовую карту. Внедренцам
// эти страницы доступны в нашей сборке (go build -tags internaldocs) и на портале.
import "html/template"
// internalDocsBuilt — технических страниц справки в этом бинаре нет.
const internalDocsBuilt = false
const internalDocsStub = `<a href="/admin/help" class="muted">← все инструкции</a>
<h1>Техническая документация</h1>
<p class="muted" style="max-width:640px;line-height:1.6">Подробные технические материалы (архитектура обмена с НРД, протоколы интеграции, схема базы данных) не входят в рабочую сборку. Они доступны специалистам внедрения на портале <b>lic.zetit.ru</b> и в документации, переданной по договору.</p>`
// parseInternalDoc — заглушка: содержимого нет, показываем, куда обратиться.
func parseInternalDoc(_ string) (*template.Template, error) {
t := template.New("layout.html").Funcs(templateFuncs)
if _, err := t.ParseFS(templatesFS, "web/templates/layout.html"); err != nil {
return nil, err
}
return t.New("content").Parse(internalDocsStub)
}
+26
View File
@@ -0,0 +1,26 @@
//go:build internaldocs
package lkgateway
// internaldocs_on.go — сборка ДЛЯ НАС (флаг internaldocs): технические страницы
// справки встроены. Собирать: go build -tags internaldocs.
import (
"embed"
"html/template"
)
//go:embed web/templates/internal/*.html
var internalDocsFS embed.FS
// internalDocsBuilt — технические страницы справки вшиты в этот бинарь.
const internalDocsBuilt = true
// parseInternalDoc — страница внедрения (layout из основного FS + сам документ).
func parseInternalDoc(name string) (*template.Template, error) {
t := template.New("layout.html").Funcs(templateFuncs)
if _, err := t.ParseFS(templatesFS, "web/templates/layout.html"); err != nil {
return nil, err
}
return t.ParseFS(internalDocsFS, "web/templates/internal/"+name)
}
@@ -5,24 +5,24 @@
</div> </div>
<div class="grid"> <div class="grid">
<a href="/admin/help/database" style="text-decoration:none"> {{if internalDocs}}<a href="/admin/help/database" style="text-decoration:none">
<div class="card" style="height:100%"> <div class="card" style="height:100%">
<h2 style="color:var(--accent)">База данных →</h2> <h2 style="color:var(--accent)">База данных →</h2>
<p class="muted">PostgreSQL: подключение, схемы <code>fansy</code> / <code>fansy_staging</code> / <code>m2m_core</code>, что подгружается извне (ETL Fansy) и что пишет сама система. Учётные записи, гранты, миграции.</p> <p class="muted">PostgreSQL: подключение, схемы <code>fansy</code> / <code>fansy_staging</code> / <code>m2m_core</code>, что подгружается извне (ETL Fansy) и что пишет сама система. Учётные записи, гранты, миграции.</p>
</div> </div>
</a> </a>{{end}}
<a href="/admin/help/lk-api" style="text-decoration:none"> <a href="/admin/help/lk-api" style="text-decoration:none">
<div class="card" style="height:100%"> <div class="card" style="height:100%">
<h2 style="color:var(--accent)">API личного кабинета →</h2> <h2 style="color:var(--accent)">API личного кабинета →</h2>
<p class="muted">REST-контракт ESIA Finance V1: <code>POST /api/v1/back_office/claims/</code>, GET/PATCH-операции, формат callback'ов, аутентификация Basic, примеры запросов curl.</p> <p class="muted">REST-контракт ESIA Finance V1: <code>POST /api/v1/back_office/claims/</code>, GET/PATCH-операции, формат callback'ов, аутентификация Basic, примеры запросов curl.</p>
</div> </div>
</a> </a>
<a href="/admin/help/crypto" style="text-decoration:none"> {{if internalDocs}}<a href="/admin/help/crypto" style="text-decoration:none">
<div class="card" style="height:100%"> <div class="card" style="height:100%">
<h2 style="color:var(--accent)">Криптография (Валидата) →</h2> <h2 style="color:var(--accent)">Криптография (Валидата) →</h2>
<p class="muted">Установка АПК «Валидата Клиент L» на Astra Linux SE, подключение через PKCS#11, проверка подписи и квитанций НРД.</p> <p class="muted">Установка АПК «Валидата Клиент L» на Astra Linux SE, подключение через PKCS#11, проверка подписи и квитанций НРД.</p>
</div> </div>
</a> </a>{{end}}
<a href="/admin/help/systems" style="text-decoration:none"> <a href="/admin/help/systems" style="text-decoration:none">
<div class="card" style="height:100%"> <div class="card" style="height:100%">
<h2 style="color:var(--accent)">Внешние системы →</h2> <h2 style="color:var(--accent)">Внешние системы →</h2>
@@ -41,23 +41,23 @@
<p class="muted">Робот НРД на TEST3 (код <code>MC0012500000</code>), 4 тестовых сценария (отказ / принять все / частично / встречный перевод), управление через DocumentSeries и DocumentNumber, тестовые наборы депозитариев и кодов ошибок.</p> <p class="muted">Робот НРД на TEST3 (код <code>MC0012500000</code>), 4 тестовых сценария (отказ / принять все / частично / встречный перевод), управление через DocumentSeries и DocumentNumber, тестовые наборы депозитариев и кодов ошибок.</p>
</div> </div>
</a>{{end}} </a>{{end}}
<a href="/admin/help/architecture" style="text-decoration:none"> {{if internalDocs}}<a href="/admin/help/architecture" style="text-decoration:none">
<div class="card" style="height:100%"> <div class="card" style="height:100%">
<h2 style="color:var(--accent)">Архитектура обмена с НРД →</h2> <h2 style="color:var(--accent)">Архитектура обмена с НРД →</h2>
<p class="muted">Полная схема: bj-server → ИШ (на Astra Linux ВМ) → ONYX (НРД) → {{if devOn}}робот-автотест{{else}}депозитарий-контрагент{{end}}. Кто на чьей стороне, какое СКЗИ, какие сертификаты, FAQ.</p> <p class="muted">Полная схема: bj-server → ИШ (на Astra Linux ВМ) → ONYX (НРД) → {{if devOn}}робот-автотест{{else}}депозитарий-контрагент{{end}}. Кто на чьей стороне, какое СКЗИ, какие сертификаты, FAQ.</p>
</div> </div>
</a> </a>{{end}}
<a href="/admin/help/fancy-events" style="text-decoration:none"> {{if internalDocs}}<a href="/admin/help/fancy-events" style="text-decoration:none">
<div class="card" style="height:100%"> <div class="card" style="height:100%">
<h2 style="color:var(--accent)">bj ↔ bj2depo: трансферные таблицы и события — для программиста Fancy →</h2> <h2 style="color:var(--accent)">bj ↔ bj2depo: трансферные таблицы и события — для программиста Fancy →</h2>
<p class="muted">FTV_M2M / FTV_M2M_LINES / FTV_M2M_EVENTS: какие идентификаторы даёт bj, словарь событий в обе стороны (BJ_* и DEPO_*), правила STATE и идемпотентности, порядок по этапам, примеры SQL, открытые вопросы.</p> <p class="muted">FTV_M2M / FTV_M2M_LINES / FTV_M2M_EVENTS: какие идентификаторы даёт bj, словарь событий в обе стороны (BJ_* и DEPO_*), правила STATE и идемпотентности, порядок по этапам, примеры SQL, открытые вопросы.</p>
</div> </div>
</a> </a>{{end}}
<a href="/admin/help/quik" style="text-decoration:none"> {{if internalDocs}}<a href="/admin/help/quik" style="text-decoration:none">
<div class="card" style="height:100%"> <div class="card" style="height:100%">
<h2 style="color:var(--accent)">QUIK МНП: подготовка к M2M — для администратора QUIK →</h2> <h2 style="color:var(--accent)">QUIK МНП: подготовка к M2M — для администратора QUIK →</h2>
<p class="muted">Чек-лист для QUIK Administrator: классы INSTROUTM2M / INSTRINM2M, инструменты, включение типов «Ввод/Вывод ЦБ М2М», права технологического пользователя, перезапуск модуля. Со ссылками на страницы документов ARQA и разбором ошибок.</p> <p class="muted">Чек-лист для QUIK Administrator: классы INSTROUTM2M / INSTRINM2M, инструменты, включение типов «Ввод/Вывод ЦБ М2М», права технологического пользователя, перезапуск модуля. Со ссылками на страницы документов ARQA и разбором ошибок.</p>
</div> </div>
</a> </a>{{end}}
</div> </div>
{{end}} {{end}}
+4
View File
@@ -21,6 +21,10 @@ rm -rf "$DIST" && mkdir -p "$DIST"
cd "$ROOT" cd "$ROOT"
ver() { eval "v=\${$1:-}"; [ -n "$v" ] && echo "$v" || echo "$VERSION"; } ver() { eval "v=\${$1:-}"; [ -n "$v" ] && echo "$v" || echo "$VERSION"; }
echo "Сборка релиза $VERSION (канал $CHANNEL) в $DIST" echo "Сборка релиза $VERSION (канал $CHANNEL) в $DIST"
# ВАЖНО: клиентский релиз собирается БЕЗ тега internaldocs — технические страницы
# справки (архитектура НРД, протокол FTV_M2M, схема базы, крипто, QUIK) в бинарь не
# попадают (см. internal/lkgateway/internaldocs_off.go). Свою сборку со справкой
# делаем отдельно: go build -tags internaldocs. Не добавляйте этот тег сюда.
go build -trimpath -ldflags "-s -w -X $MOD/internal/lkgateway.BuildVersion=$(ver BJ_SERVER_VERSION)" -o "$DIST/bj-server" ./cmd/bj-server go build -trimpath -ldflags "-s -w -X $MOD/internal/lkgateway.BuildVersion=$(ver BJ_SERVER_VERSION)" -o "$DIST/bj-server" ./cmd/bj-server
CGO_ENABLED=0 go build -trimpath -ldflags "-s -w -X main.installerVersion=$(ver INSTALLER_VERSION)" -o "$DIST/bj-installer" ./cmd/bj-installer CGO_ENABLED=0 go build -trimpath -ldflags "-s -w -X main.installerVersion=$(ver INSTALLER_VERSION)" -o "$DIST/bj-installer" ./cmd/bj-installer
CGO_ENABLED=0 go build -trimpath -ldflags "-s -w -X main.version=$(ver COMPONENT_INSTALL_VERSION)" -o "$DIST/bj-component-install" ./cmd/bj-component-install CGO_ENABLED=0 go build -trimpath -ldflags "-s -w -X main.version=$(ver COMPONENT_INSTALL_VERSION)" -o "$DIST/bj-component-install" ./cmd/bj-component-install