diff --git a/amanuensis/cli/__init__.py b/amanuensis/cli/__init__.py index f97fdf6..f572ae0 100644 --- a/amanuensis/cli/__init__.py +++ b/amanuensis/cli/__init__.py @@ -2,6 +2,8 @@ from argparse import ArgumentParser import logging import logging.config +import amanuensis.cli.admin + LOGGING_CONFIG = { "version": 1, @@ -84,6 +86,7 @@ def main(): # Add commands from cli submodules subparsers = parser.add_subparsers(metavar="COMMAND") + add_subcommand(subparsers, amanuensis.cli.admin) # Parse args and execute the desired action args = parser.parse_args() diff --git a/amanuensis/cli/admin.py b/amanuensis/cli/admin.py new file mode 100644 index 0000000..be9a60c --- /dev/null +++ b/amanuensis/cli/admin.py @@ -0,0 +1,58 @@ +import collections +import json +import logging +import os + +from amanuensis.db import DbContext + +from .helpers import add_argument + + +COMMAND_NAME = "admin" +COMMAND_HELP = "Interact with Amanuensis." + +LOG = logging.getLogger(__name__) + + +@add_argument("path", metavar="DB_PATH", help="Path to where the database should be created") +@add_argument("--force", "-f", action="store_true", help="Overwrite existing database") +@add_argument("--verbose", "-v", action="store_true", help="Enable db echo") +def command_init_db(args) -> int: + """ + Initialize the Amanuensis database. + """ + # Check if force is required + if not args.force and os.path.exists(args.path): + args.parser.error(f"{args.path} already exists and --force was not specified") + + # Initialize the database + db_uri = f"sqlite:///{os.path.abspath(args.path)}" + LOG.info(f"Creating database at {db_uri}") + db = DbContext(db_uri, debug=args.verbose) + db.create_all() + + LOG.info("Done") + return 0 + + +@add_argument("path", metavar="CONFIG_PATH", help="Path to the config file") +def command_secret_key(args) -> int: + """ + Generate a Flask secret key. + + The Flask server will not run unless a secret key has + been generated. + """ + # Load the json config + with open(args.path, mode="r", encoding="utf8") as f: + config = json.load(f, object_pairs_hook=collections.OrderedDict) + + # Set the secret key to a new random string + config["SECRET_KEY"] = os.urandom(32).hex() + + # Write the config back out + with open(args.path, mode="w", encoding="utf8") as f: + json.dump(config, f, indent=2) + + LOG.info("Regenerated Flask secret key") + return 0 diff --git a/amanuensis/cli/server.py b/amanuensis/cli/server.py deleted file mode 100644 index d337f4f..0000000 --- a/amanuensis/cli/server.py +++ /dev/null @@ -1,120 +0,0 @@ -import logging -import os - -from amanuensis.config import RootConfigDirectoryContext - -from .helpers import ( - add_argument, - no_argument, - alias, - config_get, - config_set, - CONFIG_GET_ROOT_VALUE) - -logger = logging.getLogger(__name__) - - -@alias('i') -@add_argument("--refresh", - action="store_true", - help="Refresh an existing config directory") -def command_init(args): - """ - Initialize a config directory at --config-dir - - A clean config directory will contain a config.json, a - lexicon config directory, and a user config directory. - - Refreshing an existing directory will add keys to the global config that - are present in the default configs. Users and lexicons that are missing - from the indexes will be deleted, and stale index entries will be removed. - """ - # Module imports - from amanuensis.config.init import create_config_dir - - # Verify arguments - if args.refresh and not os.path.isdir(args.config_dir): - print("Error: couldn't find directory '{}'".format(args.config_dir)) - - # Internal call - create_config_dir(args.config_dir, args.refresh) - logger.info(f'Initialized config dir at {args.config_dir}') - return 0 - - -@alias('gs') -@no_argument -def command_generate_secret(args): - """ - Generate a Flask secret key - - The Flask server will not run unless a secret key has - been generated. - """ - root: RootConfigDirectoryContext = args.root - secret_key: bytes = os.urandom(32) - with root.edit_config() as cfg: - cfg.secret_key = secret_key.hex() - logger.info("Regenerated Flask secret key") - return 0 - - -@alias('r') -@add_argument("-a", "--address", default="127.0.0.1") -@add_argument("-p", "--port", default="5000") -@add_argument("--debug", action="store_true") -def command_run(args): - """ - Run the default Flask server - - The default Flask server is not secure, and should - only be used for development. - """ - from amanuensis.server import get_app - - root: RootConfigDirectoryContext = args.root - - with root.read_config() as cfg: - if cfg.secret_key is None: - logger.error("Can't run server without a secret_key. " - "Run generate-secet first.") - return -1 - - get_app(root).run(host=args.address, port=args.port, debug=args.debug) - return 0 - - -@alias('n') -@add_argument("--get", - metavar="PATHSPEC", - dest="get", - nargs="?", - const=CONFIG_GET_ROOT_VALUE, - help="Get the value of a config key") -@add_argument("--set", - metavar=("PATHSPEC", "VALUE"), - dest="set", - nargs=2, - help="Set the value of a config key") -def command_config(args): - """ - Interact with the global config - - PATHSPEC is a path into the config object formatted as - a dot-separated sequence of keys. - """ - root: RootConfigDirectoryContext = args.root - - if args.get and args.set: - logger.error("Specify one of --get and --set") - return -1 - - if args.get: - with root.read_config() as cfg: - config_get(cfg, args.get) - - if args.set: - with root.edit_config() as cfg: - config_set("config", cfg, args.set) - - return 0