diff --git a/aw_watcher_window/config.py b/aw_watcher_window/config.py index 81eabc2..0750dae 100644 --- a/aw_watcher_window/config.py +++ b/aw_watcher_window/config.py @@ -8,6 +8,10 @@ exclude_titles = [] poll_time = 1.0 strategy_macos = "swift" +host = "" +port = "" +auth_user = "" +auth_password = "" """.strip() @@ -22,12 +26,16 @@ def parse_args(): default_exclude_title = config["exclude_title"] default_exclude_titles = config["exclude_titles"] default_strategy_macos = config["strategy_macos"] + default_host = config["host"] or None + default_port = config["port"] or None + default_auth_user = config["auth_user"] + default_auth_password = config["auth_password"] parser = argparse.ArgumentParser( description="A cross platform window watcher for Activitywatch.\nSupported on: Linux (X11), macOS and Windows." ) - parser.add_argument("--host", dest="host") - parser.add_argument("--port", dest="port") + parser.add_argument("--host", dest="host", default=default_host) + parser.add_argument("--port", dest="port", default=default_port) parser.add_argument("--testing", dest="testing", action="store_true") parser.add_argument( "--exclude-title", @@ -43,6 +51,18 @@ def parse_args(): help="Exclude window titles by regular expression. Can specify multiple times." ) parser.add_argument("--verbose", dest="verbose", action="store_true") + parser.add_argument( + "--auth-user", + dest="auth_user", + default=default_auth_user, + help="Username for HTTP Basic Auth (for nginx-proxied servers)", + ) + parser.add_argument( + "--auth-password", + dest="auth_password", + default=default_auth_password, + help="Password for HTTP Basic Auth (for nginx-proxied servers)", + ) parser.add_argument( "--poll-time", dest="poll_time", type=float, default=default_poll_time ) diff --git a/aw_watcher_window/main.py b/aw_watcher_window/main.py index b00f678..1ff6a83 100644 --- a/aw_watcher_window/main.py +++ b/aw_watcher_window/main.py @@ -1,6 +1,8 @@ +import json import logging import os import re +import requests import signal import subprocess import sys @@ -40,6 +42,34 @@ def try_compile_title_regex(title): exit(1) +def _patch_client_auth(client, user, password): + """Replace aw-client HTTP methods to inject Basic Auth credentials.""" + from aw_client.client import always_raise_for_request_errors + + auth = requests.auth.HTTPBasicAuth(user, password) + _url = client._url + + @always_raise_for_request_errors + def _get(self_ref, endpoint, params=None): + return requests.get(_url(endpoint), params=params, auth=auth) + + @always_raise_for_request_errors + def _post(self_ref, endpoint, data, params=None): + headers = {"Content-type": "application/json", "charset": "utf-8"} + return requests.post(_url(endpoint), data=bytes(json.dumps(data), "utf8"), headers=headers, params=params, auth=auth) + + @always_raise_for_request_errors + def _delete(self_ref, endpoint, data=None): + if data is None: + data = {} + headers = {"Content-type": "application/json"} + return requests.delete(_url(endpoint), data=json.dumps(data), headers=headers, auth=auth) + + client._get = lambda endpoint, params=None: _get(client, endpoint, params) + client._post = lambda endpoint, data, params=None: _post(client, endpoint, data, params) + client._delete = lambda endpoint, data=None: _delete(client, endpoint, data) + + def main(): args = parse_args() @@ -63,6 +93,10 @@ def main(): "aw-watcher-window", host=args.host, port=args.port, testing=args.testing ) + if args.auth_user and args.auth_password: + _patch_client_auth(client, args.auth_user, args.auth_password) + logger.info("HTTP Basic Auth enabled for user: %s", args.auth_user) + bucket_id = f"{client.client_name}_{client.client_hostname}" event_type = "currentwindow"