neuroalpha 0.3.16 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -1
- package/config/certs/russian_trusted_root_ca.pem +33 -0
- package/docs//320/224/320/225/320/237/320/233/320/236/320/231.md +4 -4
- package/docs//320/230/320/235/320/241/320/242/320/240/320/243/320/232/320/246/320/230/320/257.md +2 -6
- package/docs//320/234/320/225/320/245/320/220/320/235/320/230/320/232/320/220_/320/230_/320/237/320/220/320/240/320/220/320/234/320/225/320/242/320/240/320/253.md +2 -2
- package/package.json +60 -53
- package/public/activate.html +36 -0
- package/public/activate.js +33 -0
- package/public/admin.html +126 -7
- package/public/app.js +608 -182
- package/public/index.html +57 -47
- package/public/landing.html +106 -37
- package/public/legal.html +4 -4
- package/public/styles.css +152 -36
- package/server/api/routes.js +308 -37
- package/server/auth/index.js +79 -15
- package/server/brokers/index.js +1022 -172
- package/server/build-info.js +7 -0
- package/server/config.js +13 -6
- package/server/data/moex.js +6 -4
- package/server/db.js +22 -18
- package/server/desktop-entry.js +103 -0
- package/server/hub.js +28 -14
- package/server/index.js +82 -12
- package/server/instance/agent.js +259 -0
- package/server/instance/central.js +491 -0
- package/server/instance/identity.js +79 -0
- package/server/instance/provision.js +166 -0
- package/server/instance/signing.js +71 -0
- package/server/model/edge.js +19 -0
- package/server/model/forecast.js +107 -57
- package/server/model/live.js +1 -1
- package/server/model/models.js +12 -3
- package/server/model/research.js +218 -30
- package/server/notify/index.js +51 -16
- package/server/params-meta.js +3 -3
- package/server/role.js +44 -0
- package/server/security.js +40 -0
- package/server/strategist/index.js +462 -488
- package/server/strategist/tools.js +675 -532
- package/server/tls-ca.js +32 -0
package/server/role.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// Copyright (c) 2026 Roman A. Perepelkin. All rights reserved. Proprietary software.
|
|
2
|
+
// Режим работы сервера:
|
|
3
|
+
// central — центральный сервер платформы: лендинг, кабинеты, оплата, админка, ИИ-прокси, реестр машин клиентов и выпуск лицензий
|
|
4
|
+
// instance — машина клиента с лицензией центра. Вид машины (INSTANCE_KIND):
|
|
5
|
+
// cloud — арендованная машина в Kubernetes (поддомен naXXXXXX.odant.org), вход через центр
|
|
6
|
+
// desktop — установка на компьютер клиента (VIP по договору), активация кодом из кабинета
|
|
7
|
+
// dev — локальный запуск разработчика/владельца (прежнее поведение)
|
|
8
|
+
import './config.js';
|
|
9
|
+
import { BUILD } from './build-info.js';
|
|
10
|
+
|
|
11
|
+
// Защищённая сборка для компьютера клиента — всегда instance/desktop, без возможности переопределения
|
|
12
|
+
const raw = BUILD ? 'instance' : String(process.env.NEUROALPHA_ROLE || (process.env.CLOUD_MODE === '1' ? 'central' : 'dev')).trim().toLowerCase();
|
|
13
|
+
export const ROLE = ['central', 'instance', 'dev'].includes(raw) ? raw : 'dev';
|
|
14
|
+
export const IS_CENTRAL = ROLE === 'central';
|
|
15
|
+
export const IS_INSTANCE = ROLE === 'instance';
|
|
16
|
+
export const IS_DEV = ROLE === 'dev';
|
|
17
|
+
export const INSTANCE_KIND = BUILD ? 'desktop' : (IS_INSTANCE && String(process.env.INSTANCE_KIND || 'cloud').trim().toLowerCase() === 'desktop' ? 'desktop' : 'cloud');
|
|
18
|
+
export const IS_DESKTOP = IS_INSTANCE && INSTANCE_KIND === 'desktop';
|
|
19
|
+
|
|
20
|
+
if (IS_CENTRAL) process.env.CLOUD_MODE = '1';
|
|
21
|
+
if (BUILD) {
|
|
22
|
+
delete process.env.CLOUD_MODE;
|
|
23
|
+
// провайдер ИИ «НейроАльфа Облако» и проверка лицензии ходят только в вшитый центр
|
|
24
|
+
process.env.CLOUD_URL = BUILD.centralUrl;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const INSTANCE_DOMAIN = String(process.env.INSTANCE_DOMAIN || 'odant.org').trim().replace(/^\.+/, '');
|
|
28
|
+
export const CENTRAL_URL = (BUILD ? BUILD.centralUrl : String(process.env.CENTRAL_URL || process.env.CLOUD_URL || 'https://neuroalpha.odant.org')).trim().replace(/\/+$/, '');
|
|
29
|
+
|
|
30
|
+
// Адрес прослушивания: установка на компьютер по умолчанию доступна только с этого компьютера
|
|
31
|
+
export const LISTEN_HOST = String(process.env.HOST || (IS_DESKTOP ? '127.0.0.1' : '')).trim() || undefined;
|
|
32
|
+
|
|
33
|
+
// IP клиента: за ingress/прокси (central, cloud-машина или TRUST_PROXY=1) берётся последний адрес из X-Forwarded-For,
|
|
34
|
+
// который добавил наш прокси; первые значения цепочки клиент может подделать
|
|
35
|
+
export function clientIp(req) {
|
|
36
|
+
const behindProxy = IS_CENTRAL || (IS_INSTANCE && !IS_DESKTOP) || process.env.TRUST_PROXY === '1';
|
|
37
|
+
const xff = String(req?.headers?.['x-forwarded-for'] || '').split(',').map((s) => s.trim()).filter(Boolean);
|
|
38
|
+
if (behindProxy && xff.length) return xff[xff.length - 1];
|
|
39
|
+
return String(req?.socket?.remoteAddress || '');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function isHttpsRequest(req) {
|
|
43
|
+
return Boolean(req?.secure || String(req?.headers?.['x-forwarded-proto'] || '').split(',')[0].trim() === 'https');
|
|
44
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// Copyright (c) 2026 Roman A. Perepelkin. All rights reserved. Proprietary software.
|
|
2
|
+
// Защитные HTTP-заголовки, CSP для терминала и защита от межсайтовых запросов (CSRF / Cross-Site WebSocket)
|
|
3
|
+
|
|
4
|
+
const APP_CSP = [
|
|
5
|
+
"default-src 'self'",
|
|
6
|
+
"script-src 'self' https://unpkg.com",
|
|
7
|
+
"style-src 'self' 'unsafe-inline'",
|
|
8
|
+
"img-src 'self' data: https:",
|
|
9
|
+
"font-src 'self' data:",
|
|
10
|
+
"connect-src 'self' ws: wss:",
|
|
11
|
+
"object-src 'none'",
|
|
12
|
+
"base-uri 'self'",
|
|
13
|
+
"form-action 'self'",
|
|
14
|
+
"frame-ancestors 'none'",
|
|
15
|
+
].join('; ');
|
|
16
|
+
|
|
17
|
+
export function securityHeaders(req, res, next) {
|
|
18
|
+
res.setHeader('X-Content-Type-Options', 'nosniff');
|
|
19
|
+
res.setHeader('Referrer-Policy', 'same-origin');
|
|
20
|
+
res.setHeader('X-Frame-Options', 'DENY');
|
|
21
|
+
if (req.path === '/index.html') res.setHeader('Content-Security-Policy', APP_CSP);
|
|
22
|
+
next();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// CSP торгового терминала (index.html). На лендинге и в админке есть встроенные скрипты — им CSP не ставится.
|
|
26
|
+
export function setAppCsp(res) {
|
|
27
|
+
res.setHeader('Content-Security-Policy', APP_CSP);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Запрос из браузера с чужого сайта: Origin есть и не совпадает с Host.
|
|
31
|
+
// Запросы без Origin (curl, боты, серверные клиенты API) не браузерные — их защищает авторизация.
|
|
32
|
+
export function isCrossOrigin(req) {
|
|
33
|
+
const origin = req.headers?.origin;
|
|
34
|
+
if (!origin || origin === 'null') return Boolean(origin);
|
|
35
|
+
try {
|
|
36
|
+
return new URL(origin).host.toLowerCase() !== String(req.headers?.host || '').toLowerCase();
|
|
37
|
+
} catch {
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
}
|