85 lines
2.9 KiB
Python
85 lines
2.9 KiB
Python
import argparse
|
|
import hashlib
|
|
import os
|
|
from pathlib import Path
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
|
|
|
|
ROOT = Path(__file__).resolve().parent
|
|
BACKEND = ROOT / "backend"
|
|
FRONTEND = ROOT / "frontend"
|
|
VENV = ROOT / ".venv"
|
|
|
|
|
|
def install_dependencies(
|
|
inputs: tuple[Path, ...], stamp: Path, command: list[str], cwd: Path
|
|
) -> None:
|
|
fingerprint = hashlib.sha256()
|
|
for path in inputs:
|
|
fingerprint.update(hashlib.sha256(path.read_bytes()).digest())
|
|
current = fingerprint.hexdigest()
|
|
if stamp.is_file() and stamp.read_text(encoding="ascii") == current:
|
|
return
|
|
|
|
stamp.unlink(missing_ok=True)
|
|
subprocess.run(command, cwd=cwd, check=True)
|
|
stamp.write_text(current, encoding="ascii")
|
|
|
|
|
|
def main() -> int:
|
|
parser = argparse.ArgumentParser(description="Локальный запуск AI-заметок.")
|
|
parser.add_argument("--port", type=int, default=8000, help="Порт приложения (по умолчанию 8000).")
|
|
args = parser.parse_args()
|
|
if not 1 <= args.port <= 65535:
|
|
parser.error("Порт должен быть от 1 до 65535.")
|
|
if sys.version_info < (3, 11):
|
|
parser.exit(1, "Установите Python 3.11 или новее.\n")
|
|
|
|
npm = shutil.which("npm.cmd" if os.name == "nt" else "npm")
|
|
if not npm or not shutil.which("node"):
|
|
parser.exit(1, "Установите Node.js с npm и заново откройте терминал.\n")
|
|
|
|
env_file = ROOT / ".env"
|
|
if not env_file.exists():
|
|
shutil.copyfile(ROOT / ".env.example", env_file)
|
|
print("Создан .env. Для генерации добавьте ключ Groq в этот файл.", flush=True)
|
|
|
|
python = VENV / ("Scripts/python.exe" if os.name == "nt" else "bin/python")
|
|
if not python.is_file():
|
|
subprocess.run([sys.executable, "-m", "venv", str(VENV)], check=True)
|
|
|
|
install_dependencies(
|
|
(BACKEND / "requirements.txt",),
|
|
VENV / ".notes-dependencies",
|
|
[str(python), "-m", "pip", "install", "-r", "requirements.txt"],
|
|
BACKEND,
|
|
)
|
|
install_dependencies(
|
|
(FRONTEND / "package.json", FRONTEND / "package-lock.json"),
|
|
FRONTEND / "node_modules" / ".notes-dependencies",
|
|
[npm, "ci"],
|
|
FRONTEND,
|
|
)
|
|
subprocess.run([npm, "run", "build"], cwd=FRONTEND, check=True)
|
|
|
|
return subprocess.call(
|
|
[
|
|
str(python), "-m", "uvicorn", "app.main:app",
|
|
"--env-file", str(env_file), "--host", "127.0.0.1", "--port", str(args.port),
|
|
],
|
|
cwd=BACKEND,
|
|
env={**os.environ, "NOTES_WEB_DIST": str(FRONTEND / "dist")},
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
try:
|
|
raise SystemExit(main())
|
|
except subprocess.CalledProcessError as error:
|
|
print("Запуск остановлен. Причина указана выше.", file=sys.stderr)
|
|
raise SystemExit(error.returncode) from None
|
|
except KeyboardInterrupt:
|
|
raise SystemExit(130) from None
|