ваше сообщение коммита
This commit is contained in:
10
.gitignore
vendored
10
.gitignore
vendored
@@ -203,3 +203,13 @@ backend/test_*.js
|
|||||||
# Environment specific files
|
# Environment specific files
|
||||||
backend/.env.local
|
backend/.env.local
|
||||||
backend/.env.production
|
backend/.env.production
|
||||||
|
|
||||||
|
# Server management scripts - may contain sensitive information
|
||||||
|
scripts/clean-server.sh
|
||||||
|
scripts/export-template-for-release.sh
|
||||||
|
|
||||||
|
# Docker data and release archives - не коммитить в Git!
|
||||||
|
docker-data/
|
||||||
|
dle-template.tar.gz
|
||||||
|
dle-template.tar.gz.part-*
|
||||||
|
dle-template.tar.gz.join.sh
|
||||||
@@ -36,7 +36,8 @@ curl -fsSL https://raw.githubusercontent.com/VC-HB3-Accelerator/DLE/main/setup.s
|
|||||||
The script automatically downloads the latest artifacts from the release and deploys `docker-data`.
|
The script automatically downloads the latest artifacts from the release and deploys `docker-data`.
|
||||||
|
|
||||||
### 📦 Releases and artifacts
|
### 📦 Releases and artifacts
|
||||||
- [Release v1.0.0](https://github.com/VC-HB3-Accelerator/DLE/releases/tag/v1.0.0) — contains an archive split into parts (`dle-template.tar.gz.part-*`) that is installed together with the script.
|
- [Release v1.0.1](https://github.com/VC-HB3-Accelerator/DLE/releases/tag/v1.0.1) (Latest) — contains the complete application template with Docker images, volumes, and encryption key. Archive is split into parts (`dle-template.tar.gz.part-*`) for easy download.
|
||||||
|
- [Release v1.0.0](https://github.com/VC-HB3-Accelerator/DLE/releases/tag/v1.0.0) — previous version.
|
||||||
|
|
||||||
### Running the application
|
### Running the application
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -36,7 +36,8 @@ curl -fsSL https://raw.githubusercontent.com/VC-HB3-Accelerator/DLE/main/setup.s
|
|||||||
Скрипт автоматически скачивает последние артефакты из релиза и разворачивает `docker-data`.
|
Скрипт автоматически скачивает последние артефакты из релиза и разворачивает `docker-data`.
|
||||||
|
|
||||||
### 📦 Релизы и артефакты
|
### 📦 Релизы и артефакты
|
||||||
- [Релиз v1.0.0](https://github.com/VC-HB3-Accelerator/DLE/releases/tag/v1.0.0) — содержит архив, разбитый на части (`dle-template.tar.gz.part-*`), который устанавливается вместе со скриптом.
|
- [Релиз v1.0.1](https://github.com/VC-HB3-Accelerator/DLE/releases/tag/v1.0.1) (Latest) — содержит полный шаблон приложения с Docker образами, томами и ключом шифрования. Архив разделен на части (`dle-template.tar.gz.part-*`) для удобства загрузки.
|
||||||
|
- [Релиз v1.0.0](https://github.com/VC-HB3-Accelerator/DLE/releases/tag/v1.0.0) — предыдущая версия.
|
||||||
|
|
||||||
### Запуск приложения
|
### Запуск приложения
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -88,6 +88,24 @@ router.get('/okved', async (req, res, next) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Сортировка кодов ОКВЭД по коду (правильная числовая сортировка для каждой части)
|
||||||
|
codes.sort((a, b) => {
|
||||||
|
// Разбиваем коды на части для правильной сортировки
|
||||||
|
const partsA = a.code.split('.').map(p => parseInt(p, 10));
|
||||||
|
const partsB = b.code.split('.').map(p => parseInt(p, 10));
|
||||||
|
|
||||||
|
// Сравниваем части по порядку численно
|
||||||
|
for (let i = 0; i < Math.max(partsA.length, partsB.length); i++) {
|
||||||
|
const partA = partsA[i] !== undefined ? partsA[i] : 0;
|
||||||
|
const partB = partsB[i] !== undefined ? partsB[i] : 0;
|
||||||
|
|
||||||
|
if (partA !== partB) {
|
||||||
|
return partA - partB;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
|
||||||
// Ограничиваем количество результатов для производительности
|
// Ограничиваем количество результатов для производительности
|
||||||
const limit = parseInt(req.query.limit) || 2000; // Увеличили лимит для полного списка
|
const limit = parseInt(req.query.limit) || 2000; // Увеличили лимит для полного списка
|
||||||
codes = codes.slice(0, limit);
|
codes = codes.slice(0, limit);
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ async function runMigrations() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Извлекаем только UP SQL
|
// Извлекаем только UP SQL
|
||||||
const sqlToExecute = fileContent.substring(upSqlStartIndex, upSqlEndIndex).trim();
|
let sqlToExecute = fileContent.substring(upSqlStartIndex, upSqlEndIndex).trim();
|
||||||
|
|
||||||
if (!sqlToExecute) {
|
if (!sqlToExecute) {
|
||||||
logger.warn(`Migration file ${file} has no executable UP SQL content. Skipping.`);
|
logger.warn(`Migration file ${file} has no executable UP SQL content. Skipping.`);
|
||||||
@@ -99,6 +99,38 @@ async function runMigrations() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
logger.info(`Executing UP migration from ${file}...`);
|
logger.info(`Executing UP migration from ${file}...`);
|
||||||
|
|
||||||
|
// Выделяем CREATE EXTENSION команды, которые должны выполняться вне транзакции
|
||||||
|
const extensionRegex = /CREATE\s+EXTENSION\s+IF\s+NOT\s+EXISTS\s+\w+;?/gi;
|
||||||
|
const extensionCommands = [];
|
||||||
|
sqlToExecute = sqlToExecute.replace(extensionRegex, (match) => {
|
||||||
|
extensionCommands.push(match.replace(/;?\s*$/, ''));
|
||||||
|
return ''; // Удаляем из основного SQL
|
||||||
|
});
|
||||||
|
|
||||||
|
// Выполняем CREATE EXTENSION команды вне транзакции
|
||||||
|
for (const extCmd of extensionCommands) {
|
||||||
|
try {
|
||||||
|
await pool.query(extCmd);
|
||||||
|
logger.info(`Extension command executed: ${extCmd}`);
|
||||||
|
} catch (error) {
|
||||||
|
// Игнорируем ошибку, если расширение уже установлено
|
||||||
|
if (!error.message.includes('already exists')) {
|
||||||
|
logger.warn(`Warning executing extension command: ${error.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Очищаем оставшийся SQL от пустых строк
|
||||||
|
sqlToExecute = sqlToExecute.trim();
|
||||||
|
|
||||||
|
// Если после удаления CREATE EXTENSION остался только пустой SQL, пропускаем транзакцию
|
||||||
|
if (!sqlToExecute) {
|
||||||
|
await pool.query('INSERT INTO migrations (name) VALUES ($1)', [file]);
|
||||||
|
logger.info(`Migration ${file} executed successfully (extension only)`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
await pool.query('BEGIN');
|
await pool.query('BEGIN');
|
||||||
try {
|
try {
|
||||||
// Создаем функцию для получения ключа шифрования
|
// Создаем функцию для получения ключа шифрования
|
||||||
|
|||||||
@@ -1320,6 +1320,24 @@ const fetchOkvedCodes = async (level, parentCode, optionsRef, loadingRef) => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Сортировка кодов ОКВЭД по коду (правильная числовая сортировка для каждой части)
|
||||||
|
filteredCodes.sort((a, b) => {
|
||||||
|
// Разбиваем коды на части для правильной сортировки
|
||||||
|
const partsA = a.code.split('.').map(p => parseInt(p, 10));
|
||||||
|
const partsB = b.code.split('.').map(p => parseInt(p, 10));
|
||||||
|
|
||||||
|
// Сравниваем части по порядку численно
|
||||||
|
for (let i = 0; i < Math.max(partsA.length, partsB.length); i++) {
|
||||||
|
const partA = partsA[i] !== undefined ? partsA[i] : 0;
|
||||||
|
const partB = partsB[i] !== undefined ? partsB[i] : 0;
|
||||||
|
|
||||||
|
if (partA !== partB) {
|
||||||
|
return partA - partB;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
|
||||||
optionsRef.value = filteredCodes.map(code => ({
|
optionsRef.value = filteredCodes.map(code => ({
|
||||||
value: code.code,
|
value: code.code,
|
||||||
text: `${code.code} - ${code.title}`
|
text: `${code.code} - ${code.title}`
|
||||||
|
|||||||
@@ -23,12 +23,34 @@ fi
|
|||||||
ENCRYPTION_KEY=$(cat ./ssl/keys/full_db_encryption.key)
|
ENCRYPTION_KEY=$(cat ./ssl/keys/full_db_encryption.key)
|
||||||
|
|
||||||
# Создаем роли Read-Only и Editor
|
# Создаем роли Read-Only и Editor
|
||||||
|
# Используем DO блок для безопасной вставки с проверкой уникальности name_encrypted
|
||||||
docker exec dapp-postgres psql -U dapp_user -d dapp_db -c "
|
docker exec dapp-postgres psql -U dapp_user -d dapp_db -c "
|
||||||
INSERT INTO roles (id, name_encrypted) VALUES
|
DO \$\$
|
||||||
(1, encrypt_text('readonly', '$ENCRYPTION_KEY')),
|
DECLARE
|
||||||
(2, encrypt_text('editor', '$ENCRYPTION_KEY'))
|
readonly_id INTEGER;
|
||||||
ON CONFLICT (id) DO UPDATE SET
|
editor_id INTEGER;
|
||||||
name_encrypted = EXCLUDED.name_encrypted;"
|
BEGIN
|
||||||
|
-- Проверяем и создаем/обновляем роль readonly
|
||||||
|
SELECT id INTO readonly_id FROM roles WHERE decrypt_text(name_encrypted, get_encryption_key()) = 'readonly' LIMIT 1;
|
||||||
|
IF readonly_id IS NULL THEN
|
||||||
|
-- Если роли нет, пытаемся вставить с id=1, если занято - используем следующий доступный
|
||||||
|
BEGIN
|
||||||
|
INSERT INTO roles (id, name_encrypted) VALUES (1, encrypt_text('readonly', get_encryption_key()));
|
||||||
|
EXCEPTION WHEN unique_violation THEN
|
||||||
|
INSERT INTO roles (name_encrypted) VALUES (encrypt_text('readonly', get_encryption_key()));
|
||||||
|
END;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
-- Проверяем и создаем/обновляем роль editor
|
||||||
|
SELECT id INTO editor_id FROM roles WHERE decrypt_text(name_encrypted, get_encryption_key()) = 'editor' LIMIT 1;
|
||||||
|
IF editor_id IS NULL THEN
|
||||||
|
BEGIN
|
||||||
|
INSERT INTO roles (id, name_encrypted) VALUES (2, encrypt_text('editor', get_encryption_key()));
|
||||||
|
EXCEPTION WHEN unique_violation THEN
|
||||||
|
INSERT INTO roles (name_encrypted) VALUES (encrypt_text('editor', get_encryption_key()));
|
||||||
|
END;
|
||||||
|
END IF;
|
||||||
|
END \$\$;"
|
||||||
|
|
||||||
# Заполняем справочную таблицу is_rag_source
|
# Заполняем справочную таблицу is_rag_source
|
||||||
docker exec dapp-postgres psql -U dapp_user -d dapp_db -c "
|
docker exec dapp-postgres psql -U dapp_user -d dapp_db -c "
|
||||||
@@ -38,17 +60,87 @@ INSERT INTO is_rag_source (id, name_encrypted) VALUES
|
|||||||
ON CONFLICT (id) DO UPDATE SET
|
ON CONFLICT (id) DO UPDATE SET
|
||||||
name_encrypted = EXCLUDED.name_encrypted;"
|
name_encrypted = EXCLUDED.name_encrypted;"
|
||||||
|
|
||||||
|
# Заполняем RPC провайдеры с проверкой дубликатов
|
||||||
docker exec dapp-postgres psql -U dapp_user -d dapp_db -c "
|
docker exec dapp-postgres psql -U dapp_user -d dapp_db -c "
|
||||||
|
DO \$\$
|
||||||
|
BEGIN
|
||||||
|
-- Sepolia
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1 FROM rpc_providers
|
||||||
|
WHERE decrypt_text(network_id_encrypted, get_encryption_key()) = 'sepolia'
|
||||||
|
AND chain_id = 11155111
|
||||||
|
) THEN
|
||||||
INSERT INTO rpc_providers (network_id_encrypted, rpc_url_encrypted, chain_id)
|
INSERT INTO rpc_providers (network_id_encrypted, rpc_url_encrypted, chain_id)
|
||||||
VALUES
|
VALUES (encrypt_text('sepolia', get_encryption_key()), encrypt_text('https://1rpc.io/sepolia', get_encryption_key()), 11155111);
|
||||||
(encrypt_text('sepolia', '$ENCRYPTION_KEY'), encrypt_text('https://1rpc.io/sepolia', '$ENCRYPTION_KEY'), 11155111),
|
END IF;
|
||||||
(encrypt_text('arbitrum-sepolia', '$ENCRYPTION_KEY'), encrypt_text('https://sepolia-rollup.arbitrum.io/rpc', '$ENCRYPTION_KEY'), 421614),
|
|
||||||
(encrypt_text('base-sepolia', '$ENCRYPTION_KEY'), encrypt_text('https://sepolia.base.org', '$ENCRYPTION_KEY'), 84532)
|
-- Arbitrum Sepolia
|
||||||
ON CONFLICT DO NOTHING;"
|
IF NOT EXISTS (
|
||||||
|
SELECT 1 FROM rpc_providers
|
||||||
|
WHERE decrypt_text(network_id_encrypted, get_encryption_key()) = 'arbitrum-sepolia'
|
||||||
|
AND chain_id = 421614
|
||||||
|
) THEN
|
||||||
|
INSERT INTO rpc_providers (network_id_encrypted, rpc_url_encrypted, chain_id)
|
||||||
|
VALUES (encrypt_text('arbitrum-sepolia', get_encryption_key()), encrypt_text('https://sepolia-rollup.arbitrum.io/rpc', get_encryption_key()), 421614);
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
-- Base Sepolia
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1 FROM rpc_providers
|
||||||
|
WHERE decrypt_text(network_id_encrypted, get_encryption_key()) = 'base-sepolia'
|
||||||
|
AND chain_id = 84532
|
||||||
|
) THEN
|
||||||
|
INSERT INTO rpc_providers (network_id_encrypted, rpc_url_encrypted, chain_id)
|
||||||
|
VALUES (encrypt_text('base-sepolia', get_encryption_key()), encrypt_text('https://sepolia.base.org', get_encryption_key()), 84532);
|
||||||
|
END IF;
|
||||||
|
END \$\$;"
|
||||||
|
|
||||||
|
# Заполняем токены аутентификации с проверкой дубликатов
|
||||||
docker exec dapp-postgres psql -U dapp_user -d dapp_db -c "
|
docker exec dapp-postgres psql -U dapp_user -d dapp_db -c "
|
||||||
|
DO \$\$
|
||||||
|
BEGIN
|
||||||
|
-- Sepolia token
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1 FROM auth_tokens
|
||||||
|
WHERE decrypt_text(network_encrypted, get_encryption_key()) = 'sepolia'
|
||||||
|
AND decrypt_text(address_encrypted, get_encryption_key()) = '0xdD27a91692da59d1Ee7dD1Fb342B9f1B5FF29386'
|
||||||
|
) THEN
|
||||||
INSERT INTO auth_tokens (name_encrypted, address_encrypted, network_encrypted, min_balance, readonly_threshold, editor_threshold)
|
INSERT INTO auth_tokens (name_encrypted, address_encrypted, network_encrypted, min_balance, readonly_threshold, editor_threshold)
|
||||||
VALUES
|
VALUES (
|
||||||
(encrypt_text('DLE', '$ENCRYPTION_KEY'), encrypt_text('0xdD27a91692da59d1Ee7dD1Fb342B9f1B5FF29386', '$ENCRYPTION_KEY'), encrypt_text('sepolia', '$ENCRYPTION_KEY'), 1.000000000000000000, 1, 1),
|
encrypt_text('DLE', get_encryption_key()),
|
||||||
(encrypt_text('DLE', '$ENCRYPTION_KEY'), encrypt_text('0xdD27a91692da59d1Ee7dD1Fb342B9f1B5FF29386', '$ENCRYPTION_KEY'), encrypt_text('arbitrum-sepolia', '$ENCRYPTION_KEY'), 1.000000000000000000, 1, 1),
|
encrypt_text('0xdD27a91692da59d1Ee7dD1Fb342B9f1B5FF29386', get_encryption_key()),
|
||||||
(encrypt_text('DLE', '$ENCRYPTION_KEY'), encrypt_text('0xdD27a91692da59d1Ee7dD1Fb342B9f1B5FF29386', '$ENCRYPTION_KEY'), encrypt_text('base-sepolia', '$ENCRYPTION_KEY'), 1.000000000000000000, 1, 1)
|
encrypt_text('sepolia', get_encryption_key()),
|
||||||
ON CONFLICT DO NOTHING;"
|
1.000000000000000000, 1, 1
|
||||||
|
);
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
-- Arbitrum Sepolia token
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1 FROM auth_tokens
|
||||||
|
WHERE decrypt_text(network_encrypted, get_encryption_key()) = 'arbitrum-sepolia'
|
||||||
|
AND decrypt_text(address_encrypted, get_encryption_key()) = '0xdD27a91692da59d1Ee7dD1Fb342B9f1B5FF29386'
|
||||||
|
) THEN
|
||||||
|
INSERT INTO auth_tokens (name_encrypted, address_encrypted, network_encrypted, min_balance, readonly_threshold, editor_threshold)
|
||||||
|
VALUES (
|
||||||
|
encrypt_text('DLE', get_encryption_key()),
|
||||||
|
encrypt_text('0xdD27a91692da59d1Ee7dD1Fb342B9f1B5FF29386', get_encryption_key()),
|
||||||
|
encrypt_text('arbitrum-sepolia', get_encryption_key()),
|
||||||
|
1.000000000000000000, 1, 1
|
||||||
|
);
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
-- Base Sepolia token
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1 FROM auth_tokens
|
||||||
|
WHERE decrypt_text(network_encrypted, get_encryption_key()) = 'base-sepolia'
|
||||||
|
AND decrypt_text(address_encrypted, get_encryption_key()) = '0xdD27a91692da59d1Ee7dD1Fb342B9f1B5FF29386'
|
||||||
|
) THEN
|
||||||
|
INSERT INTO auth_tokens (name_encrypted, address_encrypted, network_encrypted, min_balance, readonly_threshold, editor_threshold)
|
||||||
|
VALUES (
|
||||||
|
encrypt_text('DLE', get_encryption_key()),
|
||||||
|
encrypt_text('0xdD27a91692da59d1Ee7dD1Fb342B9f1B5FF29386', get_encryption_key()),
|
||||||
|
encrypt_text('base-sepolia', get_encryption_key()),
|
||||||
|
1.000000000000000000, 1, 1
|
||||||
|
);
|
||||||
|
END IF;
|
||||||
|
END \$\$;"
|
||||||
|
|||||||
30
setup.sh
30
setup.sh
@@ -24,14 +24,13 @@ print_red() {
|
|||||||
echo -e "\e[31m$1\e[0m"
|
echo -e "\e[31m$1\e[0m"
|
||||||
}
|
}
|
||||||
|
|
||||||
ARCHIVE_VERSION="v1.0.0"
|
ARCHIVE_VERSION="v1.0.1"
|
||||||
ARCHIVE_BASE_URL="https://github.com/VC-HB3-Accelerator/DLE/releases/download/${ARCHIVE_VERSION}"
|
ARCHIVE_BASE_URL="https://github.com/VC-HB3-Accelerator/DLE/releases/download/${ARCHIVE_VERSION}"
|
||||||
ARCHIVE_PARTS=(
|
ARCHIVE_PARTS=(
|
||||||
"dle-template.tar.gz.part-aa"
|
"dle-template.tar.gz.part-aa"
|
||||||
"dle-template.tar.gz.part-ab"
|
"dle-template.tar.gz.part-ab"
|
||||||
"dle-template.tar.gz.part-ac"
|
"dle-template.tar.gz.part-ac"
|
||||||
"dle-template.tar.gz.part-ad"
|
"dle-template.tar.gz.part-ad"
|
||||||
"dle-template.tar.gz.part-ae"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Проверка curl
|
# Проверка curl
|
||||||
@@ -263,6 +262,30 @@ import_volumes() {
|
|||||||
print_green "✅ Все тома импортированы"
|
print_green "✅ Все тома импортированы"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Копирование ключа шифрования из архива
|
||||||
|
copy_encryption_key() {
|
||||||
|
print_blue "🔐 Копирование ключа шифрования..."
|
||||||
|
|
||||||
|
# Проверяем наличие ключа в архиве
|
||||||
|
if [ -f "docker-data/ssl/keys/full_db_encryption.key" ]; then
|
||||||
|
# Создаем директорию для ключа
|
||||||
|
mkdir -p ssl/keys
|
||||||
|
|
||||||
|
# Копируем ключ
|
||||||
|
cp docker-data/ssl/keys/full_db_encryption.key ssl/keys/full_db_encryption.key
|
||||||
|
chmod 600 ssl/keys/full_db_encryption.key
|
||||||
|
|
||||||
|
print_green "✅ Ключ шифрования скопирован из архива"
|
||||||
|
print_yellow "⚠️ Примечание: Это дефолтный ключ, замените его на свой!"
|
||||||
|
else
|
||||||
|
print_yellow "⚠️ Ключ шифрования не найден в архиве"
|
||||||
|
print_blue "Создайте новый ключ или он будет создан автоматически"
|
||||||
|
|
||||||
|
# Создаем директорию для ключа на всякий случай
|
||||||
|
mkdir -p ssl/keys
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
# Запуск приложения
|
# Запуск приложения
|
||||||
start_application() {
|
start_application() {
|
||||||
print_blue "🚀 Запуск приложения..."
|
print_blue "🚀 Запуск приложения..."
|
||||||
@@ -314,6 +337,9 @@ main() {
|
|||||||
create_volumes
|
create_volumes
|
||||||
import_volumes
|
import_volumes
|
||||||
|
|
||||||
|
# Копирование ключа шифрования
|
||||||
|
copy_encryption_key
|
||||||
|
|
||||||
# Запуск
|
# Запуск
|
||||||
start_application
|
start_application
|
||||||
check_status
|
check_status
|
||||||
|
|||||||
Reference in New Issue
Block a user