diff --git a/backend/README.md b/backend/README.md index 51aa3e4d..72b1c578 100644 --- a/backend/README.md +++ b/backend/README.md @@ -22,7 +22,7 @@ poe migrate To create a new admin user, you can now run: ```shell -poe create_user --user admin --pass admin +poe admin create_user --user admin --pass admin ``` Now you can start the development server with diff --git a/backend/pyproject.toml b/backend/pyproject.toml index eb8e00cf..06ce7374 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -44,6 +44,10 @@ notebooks = [ "seaborn~=0.12.2", ] +[project.scripts] +transcribee-migrate = "transcribee_backend.db.run_migrations:main" +transcribee-admin = "transcribee_backend.admin_cli:main" + [tool.uv] override-dependencies = [ "sqlalchemy==1.4.41" @@ -58,16 +62,13 @@ start = "uvicorn transcribee_backend.main:app --ws websockets" dev = "uvicorn transcribee_backend.main:app --reload --ws websockets" migrate = "alembic upgrade head" makemigrations = "alembic revision --autogenerate -m" -create_user = "scripts/create_user.py" -create_worker = "scripts/create_worker.py" -create_api_token = "scripts/create_api_token.py" -reset_task = "scripts/reset_task.py" +admin = "transcribee-admin" generate_openapi = "python -m scripts.generate_openapi" test = "pytest tests/" pyright = "pyright transcribee_backend/" -[tool.setuptools] -packages = ["transcribee_backend"] +[tool.setuptools.packages.find] +include = ["transcribee_backend*"] [build-system] requires = ["setuptools", "setuptools-scm"] diff --git a/backend/scripts/create_api_token.py b/backend/scripts/create_api_token.py deleted file mode 100755 index 2f02be92..00000000 --- a/backend/scripts/create_api_token.py +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env python - -import argparse - -from transcribee_backend.auth import create_api_token -from transcribee_backend.db import SessionContextManager - -if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument("--name", required=True) - args = parser.parse_args() - with SessionContextManager(path="management_command:create_api_token") as session: - token = create_api_token(session=session, name=args.name) - print(f"Token created: {token.token}") diff --git a/backend/scripts/create_user.py b/backend/scripts/create_user.py deleted file mode 100755 index c8566ca7..00000000 --- a/backend/scripts/create_user.py +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env python - -import argparse -import random -import string - -from transcribee_backend.auth import create_user -from transcribee_backend.db import SessionContextManager -from transcribee_backend.exceptions import UserAlreadyExists - - -def random_password(): - chars = string.ascii_lowercase + string.digits - return "".join(random.choice(chars) for _ in range(20)) - - -if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument("--user", required=True) - parser.add_argument("--password", required=False) - args = parser.parse_args() - - password = args.password - - if not password: - password = random_password() - - print("Auto-generated password.") - print("Infos to send to user:") - print() - print(f"Username: {args.user}") - print(f"Password: {password} (Please change)") - print() - - with SessionContextManager(path="management_command:create_user") as session: - try: - user = create_user(session=session, username=args.user, password=password) - print("User created") - except UserAlreadyExists: - print("Could not create user. A user with that name already exists.") diff --git a/backend/scripts/create_user_token.py b/backend/scripts/create_user_token.py deleted file mode 100755 index a1cd55b5..00000000 --- a/backend/scripts/create_user_token.py +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env python - -import argparse -import datetime - -from sqlmodel import select -from transcribee_backend.auth import generate_user_token -from transcribee_backend.db import SessionContextManager -from transcribee_backend.helpers.time import now_tz_aware -from transcribee_backend.models.user import User - -if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument("--username", required=True) - parser.add_argument("--valid-days", required=True) - args = parser.parse_args() - with SessionContextManager(path="management_command:create_user_token") as session: - valid_days = int(args.valid_days) - if valid_days < 0: - print("Valid days must be positive") - exit(1) - - valid_until = now_tz_aware() + datetime.timedelta(days=valid_days) - - user = session.exec( - select(User).where(User.username == args.username) - ).one_or_none() - - if user is None: - print(f"User {args.user} not found") - exit(1) - - key, user_token = generate_user_token(user, valid_until=valid_until) - session.add(user_token) - session.commit() - - print(f"User token created and valid until {valid_until}") - print(f"Secret: {key}") diff --git a/backend/scripts/create_worker.py b/backend/scripts/create_worker.py deleted file mode 100755 index 9a2dc3e0..00000000 --- a/backend/scripts/create_worker.py +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env python - -import argparse - -from sqlmodel import select -from transcribee_backend import utils -from transcribee_backend.db import SessionContextManager -from transcribee_backend.models import Worker - -if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument("--name", required=True) - parser.add_argument("--token", required=False) - args = parser.parse_args() - if args.token is None: - args.token = utils.get_random_string() - - with SessionContextManager(path="management_command:create_worker") as session: - statement = select(Worker).where(Worker.token == args.token) - results = session.exec(statement) - existing_worker = results.one_or_none() - if existing_worker is None: - worker = Worker(name=args.name, token=args.token, last_seen=None) - session.add(worker) - session.commit() - print(f"Worker with token {args.token} created") - else: - print(f"Worker with token {args.token} already exists") diff --git a/backend/scripts/reset_task.py b/backend/scripts/reset_task.py deleted file mode 100755 index b8120ea2..00000000 --- a/backend/scripts/reset_task.py +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env python - -import argparse -import uuid - -from sqlmodel import or_, update -from transcribee_backend.config import settings -from transcribee_backend.db import SessionContextManager -from transcribee_backend.models.task import Task, TaskState - -if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument( - "--uuid", required=True, type=uuid.UUID, help="Task UUID or Document UUID" - ) - parser.add_argument( - "--state", - type=TaskState, - choices=list(TaskState), - default=TaskState.FAILED, - help="State of tasks to reset", - ) - args = parser.parse_args() - with SessionContextManager(path="management_command:reset_task") as session: - task = session.execute( - update(Task) - .where( - or_(Task.id == args.uuid, Task.document_id == args.uuid), - Task.state == args.state, - ) - .values(state=TaskState.NEW, remaining_attempts=settings.task_attempt_limit) - ) - session.commit() diff --git a/backend/scripts/set_password.py b/backend/scripts/set_password.py deleted file mode 100755 index 5e64ebd3..00000000 --- a/backend/scripts/set_password.py +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env python - -import argparse - -from transcribee_backend.auth import change_user_password -from transcribee_backend.db import SessionContextManager -from transcribee_backend.exceptions import UserDoesNotExist - -if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument("--user", required=True) - parser.add_argument("--pass", required=True) - args = parser.parse_args() - - with SessionContextManager(path="management_command:set_password") as session: - try: - user = change_user_password( - session=session, username=args.user, new_password=getattr(args, "pass") - ) - print("Password changed") - except UserDoesNotExist: - print("User does not exists") diff --git a/backend/transcribee_backend/admin_cli/__init__.py b/backend/transcribee_backend/admin_cli/__init__.py new file mode 100755 index 00000000..07122fc6 --- /dev/null +++ b/backend/transcribee_backend/admin_cli/__init__.py @@ -0,0 +1,40 @@ +import argparse + +from .command import Command +from .commands.create_api_token import CreateApiTokenCmd +from .commands.create_user import CreateUserCmd +from .commands.create_user_token import CreateUserTokenCmd +from .commands.create_worker import CreateWorkerCmd +from .commands.reset_task import ResetTaskCmd +from .commands.set_password import SetPasswordCmd + +parser = argparse.ArgumentParser() +subparsers = parser.add_subparsers(metavar="COMMAND") + + +def add_command(name: str, description: str, command: Command): + subparser = subparsers.add_parser(name, help=description, description=description) + command.configure_parser(subparser) + subparser.set_defaults(func=command.run) + + +# all commands belong here +add_command("create_api_token", "Create an API token", CreateApiTokenCmd()) +add_command("create_user_token", "Create an user token", CreateUserTokenCmd()) +add_command("create_user", "Create a new user", CreateUserCmd()) +add_command("create_worker", "Register a new worker", CreateWorkerCmd()) +add_command("reset_task", "Reset a task", ResetTaskCmd()) +add_command("set_password", "Set the password of a user", SetPasswordCmd()) + + +def main(): + args = parser.parse_args() + + if "func" in args: + args.func(args) + else: + parser.print_help() + + +if __name__ == "__main__": + main() diff --git a/backend/transcribee_backend/admin_cli/command.py b/backend/transcribee_backend/admin_cli/command.py new file mode 100644 index 00000000..31c1baed --- /dev/null +++ b/backend/transcribee_backend/admin_cli/command.py @@ -0,0 +1,15 @@ +from abc import ABC, abstractmethod +from argparse import ArgumentParser + + +class Command(ABC): + def __init__(self): + pass + + @abstractmethod + def configure_parser(self, parser: ArgumentParser): + pass + + @abstractmethod + def run(self, args): + pass diff --git a/backend/transcribee_backend/admin_cli/commands/create_api_token.py b/backend/transcribee_backend/admin_cli/commands/create_api_token.py new file mode 100755 index 00000000..4ae11fe9 --- /dev/null +++ b/backend/transcribee_backend/admin_cli/commands/create_api_token.py @@ -0,0 +1,15 @@ +from transcribee_backend.admin_cli.command import Command +from transcribee_backend.auth import create_api_token +from transcribee_backend.db import SessionContextManager + + +class CreateApiTokenCmd(Command): + def configure_parser(self, parser): + parser.add_argument("--name", required=True) + + def run(self, args): + with SessionContextManager( + path="management_command:create_api_token" + ) as session: + token = create_api_token(session=session, name=args.name) + print(f"Token created: {token.token}") diff --git a/backend/transcribee_backend/admin_cli/commands/create_user.py b/backend/transcribee_backend/admin_cli/commands/create_user.py new file mode 100755 index 00000000..46c8c22a --- /dev/null +++ b/backend/transcribee_backend/admin_cli/commands/create_user.py @@ -0,0 +1,38 @@ +import random +import string + +from transcribee_backend.admin_cli.command import Command +from transcribee_backend.auth import create_user +from transcribee_backend.db import SessionContextManager +from transcribee_backend.exceptions import UserAlreadyExists + + +def random_password(): + chars = string.ascii_lowercase + string.digits + return "".join(random.choice(chars) for _ in range(20)) + + +class CreateUserCmd(Command): + def configure_parser(self, parser): + parser.add_argument("--user", required=True) + parser.add_argument("--password", required=False) + + def run(self, args): + password = args.password + + if not password: + password = random_password() + + print("Auto-generated password.") + print("Infos to send to user:") + print() + print(f"Username: {args.user}") + print(f"Password: {password} (Please change)") + print() + + with SessionContextManager(path="management_command:create_user") as session: + try: + create_user(session=session, username=args.user, password=password) + print("User created") + except UserAlreadyExists: + print("Could not create user. A user with that name already exists.") diff --git a/backend/transcribee_backend/admin_cli/commands/create_user_token.py b/backend/transcribee_backend/admin_cli/commands/create_user_token.py new file mode 100755 index 00000000..ad55c8cc --- /dev/null +++ b/backend/transcribee_backend/admin_cli/commands/create_user_token.py @@ -0,0 +1,40 @@ +import datetime + +from sqlmodel import select +from transcribee_backend.admin_cli.command import Command +from transcribee_backend.auth import generate_user_token +from transcribee_backend.db import SessionContextManager +from transcribee_backend.helpers.time import now_tz_aware +from transcribee_backend.models.user import User + + +class CreateUserTokenCmd(Command): + def configure_parser(self, parser): + parser.add_argument("--username", required=True) + parser.add_argument("--valid-days", required=True) + + def run(self, args): + with SessionContextManager( + path="management_command:create_user_token" + ) as session: + valid_days = int(args.valid_days) + if valid_days < 0: + print("Valid days must be positive") + exit(1) + + valid_until = now_tz_aware() + datetime.timedelta(days=valid_days) + + user = session.exec( + select(User).where(User.username == args.username) + ).one_or_none() + + if user is None: + print(f"User {args.user} not found") + exit(1) + + key, user_token = generate_user_token(user, valid_until=valid_until) + session.add(user_token) + session.commit() + + print(f"User token created and valid until {valid_until}") + print(f"Secret: {key}") diff --git a/backend/transcribee_backend/admin_cli/commands/create_worker.py b/backend/transcribee_backend/admin_cli/commands/create_worker.py new file mode 100755 index 00000000..f57664a8 --- /dev/null +++ b/backend/transcribee_backend/admin_cli/commands/create_worker.py @@ -0,0 +1,32 @@ +from sqlmodel import select +from transcribee_backend import utils +from transcribee_backend.admin_cli.command import Command +from transcribee_backend.db import SessionContextManager +from transcribee_backend.models import Worker + + +class CreateWorkerCmd(Command): + def configure_parser(self, parser): + parser.add_argument("--name", required=True) + parser.add_argument("--token", required=False) + + def run(self, args): + if args.token is None: + args.token = utils.get_random_string() + + with SessionContextManager(path="management_command:create_worker") as session: + statement = select(Worker).where(Worker.token == args.token) + results = session.exec(statement) + existing_worker = results.one_or_none() + if existing_worker is None: + worker = Worker( + name=args.name, + token=args.token, + last_seen=None, + deactivated_at=None, + ) + session.add(worker) + session.commit() + print(f"Worker with token {args.token} created") + else: + print(f"Worker with token {args.token} already exists") diff --git a/backend/transcribee_backend/admin_cli/commands/reset_task.py b/backend/transcribee_backend/admin_cli/commands/reset_task.py new file mode 100755 index 00000000..0f8dc0eb --- /dev/null +++ b/backend/transcribee_backend/admin_cli/commands/reset_task.py @@ -0,0 +1,30 @@ +from sqlmodel import or_, update +from transcribee_backend.admin_cli.command import Command +from transcribee_backend.config import settings +from transcribee_backend.db import SessionContextManager +from transcribee_backend.models.task import Task, TaskState + + +class ResetTaskCmd(Command): + def configure_parser(self, parser): + parser.add_argument("--uuid", required=True) + parser.add_argument( + "--state", + type=TaskState, + choices=list(TaskState), + default=TaskState.FAILED, + ) + + def run(self, args): + with SessionContextManager(path="management_command:reset_task") as session: + session.execute( + update(Task) + .where( + or_(Task.id == args.uuid, Task.document_id == args.uuid), + Task.state == args.state, + ) + .values( + state=TaskState.NEW, remaining_attempts=settings.task_attempt_limit + ) + ) + session.commit() diff --git a/backend/transcribee_backend/admin_cli/commands/set_password.py b/backend/transcribee_backend/admin_cli/commands/set_password.py new file mode 100755 index 00000000..9885527c --- /dev/null +++ b/backend/transcribee_backend/admin_cli/commands/set_password.py @@ -0,0 +1,22 @@ +from transcribee_backend.admin_cli.command import Command +from transcribee_backend.auth import change_user_password +from transcribee_backend.db import SessionContextManager +from transcribee_backend.exceptions import UserDoesNotExist + + +class SetPasswordCmd(Command): + def configure_parser(self, parser): + parser.add_argument("--user", required=True) + parser.add_argument("--pass", required=True) + + def run(self, args): + with SessionContextManager(path="management_command:set_password") as session: + try: + change_user_password( + session=session, + username=args.user, + new_password=getattr(args, "pass"), + ) + print("Password changed") + except UserDoesNotExist: + print("User does not exists") diff --git a/backend/transcribee_backend/admin_cli/main.py b/backend/transcribee_backend/admin_cli/main.py new file mode 100755 index 00000000..bd9bf0cf --- /dev/null +++ b/backend/transcribee_backend/admin_cli/main.py @@ -0,0 +1,36 @@ +import argparse + +from .command import Command +from .commands.create_api_token import CreateApiTokenCmd +from .commands.create_user import CreateUserCmd +from .commands.create_user_token import CreateUserTokenCmd +from .commands.create_worker import CreateWorkerCmd +from .commands.reset_task import ResetTaskCmd +from .commands.set_password import SetPasswordCmd + +parser = argparse.ArgumentParser() +subparsers = parser.add_subparsers(required=True, title="commands") + + +def add_command(name: str, description: str, command: Command): + subparser = subparsers.add_parser(name, help=description, description=description) + command.configure_parser(subparser) + subparser.set_defaults(func=command.run) + + +# all commands belong here +add_command("create_api_token", "Create an API token", CreateApiTokenCmd()) +add_command("create_user_token", "Create an user token", CreateUserTokenCmd()) +add_command("create_user", "Create a new user", CreateUserCmd()) +add_command("create_worker", "Register a new worker", CreateWorkerCmd()) +add_command("reset_task", "Reset a task", ResetTaskCmd()) +add_command("set_password", "Set the password of a user", SetPasswordCmd()) + + +def main(): + args = parser.parse_args() + args.func(args) + + +if __name__ == "__main__": + main() diff --git a/backend/transcribee_backend/config.py b/backend/transcribee_backend/config.py index 7bc482b8..b7ff5593 100644 --- a/backend/transcribee_backend/config.py +++ b/backend/transcribee_backend/config.py @@ -17,7 +17,9 @@ class Settings(BaseSettings): media_url_base = "http://localhost:8000/" logged_out_redirect_url: None | str = None - model_config_path: Path = Path("data/models.json") + model_config_path: Path = Path(__file__).parent.resolve() / Path( + "default_models.json" + ) pages_dir: Path = Path("data/pages/") metrics_username = "transcribee" diff --git a/backend/transcribee_backend/db/run_migrations.py b/backend/transcribee_backend/db/run_migrations.py new file mode 100644 index 00000000..71fb1918 --- /dev/null +++ b/backend/transcribee_backend/db/run_migrations.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python + +from os import path + +from alembic import command +from alembic.config import Config + + +def main(): + alembic_cfg = Config() + alembic_cfg.set_main_option( + "script_location", + path.join(path.dirname(path.realpath(__file__)), "migrations"), + ) + command.upgrade(alembic_cfg, "head") + + +if __name__ == "__main__": + main() diff --git a/backend/data/models.json b/backend/transcribee_backend/default_models.json similarity index 100% rename from backend/data/models.json rename to backend/transcribee_backend/default_models.json diff --git a/flake.nix b/flake.nix index b363c6d7..def73a8a 100644 --- a/flake.nix +++ b/flake.nix @@ -35,89 +35,140 @@ self, ... }: - (flake-utils.lib.eachDefaultSystem - (system: + let + pythonPkgName = "python311"; + in + { + overlays.default = ( + final: prev: let - pkgs = nixpkgs.legacyPackages.${system}; + pkgs = import nixpkgs { + system = final.system; + }; lib = nixpkgs.lib; - python = pkgs.python311; - - ld_packages = [ - pkgs.file # provides libmagic - - # for ctranslate2 - pkgs.stdenv.cc.cc.lib - ]; + python = pkgs."${pythonPkgName}"; in { - packages = rec { - worker = (import ./nix/pkgs/worker.nix { inherit pkgs lib python uv2nix pyproject-nix pyproject-build-systems; }); - backend = (import ./nix/pkgs/backend.nix { inherit pkgs lib python uv2nix pyproject-nix pyproject-build-systems; }); - frontend = (import ./nix/pkgs/frontend.nix { - inherit pkgs lib; - versionInfo = { - commitHash = if (self ? rev) then self.rev else self.dirtyRev; - commitDate = lib.readFile "${pkgs.runCommand "timestamp" { env.when = self.lastModified; } "echo -n `date -d @$when --iso-8601=s` > $out"}"; - }; - }); + transcribee-worker = import ./nix/pkgs/worker.nix { + inherit + pkgs + lib + python + uv2nix + pyproject-nix + pyproject-build-systems + ; }; - devShells.default = pkgs.mkShell { - packages = [ + transcribee-backend = import ./nix/pkgs/backend.nix { + inherit + pkgs + lib python - pkgs.uv - python.pkgs.black - pkgs.poethepoet - - pkgs.overmind - pkgs.wait4x - pkgs.pre-commit - - pkgs.nodejs_20 - - # nix tooling - pkgs.nixpkgs-fmt - - # required by whispercppy - pkgs.cmake - - # required by pre-commit - pkgs.git - pkgs.ruff - - # required by psycopg2 - pkgs.openssl - - pkgs.ffmpeg - - # for automerge-py - pkgs.libiconv - pkgs.rustc - pkgs.cargo - - pkgs.icu.dev - - # Our database - pkgs.postgresql_14 + uv2nix + pyproject-nix + pyproject-build-systems + ; + }; - # Our database2 ? - pkgs.redis + transcribee-frontend = import ./nix/pkgs/frontend.nix { + inherit pkgs lib; - pkgs.glibcLocales - ]; + versionInfo = { + commitHash = if (self ? rev) then self.rev else self.dirtyRev; + commitDate = self.lastModified; + }; + }; + } + ); + + nixosModules.default = { + nixpkgs.overlays = [ self.overlays.default ]; + }; + } + // (flake-utils.lib.eachDefaultSystem ( + system: + let + pkgs = import nixpkgs { + inherit system; + overlays = [ self.overlays.default ]; + }; + python = pkgs."${pythonPkgName}"; + + ld_packages = [ + pkgs.file # provides libmagic + + # for ctranslate2 + pkgs.stdenv.cc.cc.lib + ]; + in + { + packages = { + backend = pkgs.transcribee-backend; + worker = pkgs.transcribee-worker; + frontend = pkgs.transcribee-frontend; + }; + + formatter = pkgs.nixfmt-rfc-style; + + devShells.default = pkgs.mkShell { + packages = [ + python + pkgs.uv + python.pkgs.black + pkgs.poethepoet + + pkgs.overmind + pkgs.wait4x + pkgs.pre-commit + + pkgs.nodejs_20 + + # nix tooling + pkgs.nixpkgs-fmt + + # required by whispercppy + pkgs.cmake + + # required by pre-commit + pkgs.git + pkgs.ruff + + # required by psycopg2 + pkgs.openssl + + pkgs.ffmpeg + + # for automerge-py + pkgs.libiconv + pkgs.rustc + pkgs.cargo + + pkgs.icu.dev + + # Our database + pkgs.postgresql_14 + + # Our database2 ? + pkgs.redis + + pkgs.glibcLocales + ]; - shellHook = '' + shellHook = + '' unset PYTHONPATH export UV_PYTHON_DOWNLOADS=never export TRANSCRIBEE_DYLD_LIBRARY_PATH=${pkgs.lib.makeLibraryPath ld_packages} export LD_LIBRARY_PATH=$LD_SEARCH_PATH:$TRANSCRIBEE_DYLD_LIBRARY_PATH - '' + pkgs.lib.optionalString pkgs.stdenv.isDarwin '' + '' + + pkgs.lib.optionalString pkgs.stdenv.isDarwin '' export CPPFLAGS="-I${pkgs.libcxx.dev}/include/c++/v1" # `dyld` needs to find the libraries export DYLD_LIBRARY_PATH=$LD_LIBRARY_PATH:$DYLD_LIBRARY_PATH ''; - }; - } - )); + }; + } + )); } diff --git a/nix/pkgs/backend.nix b/nix/pkgs/backend.nix index 690fdc3b..08592100 100644 --- a/nix/pkgs/backend.nix +++ b/nix/pkgs/backend.nix @@ -21,7 +21,7 @@ let pkgs.postgresql_14 ]; - buildInputs = (old.buildInputs or []) ++ [ + buildInputs = (old.buildInputs or [ ]) ++ [ pkgs.openssl ]; }); @@ -45,4 +45,5 @@ let ] ); -in pythonSet.mkVirtualEnv "transcribee-backend-env" workspace.deps.default +in +pythonSet.mkVirtualEnv "transcribee-backend-env" workspace.deps.default diff --git a/nix/pkgs/frontend.nix b/nix/pkgs/frontend.nix index a44234c7..a1fcc3d7 100644 --- a/nix/pkgs/frontend.nix +++ b/nix/pkgs/frontend.nix @@ -4,58 +4,72 @@ versionInfo, ... }: -pkgs.buildNpmPackage (lib.fix (self: { - pname = "transcribee-frontend"; - version = "0.1.0"; - src = ../../frontend; +pkgs.buildNpmPackage ( + lib.fix (self: { + pname = "transcribee-frontend"; + version = "0.1.0"; + src = ../../frontend; - nativeBuildInputs = [ pkgs.git ]; + nativeBuildInputs = [ pkgs.git ]; - npmDeps = pkgs.importNpmLock { - npmRoot = self.src; - packageSourceOverrides = let - gitDeps = (lib.attrsets.filterAttrs (name: pkgInfo: - (lib.hasPrefix "node_modules/" name) - && (lib.hasPrefix "git" (pkgInfo.resolved or ""))) - (pkgs.lib.importJSON (self.src + "/package-lock.json")).packages); - in lib.attrsets.mapAttrs (name: pkgInfo: - let - src = builtins.fetchGit { - url = "https" + lib.removePrefix "git+ssh" pkgInfo.resolved; - rev = lib.last (lib.splitString "#" pkgInfo.resolved); - allRefs = true; - }; - pname = lib.removePrefix "node_modules/" name; - thePkg = pkgs.buildNpmPackage { - inherit src pname; - version = pkgInfo.version; - npmDeps = pkgs.importNpmLock { npmRoot = src; }; - npmConfigHook = pkgs.importNpmLock.npmConfigHook; - installPhase = '' - mkdir $out - npm pack --pack-destination=$out - mv $out/*.tgz $out/package.tgz - ''; - }; - in "${thePkg}/package.tgz") gitDeps; - }; + npmDeps = pkgs.importNpmLock { + npmRoot = self.src; + packageSourceOverrides = + let + gitDeps = ( + lib.attrsets.filterAttrs ( + name: pkgInfo: + (lib.hasPrefix "node_modules/" name) && (lib.hasPrefix "git" (pkgInfo.resolved or "")) + ) (lib.importJSON (self.src + "/package-lock.json")).packages + ); + in + lib.attrsets.mapAttrs ( + name: pkgInfo: + let + src = builtins.fetchGit { + url = "https" + lib.removePrefix "git+ssh" pkgInfo.resolved; + rev = lib.last (lib.splitString "#" pkgInfo.resolved); + allRefs = true; + }; + pname = lib.removePrefix "node_modules/" name; + thePkg = pkgs.buildNpmPackage { + inherit src pname; + version = pkgInfo.version; + npmDeps = pkgs.importNpmLock { npmRoot = src; }; + npmConfigHook = pkgs.importNpmLock.npmConfigHook; + installPhase = '' + mkdir $out + npm pack --pack-destination=$out + mv $out/*.tgz $out/package.tgz + ''; + }; + in + "${thePkg}/package.tgz" + ) gitDeps; + }; - preBuild = let - versionExports = ([ ] ++ (lib.optional (versionInfo ? commitHash) - ''export COMMIT_HASH="${versionInfo.commitHash}"'') - ++ (lib.optional (versionInfo ? commitDate) - ''export COMMIT_DATE="${versionInfo.commitDate}"'')); - in '' - ${lib.concatStringsSep "\n" versionExports} - ''; + preBuild = + let + versionExports = ( + [ ] + ++ (lib.optional (versionInfo ? commitHash) ''export COMMIT_HASH="${versionInfo.commitHash}"'') + ++ (lib.optional ( + versionInfo ? commitDate + ) ''export COMMIT_DATE="$(date -d @${builtins.toString versionInfo.commitDate} --iso-8601=s)"'') + ); + in + '' + ${lib.concatStringsSep "\n" versionExports} + ''; - installPhase = '' - runHook preInstall - cp -r dist $out - runHook postInstall - ''; + installPhase = '' + runHook preInstall + cp -r dist $out + runHook postInstall + ''; - npmBuildScript = "build"; + npmBuildScript = "build"; - npmConfigHook = pkgs.importNpmLock.npmConfigHook; -})) + npmConfigHook = pkgs.importNpmLock.npmConfigHook; + }) +) diff --git a/nix/pkgs/worker.nix b/nix/pkgs/worker.nix index 49f2c03c..b28c3bb6 100644 --- a/nix/pkgs/worker.nix +++ b/nix/pkgs/worker.nix @@ -36,7 +36,7 @@ let pyicu = prev.pyicu.overrideAttrs (old: { nativeBuildInputs = old.nativeBuildInputs ++ [ pkgs.icu.dev - (final.resolveBuildSystem {}) + (final.resolveBuildSystem { }) ]; }); }; @@ -58,4 +58,5 @@ let ] ); -in pythonSet.mkVirtualEnv "transcribee-worker-env" workspace.deps.default +in +pythonSet.mkVirtualEnv "transcribee-worker-env" workspace.deps.default