All checks were successful
Тест-фича для МИНа. Полное описание в rbxl-importer/INFO_PROCESS.md. Backend (rbxl-importer/ на VM 130 S1): - Python-парсер Roblox Binary (28+ типов значений) - Asset downloader через Marfusha proxy + .ROBLOSECURITY cookie - Mesh→GLB конвертер (v1-v5) - Converter Roblox-классов → project_data - Flask API: /analyze + /create Frontend: - API.js + components/RbxlImportModal.jsx (drag-n-drop) Тестовый импорт Easy Obby: project_id 2697, 2244 primitives + 742 lua-scripts + 5 ассетов. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
92 lines
4.2 KiB
PL/PgSQL
92 lines
4.2 KiB
PL/PgSQL
-- Миграция 001: таблица roblox_assets для кеширования assets Roblox CDN
|
||
--
|
||
-- Применяется к storys_db.
|
||
-- Запускать на S2 VM 117 (service-storys, primary PG).
|
||
-- При репликации S2 → S1 миграция доедет автоматически.
|
||
|
||
BEGIN;
|
||
|
||
CREATE TABLE IF NOT EXISTS roblox_assets (
|
||
-- ID ассета в Roblox (числовой, из rbxassetid://<id>)
|
||
rbx_asset_id BIGINT PRIMARY KEY,
|
||
|
||
-- SHA256 сырого файла (после скачки с CDN, до конверта)
|
||
-- Используется для второй дедупликации: разные rbx_asset_id могут указывать
|
||
-- на одинаковый файл (Roblox делает редиректы).
|
||
sha256_raw CHAR(64) NOT NULL,
|
||
|
||
-- Тип ассета: 'mesh', 'texture', 'sound', 'csg', 'animation', 'video', 'unknown'
|
||
asset_kind VARCHAR(16) NOT NULL,
|
||
|
||
-- Content-Type как пришёл с CDN
|
||
content_type VARCHAR(64) NOT NULL,
|
||
|
||
-- Размер сырого файла
|
||
raw_size_bytes BIGINT NOT NULL,
|
||
|
||
-- Путь сырого файла в /opt/roblox-assets/raw/<sha256>.bin (или конкретное расширение)
|
||
raw_path TEXT NOT NULL,
|
||
|
||
-- Если делали конверт (mesh→glb, csg→glb) — путь и хеш конвертированного файла.
|
||
-- Для остальных типов = NULL.
|
||
converted_path TEXT,
|
||
converted_sha256 CHAR(64),
|
||
converted_size_bytes BIGINT,
|
||
|
||
-- URL по которому ассет реально отдаётся юзеру (https://assets.rublox.pro/...)
|
||
public_url TEXT NOT NULL,
|
||
|
||
-- Время первой скачки
|
||
downloaded_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||
|
||
-- Последнее использование (бампается при каждом импорте новой карты)
|
||
last_used_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||
|
||
-- Сколько карт использует этот ассет (для cleanup'а)
|
||
refcount INTEGER NOT NULL DEFAULT 0,
|
||
|
||
-- Метаданные конкретного типа (mesh: vertex count, texture: dimensions)
|
||
metadata JSONB DEFAULT '{}'::jsonb,
|
||
|
||
-- Failed reason если скачка/конверт упал
|
||
error_msg TEXT
|
||
);
|
||
|
||
CREATE INDEX IF NOT EXISTS idx_roblox_assets_sha256 ON roblox_assets(sha256_raw);
|
||
CREATE INDEX IF NOT EXISTS idx_roblox_assets_kind ON roblox_assets(asset_kind);
|
||
CREATE INDEX IF NOT EXISTS idx_roblox_assets_last_used ON roblox_assets(last_used_at);
|
||
|
||
-- Лог скачек по проектам (для отладки и tracking'а кто чем пользуется)
|
||
CREATE TABLE IF NOT EXISTS roblox_asset_usage (
|
||
id BIGSERIAL PRIMARY KEY,
|
||
project_id INTEGER NOT NULL,
|
||
rbx_asset_id BIGINT NOT NULL REFERENCES roblox_assets(rbx_asset_id) ON DELETE CASCADE,
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||
UNIQUE(project_id, rbx_asset_id)
|
||
);
|
||
|
||
CREATE INDEX IF NOT EXISTS idx_asset_usage_project ON roblox_asset_usage(project_id);
|
||
|
||
-- Лог импортов .rbxl (родительская запись для usage)
|
||
CREATE TABLE IF NOT EXISTS roblox_imports (
|
||
id BIGSERIAL PRIMARY KEY,
|
||
project_id INTEGER, -- может быть NULL если ещё не создан
|
||
user_id INTEGER NOT NULL, -- кто грузил (МИН только пока)
|
||
rbxl_filename TEXT NOT NULL,
|
||
rbxl_size BIGINT NOT NULL,
|
||
rbxl_sha256 CHAR(64) NOT NULL,
|
||
instance_count INTEGER,
|
||
class_count INTEGER,
|
||
assets_total INTEGER DEFAULT 0,
|
||
assets_failed INTEGER DEFAULT 0,
|
||
status VARCHAR(16) NOT NULL DEFAULT 'pending', -- pending/parsing/downloading/converting/done/failed
|
||
error_msg TEXT,
|
||
started_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||
finished_at TIMESTAMPTZ
|
||
);
|
||
|
||
CREATE INDEX IF NOT EXISTS idx_imports_user ON roblox_imports(user_id);
|
||
CREATE INDEX IF NOT EXISTS idx_imports_status ON roblox_imports(status);
|
||
|
||
COMMIT;
|