diff --git a/files/hedgedoc-util.py b/files/hedgedoc-util.py old mode 100644 new mode 100755 index 5f27a6c1c9ab4cc92e2206cae26aaa8d45c58223..4088070a7b45b31864ab0baba90c7f99253a5747 --- a/files/hedgedoc-util.py +++ b/files/hedgedoc-util.py @@ -12,17 +12,21 @@ import pymysql.cursors import configparser class GlobalState(): - def __init__(self, outputformat): - self.outputformat = outputformat - self.config = self._load_config() - self.db = self._get_connection() + def __init__(self, options): + self.config = self._load_config(options.get('config')) + self.config.update(self._clean_config(options)) + try: + self.db = self._get_connection() + except Exception as e: + click.echo("Database connection failed: {}".format(repr(e))) + sys.exit(2) self._check_schema() def _get_connection(self): - return pymysql.connect(host=self.config['db']['host'], - user=self.config['db']['username'], - password=self.config['db']['password'], - database=self.config['db']['database'], + return pymysql.connect(host=self.config['dbhost'], + user=self.config['dbuser'], + password=self.config['dbpw'], + database=self.config['dbname'], charset='utf8mb4', cursorclass=pymysql.cursors.DictCursor) @@ -30,18 +34,34 @@ class GlobalState(): with self.db.cursor() as cursor: cursor.execute('SELECT name from SequelizeMeta ORDER BY name ASC') schema = ','.join([i['name'] for i in cursor.fetchall()]) - if schema != self.config['db']['schema']: + if schema != self.config['dbschema']: click.echo("Unsupportet db schema: {}".format(schema)) sys.exit(2) - def _load_config(self): - config = configparser.ConfigParser() - config.read('/usr/local/etc/hedgedoc-util/hedgedoc-util.cfg') - return config + def _load_config(self, path): + result = {} + try: + with open(path, 'r') as f: + for line in f.readlines(): + if line.startswith('#'): + continue + data = line.replace('\n', '').split('=') + if len(data) != 2: + continue + result[data[0]] = data[1] + except: + return {} + return result + def _clean_config(self, d): + return { + k.replace('HEDGEDOCUTIL_','').lower():v + for k, v in d.items() + if v is not None + } def _default_template_mail(): ctx = click.get_current_context().obj - return ctx.config['templates']['mail'] + return ctx.config['pad_mail_template'] def _date_json_handler(obj): return obj.isoformat() if hasattr(obj, 'isoformat') else obj @@ -145,12 +165,12 @@ def tsv_escape(value): return value.replace('\\', '\\\\').replace('\t', '\\t').replace('\n', '\\n').replace('\r', '\\r') def output_object(obj): - ctx = click.get_current_context().obj - if ctx.outputformat == "text": + outputformat = click.get_current_context().obj.config['output'] + if outputformat == "text": click.echo(obj) - elif ctx.outputformat == "json": + elif outputformat == "json": click.echo(json.dumps(obj, default=_date_json_handler)) - elif ctx.outputformat in ("tsv", "tsv-noheader"): + elif outputformat in ("tsv", "tsv-noheader"): if not obj: return if type(obj) is dict: @@ -158,17 +178,23 @@ def output_object(obj): if not type(obj) is list: click.echo(obj) header = obj[0].keys() - if not ctx.outputformat == "tsv-noheader": + if not outputformat == "tsv-noheader": click.echo('\t'.join(header)) for i in obj: click.echo('\t'.join([ tsv_escape(i.get(j,'')) for j in header ])) @click.group() -@click.option('-o', '--output', type=click.Choice(['text', 'json', 'tsv', 'tsv-noheader']), default='text', help='Select output format', show_default=True) +@click.option('-o', '--output', type=click.Choice(['text', 'json', 'tsv', 'tsv-noheader']), default='text', help='Select output format', show_default=True, show_envvar=True) +@click.option('--config', default='/usr/local/etc/hedgedoc-util/hedgedoc-util.cfg', type=click.Path(), help='Config to load db and template default settings from', show_envvar=True, show_default=True) +@click.option('--dbuser', help='User name used for the db connection', show_envvar=True) +@click.option('--dbpw', help='Password used for the db connection', show_envvar=True) +@click.option('--dbname', help='Database used', show_envvar=True) +@click.option('--dbhost', help='Host the db is running on', show_envvar=True) +@click.option('--dbschema', help='Schema string to verify the db schema against', show_envvar=True) @click.pass_context -def cli(ctx, output): - ctx.obj = GlobalState(output) +def cli(ctx, **kwargs): + ctx.obj = GlobalState(kwargs) @cli.command(name="test-connect", help="Checks wether the connection to the db works and if we support the used schema") @click.pass_obj @@ -185,9 +211,9 @@ def cli_pad(): pass @cli_pad.command(name="list", help="List all pads") -@click.option('-c', '--columns', default=['id'], type=click.Choice(['id', 'title', 'ownerId', 'createdAt', 'updatedAt', 'shortid', 'permission', 'viewcount', 'lastchangeuserId', 'lastchangeAt', 'alias', 'deletedAt', 'authorship']), help="Select what data to display. Can be passed multiple times.", multiple=True, show_default=True) -@click.option('--last-change-older', type=click.INT, default=0, help='Only list those pads which are older than this value. In days.') -@click.option('--owner', type=click.STRING, default='', help='Only list pads with this owner, pass the user id') +@click.option('-c', '--columns', default=['id'], type=click.Choice(['id', 'title', 'content', 'ownerId', 'createdAt', 'updatedAt', 'shortid', 'permission', 'viewcount', 'lastchangeuserId', 'lastchangeAt', 'alias', 'deletedAt', 'authorship']), help="Select what data to display. Can be passed multiple times.", multiple=True, show_default=True, show_envvar=True) +@click.option('--last-change-older', type=click.INT, default=0, help='Only list those pads which are older than this value. In days.', show_envvar=True) +@click.option('--owner', type=click.STRING, default='', help='Only list pads with this owner, pass the user id', show_envvar=True) @click.pass_obj def _pad_list(obj, columns, last_change_older, owner): output_object(pad_list(obj.db, columns, last_change_older=last_change_older, owner=owner)) @@ -211,8 +237,8 @@ def _pad_get_content(obj, id): output_object(pad_get_content(obj.db, id)) @cli_pad.command(name="mail", help="Send a pad to its creator via mail") -@click.option('--template', type=click.File(), default=_default_template_mail, help='The template file to use', show_default=True) -@click.option('--convert', default=['markdown'], type=click.Choice(['markdown', 'dokuwiki']), help="Add the pad as attachment to the mail. Can be passed multiple times.", multiple=True, show_default=True) +@click.option('--template', type=click.File(), default=_default_template_mail, help='The template file to use', show_default=True, show_envvar=True) +@click.option('--convert', default=['markdown'], type=click.Choice(['markdown', 'dokuwiki']), help="Add the pad as attachment to the mail. Can be passed multiple times.", multiple=True, show_default=True, show_envvar=True) @click.argument('id') @click.pass_obj def _pad_mail(obj, id, template, convert): @@ -233,7 +259,7 @@ def cli_user(): pass @cli_user.command(name="list", help="List all user") -@click.option('-c', '--columns', default=['id'], type=click.Choice(['id', 'profileid', 'profile', 'history', 'createdAt', 'updatedAt', 'accessToken', 'refreshToken', 'email', 'password', 'deleteToken']), help="Select what data to display. Can be passed multiple times.", multiple=True, show_default=True) +@click.option('-c', '--columns', default=['id'], type=click.Choice(['id', 'profileid', 'profile', 'history', 'createdAt', 'updatedAt', 'accessToken', 'refreshToken', 'email', 'password', 'deleteToken']), help="Select what data to display. Can be passed multiple times.", multiple=True, show_default=True, show_envvar=True) @click.pass_obj def _user_list(obj, columns): output_object(user_list(obj.db, columns)) @@ -251,4 +277,4 @@ def _user_get_mail(obj, id): output_object(user_get_mail(obj.db, id)) if __name__ == '__main__': - cli() + cli(auto_envvar_prefix='HEDGEDOCUTIL') diff --git a/templates/hedgedoc-util.cfg.j2 b/templates/hedgedoc-util.cfg.j2 index 05a06c749108e14c4b770a3c2a87a54216bfcdaa..84e4bab082695f2f16f650d1151d02db28adf454 100644 --- a/templates/hedgedoc-util.cfg.j2 +++ b/templates/hedgedoc-util.cfg.j2 @@ -1,8 +1,6 @@ -[db] -host=localhost -database=hedgedoc -username=hedgedoc -password={{ hedgedoc.db.pw }} -schema=20150504155329-create-users.js,20150508114741-create-notes.js,20150515125813-create-temp.js,20150702001020-update-to-0_3_1.js,20150915153700-change-notes-title-to-text.js,20160112220142-note-add-lastchange.js,20160420180355-note-add-alias.js,20160515114000-user-add-tokens.js,20160607060246-support-revision.js,20160703062241-support-authorship.js,20161009040430-support-delete-note.js,20161201050312-support-email-signin.js,20171009121200-longtext-for-mysql.js,20180209120907-longtext-of-authorship.js,20180306150303-fix-enum.js,20180326103000-use-text-in-tokens.js,20180525153000-user-add-delete-token.js,20200321153000-fix-account-deletion.js -[templates] -mail=/usr/local/etc/hedgedoc-util/mail-std.tpl +HEDGEDOCUTIL_DBHOSTt=localhost +HEDGEDOCUTIL_DBNAME=hedgedoc +HEDGEDOCUTIL_DBUSER=hedgedoc +HEDGEDOCUTIL_DBPW={{ hedgedoc.db.pw }} +HEDGEDOCUTIL_DBSCHEMA=20150504155329-create-users.js,20150508114741-create-notes.js,20150515125813-create-temp.js,20150702001020-update-to-0_3_1.js,20150915153700-change-notes-title-to-text.js,20160112220142-note-add-lastchange.js,20160420180355-note-add-alias.js,20160515114000-user-add-tokens.js,20160607060246-support-revision.js,20160703062241-support-authorship.js,20161009040430-support-delete-note.js,20161201050312-support-email-signin.js,20171009121200-longtext-for-mysql.js,20180209120907-longtext-of-authorship.js,20180306150303-fix-enum.js,20180326103000-use-text-in-tokens.js,20180525153000-user-add-delete-token.js,20200321153000-fix-account-deletion.js +HEDGEDOCUTIL_PAD_MAIL_TEMPLATE=/usr/local/etc/hedgedoc-util/mail-std.tpl