diff --git a/.gitignore b/.gitignore index a399bad..4d7edc6 100644 --- a/.gitignore +++ b/.gitignore @@ -154,3 +154,6 @@ out/ # nix result + +# for local dev might dump cred stuff here +cred/ diff --git a/config.toml b/config.toml index 6993c35..927899d 100644 --- a/config.toml +++ b/config.toml @@ -1 +1,4 @@ [general_config] +backend_base_url = "http://localhost:8080" +backend_user = "test@example.com" +backend_pw = "test" diff --git a/scripts/simple_create_user.sh b/scripts/simple_create_user.sh new file mode 100755 index 0000000..120b58e --- /dev/null +++ b/scripts/simple_create_user.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash +set -Eeuox pipefail + +banner() { + echo "========================================================" + echo " $*" + echo "========================================================" +} + +# utility script for easy testing + +uv run taco register --display-name "Display Test" --email "test@example.com" --password "test" diff --git a/src/trygo_py_cliclient/cli/__init__.py b/src/trygo_py_cliclient/cli/__init__.py index 483642f..16d5072 100644 --- a/src/trygo_py_cliclient/cli/__init__.py +++ b/src/trygo_py_cliclient/cli/__init__.py @@ -22,7 +22,6 @@ def parse_args(): trygo_py_cliclient.cli.register.setup_parser(subparsers) trygo_py_cliclient.cli.login.setup_parser(subparsers) - args = parser.parse_args() return args @@ -38,4 +37,6 @@ def main(): _logger.info(f"Got args {args=}") _logger.info(f"Loaded config {config=}") - args.func(args) + # TODO is there a clean way to hang on to a session for a bit when the cli command has run? + # i guess realistically we don't want that + args.func(config, args) diff --git a/src/trygo_py_cliclient/cli/login.py b/src/trygo_py_cliclient/cli/login.py index 509d274..866d6ae 100644 --- a/src/trygo_py_cliclient/cli/login.py +++ b/src/trygo_py_cliclient/cli/login.py @@ -2,6 +2,7 @@ import argparse import logging import typing +import trygo_py_cliclient.config import trygo_py_cliclient.client _logger = logging.getLogger(__name__) @@ -16,13 +17,13 @@ else: def setup_parser(subparsers: _SubparserType) -> None: parser = subparsers.add_parser("login") - parser.add_argument("--email", type=str, help="config file location", default="") - parser.add_argument("--password", type=str, help="config file location", default="") + parser.add_argument("--email", type=str, help="The email to log in with ", default="") + parser.add_argument("--password", type=str, help="Password", default="") parser.set_defaults(func=run) -def run(args): - - clnt = trygo_py_cliclient.client.TryGoAPIClient("http://localhost:8080/") - + +def run(config: trygo_py_cliclient.config.Config, args): + clnt = trygo_py_cliclient.client.BackendService(config) + response = clnt.login(args.email, args.password) _logger.info(response) diff --git a/src/trygo_py_cliclient/cli/register.py b/src/trygo_py_cliclient/cli/register.py index 9cc528a..a348840 100644 --- a/src/trygo_py_cliclient/cli/register.py +++ b/src/trygo_py_cliclient/cli/register.py @@ -23,8 +23,8 @@ def setup_parser(subparsers: _SubparserType) -> None: parser.add_argument("--password", type=str, help="config file location", default="") parser.set_defaults(func=run) + def run(args): - clnt = trygo_py_cliclient.client.TryGoAPIClient("http://localhost:8080/") response = clnt.register(args.email, args.password, args.display_name) _logger.info(response) diff --git a/src/trygo_py_cliclient/client.py b/src/trygo_py_cliclient/client.py index 6b275ca..ff8583f 100644 --- a/src/trygo_py_cliclient/client.py +++ b/src/trygo_py_cliclient/client.py @@ -2,8 +2,10 @@ import requests import logging from typing import Optional, Dict, Any from dataclasses import asdict +from pathlib import Path -from trygo_py_cliclient.models import User, RegisterRequest, LoginRequest, AuthResponse +from trygo_py_cliclient.models import RegisterRequest, LoginRequest, AuthResponse +from trygo_py_cliclient.config import Config from trygo_py_cliclient.exceptions import ( TryGoAPIError, AuthenticationError, @@ -15,17 +17,14 @@ from trygo_py_cliclient.exceptions import ( _logger = logging.getLogger(__name__) -class TryGoAPIClient: - """HTTP client for TryGo webapp REST API""" - - def __init__(self, base_url: str, timeout: int = 30): - self.base_url = base_url.rstrip("/") - self.timeout = timeout +class BackendService: + def __init__(self, config: Config): + self.base_url = config.general_config.backend_base_url.rstrip("/") + self.timeout = 30 self.session = requests.Session() self.session.headers.update( {"Content-Type": "application/json", "User-Agent": "trygo-py-client/0.1.0"} ) - self._auth_token: Optional[str] = None def _make_request( self, method: str, endpoint: str, data: Optional[Dict[str, Any]] = None @@ -82,6 +81,7 @@ class TryGoAPIClient: except ValueError as e: raise TryGoAPIError(f"Invalid JSON response: {e}") + # claude really fucked this except requests.exceptions.Timeout: raise NetworkError("Request timeout") except requests.exceptions.ConnectionError: @@ -92,18 +92,6 @@ class TryGoAPIClient: def register(self, email: str, password: str, display_name: str) -> AuthResponse: """ Register a new user account - - Args: - email: User's email address - password: User's password - display_name: User's display name - - Returns: - AuthResponse with registration result - - Raises: - ValidationError: If registration data is invalid - TryGoAPIError: If registration fails """ request_data = RegisterRequest( email=email, password=password, display_name=display_name @@ -117,42 +105,16 @@ class TryGoAPIClient: # Parse response and create AuthResponse auth_response = AuthResponse( - success=response_data.get("success", False), message=response_data.get("message", ""), - token=response_data.get("token"), - user=None, + email=response_data.get("email", ""), + id=response_data.get("id", ""), ) - # Parse user data if present - if "user" in response_data and response_data["user"]: - user_data = response_data["user"] - auth_response.user = User( - email=user_data.get("email", ""), - password="", # Never store password in response - display_name=user_data.get("display_name", ""), - id=user_data.get("id"), - ) - - # Store auth token on successful registration - if auth_response.success and auth_response.token: - self.set_auth_token(auth_response.token) - return auth_response - def login(self, email: str, password: str) -> AuthResponse: + def login(self, email: str, password: str) -> str: """ Authenticate user and get auth token - - Args: - email: User's email address - password: User's password - - Returns: - AuthResponse with login result and token - - Raises: - AuthenticationError: If login credentials are invalid - TryGoAPIError: If login fails """ request_data = LoginRequest(email=email, password=password) @@ -162,39 +124,10 @@ class TryGoAPIClient: _logger.info(response_data) # Parse response and create AuthResponse - auth_response = AuthResponse( - success=response_data.get("success", False), - message=response_data.get("message", ""), - token=response_data.get("token"), - user=None, - ) + token = response_data.get("token", "") + self._store_credential(token) - # Parse user data if present - if "user" in response_data and response_data["user"]: - user_data = response_data["user"] - auth_response.user = User( - email=user_data.get("email", ""), - password="", # Never store password in response - display_name=user_data.get("display_name", ""), - id=user_data.get("id"), - ) - - # Store auth token on successful login - if auth_response.success and auth_response.token: - self.set_auth_token(auth_response.token) - - return auth_response - - def set_auth_token(self, token: str) -> None: - """ - Set authentication token for subsequent requests - - Args: - token: JWT or session token for authentication - """ - self._auth_token = token - self.session.headers["Authorization"] = f"Bearer {token}" - _logger.debug("Authentication token set") + return token def logout(self) -> None: """ @@ -205,16 +138,12 @@ class TryGoAPIClient: _logger.info("Logging out user") # Try to call logout endpoint if we have a token - if self._auth_token: - try: - self._make_request("POST", "/auth/logout") - _logger.debug("Server logout successful") - except Exception as e: - _logger.warning(f"Server logout failed (continuing anyway): {e}") - - # Clear local auth state - self._auth_token = None - if "Authorization" in self.session.headers: - del self.session.headers["Authorization"] _logger.debug("Local authentication cleared") + + def _store_credential(self, token: str) -> None: + + token_path = Path("cred/token") + token_path.parent.mkdir(parents=True,exist_ok=True) + with token_path.open(mode="w") as tokenf: + tokenf.write(token) diff --git a/src/trygo_py_cliclient/config/config.py b/src/trygo_py_cliclient/config/config.py index a03341b..f65103e 100644 --- a/src/trygo_py_cliclient/config/config.py +++ b/src/trygo_py_cliclient/config/config.py @@ -8,6 +8,10 @@ class GeneralConfig: log_pattern: str = "%(asctime)s | %(process)d | %(levelname)-7s | %(name)s:%(lineno)d | %(message)s" log_file: Optional[str] = None log_stream: bool = True + backend_base_url: str = "" + # obviously bad, but for now just keep it + backend_user: str = "" + backend_pw: str = "" @dataclass(frozen=True) diff --git a/src/trygo_py_cliclient/config/config_reader.py b/src/trygo_py_cliclient/config/config_reader.py index ee2d853..d8fe740 100644 --- a/src/trygo_py_cliclient/config/config_reader.py +++ b/src/trygo_py_cliclient/config/config_reader.py @@ -42,31 +42,7 @@ def serialize_config(config_dict: dict) -> Config: Converts a dictionary to a Config object Makes assumptions about structure of the config_dict, so validation should happen here too if needed. - - :param config_dict: dictionary containing config values - :return: Config object """ - # generation_config = GenerationConfig(**config_dict["generation_config"]) - - # general_config_dict = config_dict["general_config"] - # general_config = GeneralConfig( - # root_directory=general_config_dict["root_directory"], - # out_dir_name=general_config_dict["out_dir_name"], - # dots_json_name=general_config_dict["dots_json_name"], - # mega_merged_name=general_config_dict["mega_merged_name"], - # mega_merged_inferenced_name=general_config_dict["mega_merged_inferenced_name"], - # skip_to_stage=general_config_dict["skip_to_stage"], - # measurement_type=MeasurementTypeEnum(general_config_dict["measurement_type"]), - # indexes_json_name=general_config_dict["indexes_json_name"], - # log_pattern=general_config_dict["log_pattern"], - # ) - - # deepdog_config = DeepdogConfig(**config_dict["deepdog_config"]) - # config = Config( - # generation_config=generation_config, - # general_config=general_config, - # deepdog_config=deepdog_config, - # ) config = dacite.from_dict( data_class=Config, data=config_dict, diff --git a/src/trygo_py_cliclient/models.py b/src/trygo_py_cliclient/models.py index 142a2fe..ee1085f 100644 --- a/src/trygo_py_cliclient/models.py +++ b/src/trygo_py_cliclient/models.py @@ -33,7 +33,6 @@ class LoginRequest: class AuthResponse: """Response from authentication endpoints""" - success: bool message: str - user: Optional[User] = None - token: Optional[str] = None + email: Optional[str] = None + id: Optional[str] = None