Compare commits

...

14 Commits

Author SHA1 Message Date
f47b280634 Обновить .gitattributes
All checks were successful
CI / backend (push) Successful in 51s
CI / frontend (push) Successful in 4s
2025-12-07 08:18:31 +00:00
67db5c7a11 CI тесты
All checks were successful
CI / backend (push) Successful in 9s
CI / frontend (push) Successful in 4s
2025-12-01 11:57:02 +00:00
4a75e30671 Утилита декордирования
Some checks failed
CI / backend (push) Failing after 12s
CI / frontend (push) Failing after 4s
2025-12-01 11:52:57 +00:00
68649ba214 Генерация лицензии Custom.mxtpro
Some checks failed
CI / backend (push) Failing after 9s
CI / frontend (push) Failing after 4s
2025-12-01 11:51:00 +00:00
b052e82358 Точка входа backend API
Some checks failed
CI / backend (push) Failing after 9s
CI / frontend (push) Failing after 4s
2025-12-01 11:50:10 +00:00
667d3c205a Создание временной лицензии
Some checks failed
CI / backend (push) Failing after 8s
CI / frontend (push) Failing after 3s
2025-12-01 11:47:45 +00:00
adbf40240c Утилита кодировки
Some checks failed
CI / backend (push) Failing after 8s
CI / frontend (push) Failing after 4s
2025-12-01 11:47:20 +00:00
6232b79ffa Улучшение генератора
Some checks failed
CI / backend (push) Failing after 8s
CI / frontend (push) Failing after 3s
2025-12-01 11:45:18 +00:00
043526229e Инициализация пакета python
Some checks failed
CI / backend (push) Failing after 9s
CI / frontend (push) Failing after 4s
2025-12-01 11:43:42 +00:00
98e0f594df Инициализация пакета python
Some checks failed
CI / backend (push) Failing after 8s
CI / frontend (push) Has been cancelled
2025-12-01 11:43:29 +00:00
2be258380f Инициализация пакета python
Some checks failed
CI / backend (push) Failing after 9s
CI / frontend (push) Failing after 4s
2025-12-01 11:43:13 +00:00
c6a8d7c804 Основная точка входа backend API
Some checks failed
CI / backend (push) Failing after 8s
CI / frontend (push) Failing after 4s
2025-12-01 11:42:36 +00:00
06f0e5eb86 Точка входа backend API
Some checks failed
CI / backend (push) Failing after 9s
CI / frontend (push) Failing after 3s
2025-12-01 11:39:23 +00:00
5402524456 Настройка конфигурации API
Some checks failed
CI / backend (push) Failing after 9s
CI / frontend (push) Failing after 4s
2025-12-01 11:38:02 +00:00
10 changed files with 63 additions and 38 deletions

4
.gitattributes vendored
View File

@@ -119,8 +119,8 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
*.html linguist-language=HTML *.html linguist-language=HTML
*.css linguist-language=CSS *.css linguist-language=CSS
*.js linguist-language=JavaScript *.js linguist-language=JavaScript
*.json linguist-language=JSON #*.json linguist-language=JSON
*.md linguist-language=Markdown #*.md linguist-language=Markdown
*.yml linguist-language=YAML *.yml linguist-language=YAML
*.yaml linguist-language=YAML *.yaml linguist-language=YAML
*.c linguist-language=C *.c linguist-language=C

View File

@@ -12,44 +12,32 @@ jobs:
backend: backend:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
# 1. Клонируем репозиторий
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v3 uses: actions/checkout@v3
# 2. Настройка Python
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v4 uses: actions/setup-python@v4
with: with:
python-version: '3.11' python-version: '3.11'
# 3. Установка зависимостей
- name: Install dependencies - name: Install dependencies
run: | run: |
python -m pip install --upgrade pip python -m pip install --upgrade pip
pip install -r backend/requirements.txt pip install -r backend/requirements.txt
# 4. Проверка синтаксиса Python
- name: Lint backend - name: Lint backend
run: | run: |
pip install flake8 pip install flake8
flake8 backend flake8 backend
# 5. Запуск тестов (если есть)
- name: Run backend tests
run: |
pip install pytest
pytest backend
frontend: frontend:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
# 1. Клонируем репозиторий
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v3 uses: actions/checkout@v3
# 2. Проверка наличия основных файлов
- name: Check frontend files - name: Check frontend files
run: | run: |
test -f frontend/index.html test -f frontend/templates/index.html
test -f frontend/css/style.css test -f frontend/css/style.css
test -f frontend/js/main.js test -f frontend/js/main.js

View File

@@ -3,6 +3,7 @@ from dotenv import load_dotenv
load_dotenv() load_dotenv()
class Config: class Config:
""" """
Конфигурация FastAPI приложения. Конфигурация FastAPI приложения.

View File

@@ -4,11 +4,12 @@ from fastapi.responses import FileResponse
from pathlib import Path from pathlib import Path
# Импорт роутера # Импорт роутера
from .routes.license_routes import router as license_router # Изменено: один dot вместо двух from .routes.license_routes import router as license_router
# Путь к корню проекта # Путь к корню проекта
BASE_DIR = Path(__file__).resolve().parent BASE_DIR = Path(__file__).resolve().parent
def create_app() -> FastAPI: def create_app() -> FastAPI:
""" """
Создание и конфигурация FastAPI приложения. Создание и конфигурация FastAPI приложения.
@@ -24,9 +25,21 @@ def create_app() -> FastAPI:
apps.include_router(license_router, prefix="/api") apps.include_router(license_router, prefix="/api")
# Монтируем статические папки фронтенда # Монтируем статические папки фронтенда
apps.mount("/static/css", StaticFiles(directory=BASE_DIR.parent / "frontend" / "css"), name="css") apps.mount(
apps.mount("/static/js", StaticFiles(directory=BASE_DIR.parent / "frontend" / "js"), name="js") "/static/css",
apps.mount("/static/assets", StaticFiles(directory=BASE_DIR.parent / "frontend" / "assets"), name="assets") StaticFiles(directory=BASE_DIR.parent / "frontend" / "css"),
name="css"
)
apps.mount(
"/static/js",
StaticFiles(directory=BASE_DIR.parent / "frontend" / "js"),
name="js"
)
apps.mount(
"/static/assets",
StaticFiles(directory=BASE_DIR.parent / "frontend" / "assets"),
name="assets"
)
# Отдача главной страницы # Отдача главной страницы
@apps.get("/", include_in_schema=False) @apps.get("/", include_in_schema=False)
@@ -34,8 +47,11 @@ def create_app() -> FastAPI:
""" """
Отдает главную страницу сайта (index.html) Отдает главную страницу сайта (index.html)
""" """
return FileResponse(BASE_DIR.parent / "frontend" / "templates" / "index.html") index_file = BASE_DIR.parent / "frontend" / "templates" / "index.html"
return FileResponse(index_file)
return apps return apps
# Экземпляр приложения # Экземпляр приложения
app: FastAPI = create_app() app: FastAPI = create_app()

View File

@@ -1 +0,0 @@
from .license_routes import *

View File

@@ -5,6 +5,7 @@ from ..services.license_service import generate_license_file
router: APIRouter = APIRouter() router: APIRouter = APIRouter()
@router.post("/generate", response_class=FileResponse) @router.post("/generate", response_class=FileResponse)
async def generate_license( async def generate_license(
background_tasks: BackgroundTasks, background_tasks: BackgroundTasks,
@@ -20,17 +21,27 @@ async def generate_license(
- FileResponse: сгенерированный ZIP файл с именем Custom.mxtpro - FileResponse: сгенерированный ZIP файл с именем Custom.mxtpro
""" """
if not name.strip() or not version.strip(): if not name.strip() or not version.strip():
raise HTTPException(status_code=400, detail="NAME и VERSION обязательны.") raise HTTPException(
status_code=400,
detail="NAME и VERSION обязательны."
)
try: try:
# Создаём временный файл на сервере # Создаём временный файл на сервере
filepath, temp_dir = generate_license_file(name, version) filepath, temp_dir = generate_license_file(name, version)
except Exception as e: except Exception as e:
raise HTTPException(status_code=500, detail=f"Ошибка генерации лицензии: {e}") raise HTTPException(
status_code=500,
detail=f"Ошибка генерации лицензии: {e}"
)
# Удаляем директорию (и файл внутри) после отправки пользователю # Удаляем директорию (и файл внутри) после отправки пользователю
background_tasks.add_task(temp_dir.cleanup) background_tasks.add_task(temp_dir.cleanup)
# Отправляем с фиксированным именем Custom.mxtpro # Отправляем с фиксированным именем Custom.mxtpro
return FileResponse( return FileResponse(
filepath, filepath,
media_type="application/octet-stream", media_type="application/octet-stream",
headers={"Content-Disposition": 'attachment; filename="Custom.mxtpro"'} headers={
"Content-Disposition": 'attachment; filename="Custom.mxtpro"'
}
) )

View File

@@ -1 +0,0 @@
from .license_service import *

View File

@@ -4,11 +4,13 @@ from pathlib import Path
from ..utils.encoding import variant_base64_encode from ..utils.encoding import variant_base64_encode
from ..utils.crypto import encrypt_bytes from ..utils.crypto import encrypt_bytes
class LicenseType: class LicenseType:
Professional: int = 1 Professional: int = 1
Educational: int = 3 Educational: int = 3
Personal: int = 4 Personal: int = 4
def generate_license_file( def generate_license_file(
user_name: str, user_name: str,
version: str, version: str,
@@ -26,11 +28,13 @@ def generate_license_file(
minor_version: int = int(minor_str) minor_version: int = int(minor_str)
except Exception as e: except Exception as e:
raise ValueError(f"Неверный формат версии: {version}") from e raise ValueError(f"Неверный формат версии: {version}") from e
# Формирование строки лицензии (исправлено: убрали лишний #, интегрировали цифры в один блок)
# Формирование строки лицензии
license_str: str = ( license_str: str = (
f"{lic_type}#{user_name}|{major_version}{minor_version}" f"{lic_type}#{user_name}|{major_version}{minor_version}"
f"#{count}#{major_version}3{minor_version}6{minor_version}#0#0#0#" f"#{count}#{major_version}3{minor_version}6{minor_version}#0#0#0#"
) )
# Шифрование и кодирование # Шифрование и кодирование
encrypted: bytes = encrypt_bytes(0x0787, license_str.encode("utf-8")) encrypted: bytes = encrypt_bytes(0x0787, license_str.encode("utf-8"))
encoded: str = variant_base64_encode(encrypted).decode("ascii") encoded: str = variant_base64_encode(encrypted).decode("ascii")
@@ -40,4 +44,5 @@ def generate_license_file(
filepath = Path(temp_dir.name) / "Custom.mxtpro" filepath = Path(temp_dir.name) / "Custom.mxtpro"
with ZipFile(filepath, "w") as zf: with ZipFile(filepath, "w") as zf:
zf.writestr("Pro.key", encoded) zf.writestr("Pro.key", encoded)
return str(filepath), temp_dir return str(filepath), temp_dir

View File

@@ -1,2 +0,0 @@
from .crypto import *
from .encoding import *

View File

@@ -1,9 +1,15 @@
from typing import Dict from typing import Dict
# Таблица Base64-подобного варианта # Таблица Base64-подобного варианта
VariantBase64Table: str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=" VariantBase64Table: str = (
VariantBase64Dict: Dict[int, str] = {i: VariantBase64Table[i] for i in range(len(VariantBase64Table))} "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
VariantBase64ReverseDict: Dict[str, int] = {VariantBase64Table[i]: i for i in range(len(VariantBase64Table))} )
VariantBase64Dict: Dict[int, str] = {
i: VariantBase64Table[i] for i in range(len(VariantBase64Table))
}
VariantBase64ReverseDict: Dict[str, int] = {
VariantBase64Table[i]: i for i in range(len(VariantBase64Table))
}
def variant_base64_encode(bs: bytes) -> bytes: def variant_base64_encode(bs: bytes) -> bytes:
@@ -24,7 +30,8 @@ def variant_base64_encode(bs: bytes) -> bytes:
b: bytes = bs[3*i:3*i+3] b: bytes = bs[3*i:3*i+3]
coding_int: int = int.from_bytes(b, "little") coding_int: int = int.from_bytes(b, "little")
block: str = "".join( block: str = "".join(
VariantBase64Dict[(coding_int >> shift) & 0x3F] for shift in (0, 6, 12, 18) VariantBase64Dict[(coding_int >> shift) & 0x3F]
for shift in (0, 6, 12, 18)
) )
result.extend(block.encode("ascii")) result.extend(block.encode("ascii"))
@@ -33,7 +40,8 @@ def variant_base64_encode(bs: bytes) -> bytes:
b: bytes = bs[-left_bytes:] b: bytes = bs[-left_bytes:]
coding_int: int = int.from_bytes(b, "little") coding_int: int = int.from_bytes(b, "little")
block: str = "".join( block: str = "".join(
VariantBase64Dict[(coding_int >> shift) & 0x3F] for shift in range(0, left_bytes*8 + 1, 6) VariantBase64Dict[(coding_int >> shift) & 0x3F]
for shift in range(0, left_bytes * 8 + 1, 6)
) )
result.extend(block.encode("ascii")) result.extend(block.encode("ascii"))