Start trimming some of the __main__ crud

This commit is contained in:
Reid 'arrdem' McKenzie 2022-11-20 22:34:07 -07:00
parent c21ee1fe48
commit 01c24f64ab
5 changed files with 102 additions and 120 deletions

View file

@ -10,14 +10,13 @@ USER app
WORKDIR /app WORKDIR /app
ENV PATH="/app/.local/bin:${PATH}" ENV PATH="/app/.local/bin:${PATH}"
ENV PYTHONPATH="/app:${PYTHONPATH}" ENV PYTHONPATH="/app:${PYTHONPATH}"
ENV DOCKER_RUNNING=true # Trivialize detecting dockerization # Trivialize detecting dockerization
ENV DOCKER_RUNNING=true
### App specific crap ### App specific crap
# Deps vary least so do them first # Deps vary least so do them first
RUN pip install --user install aiohttp aiohttp_basicauth async_lru cachetools click pycryptodome pyyaml retry RUN pip install --user install aiohttp aiohttp_basicauth async_lru cachetools click pycryptodome pyyaml retry
COPY --chown=app:app src/python . COPY --chown=app:app src/python relay.yaml relay.jsonld .
COPY --chown=app:app relay.yaml .
COPY --chown=app:app relay.jsonld .
CMD ["python3", "relay/__main__.py", "-c", "relay.yaml"] CMD ["python3", "relay/__main__.py", "-c", "relay.yaml", "run"]

View file

@ -4,4 +4,4 @@ cd "$(realpath $(dirname $0))"
bazel build ... bazel build ...
exec ../../bazel-bin/projects/activitypub_relay/activitypub_relay -c $(realpath ./relay.yaml) exec ../../bazel-bin/projects/activitypub_relay/activitypub_relay -c $(realpath ./relay.yaml) run

View file

@ -1,3 +1 @@
__version__ = "0.2.2" __version__ = "0.2.2"
from . import logger

View file

@ -2,6 +2,8 @@ import Crypto
import asyncio import asyncio
import click import click
import platform import platform
import logging
import os
from urllib.parse import urlparse from urllib.parse import urlparse
@ -10,9 +12,6 @@ from relay.application import Application, request_id_middleware
from relay.config import relay_software_names from relay.config import relay_software_names
app = None
@click.group( @click.group(
"cli", context_settings={"show_default": True}, invoke_without_command=True "cli", context_settings={"show_default": True}, invoke_without_command=True
) )
@ -20,40 +19,47 @@ app = None
@click.version_option(version=__version__, prog_name="ActivityRelay") @click.version_option(version=__version__, prog_name="ActivityRelay")
@click.pass_context @click.pass_context
def cli(ctx, config): def cli(ctx, config):
global app ctx.obj = Application(config, middlewares=[request_id_middleware])
app = Application(config, middlewares=[request_id_middleware])
if not ctx.invoked_subcommand: level = {
if app.config.host.endswith("example.com"): "debug": logging.DEBUG,
relay_setup.callback() "info": logging.INFO,
"error": logging.ERROR,
"critical": logging.CRITICAL,
"fatal": logging.FATAL,
}.get(os.getenv("LOG_LEVE", "INFO").lower(), logging.INFO)
else: logging.basicConfig(
relay_run.callback() level=level,
format="[%(asctime)s] %(levelname)s: %(message)s",
)
@cli.group("inbox") @cli.group("inbox")
@click.pass_context @click.pass_obj
def cli_inbox(ctx): def cli_inbox(ctx: Application):
"Manage the inboxes in the database" "Manage the inboxes in the database"
pass pass
@cli_inbox.command("list") @cli_inbox.command("list")
def cli_inbox_list(): @click.pass_obj
def cli_inbox_list(obj: Application):
"List the connected instances or relays" "List the connected instances or relays"
click.echo("Connected to the following instances or relays:") click.echo("Connected to the following instances or relays:")
for inbox in app.database.inboxes: for inbox in obj.database.inboxes:
click.echo(f"- {inbox}") click.echo(f"- {inbox}")
@cli_inbox.command("follow") @cli_inbox.command("follow")
@click.argument("actor") @click.argument("actor")
def cli_inbox_follow(actor): @click.pass_obj
def cli_inbox_follow(obj: Application, actor):
"Follow an actor (Relay must be running)" "Follow an actor (Relay must be running)"
if app.config.is_banned(actor): if obj.config.is_banned(actor):
return click.echo(f"Error: Refusing to follow banned actor: {actor}") return click.echo(f"Error: Refusing to follow banned actor: {actor}")
if not actor.startswith("http"): if not actor.startswith("http"):
@ -64,14 +70,14 @@ def cli_inbox_follow(actor):
domain = urlparse(actor).hostname domain = urlparse(actor).hostname
try: try:
inbox_data = app.database["relay-list"][domain] inbox_data = obj.database["relay-list"][domain]
inbox = inbox_data["inbox"] inbox = inbox_data["inbox"]
except KeyError: except KeyError:
actor_data = asyncio.run(misc.request(actor)) actor_data = asyncio.run(misc.request(actor))
inbox = actor_data.shared_inbox inbox = actor_data.shared_inbox
message = misc.Message.new_follow(host=app.config.host, actor=actor.id) message = misc.Message.new_follow(host=obj.config.host, actor=actor.id)
asyncio.run(misc.request(inbox, message)) asyncio.run(misc.request(inbox, message))
click.echo(f"Sent follow message to actor: {actor}") click.echo(f"Sent follow message to actor: {actor}")
@ -79,7 +85,8 @@ def cli_inbox_follow(actor):
@cli_inbox.command("unfollow") @cli_inbox.command("unfollow")
@click.argument("actor") @click.argument("actor")
def cli_inbox_unfollow(actor): @click.pass_obj
def cli_inbox_unfollow(obj: Application, actor):
"Unfollow an actor (Relay must be running)" "Unfollow an actor (Relay must be running)"
if not actor.startswith("http"): if not actor.startswith("http"):
@ -90,22 +97,22 @@ def cli_inbox_unfollow(actor):
domain = urlparse(actor).hostname domain = urlparse(actor).hostname
try: try:
inbox_data = app.database["relay-list"][domain] inbox_data = obj.database["relay-list"][domain]
inbox = inbox_data["inbox"] inbox = inbox_data["inbox"]
message = misc.Message.new_unfollow( message = misc.Message.new_unfollow(
host=app.config.host, actor=actor, follow=inbox_data["followid"] host=obj.config.host, actor=actor, follow=inbox_data["followid"]
) )
except KeyError: except KeyError:
actor_data = asyncio.run(misc.request(actor)) actor_data = asyncio.run(misc.request(actor))
inbox = actor_data.shared_inbox inbox = actor_data.shared_inbox
message = misc.Message.new_unfollow( message = misc.Message.new_unfollow(
host=app.config.host, host=obj.config.host,
actor=actor, actor=actor,
follow={ follow={
"type": "Follow", "type": "Follow",
"object": actor, "object": actor,
"actor": f"https://{app.config.host}/actor", "actor": f"https://{obj.config.host}/actor",
}, },
) )
@ -115,17 +122,18 @@ def cli_inbox_unfollow(actor):
@cli_inbox.command("add") @cli_inbox.command("add")
@click.argument("inbox") @click.argument("inbox")
def cli_inbox_add(inbox): @click.pass_obj
def cli_inbox_add(obj: Application, inbox):
"Add an inbox to the database" "Add an inbox to the database"
if not inbox.startswith("http"): if not inbox.startswith("http"):
inbox = f"https://{inbox}/inbox" inbox = f"https://{inbox}/inbox"
if app.config.is_banned(inbox): if obj.config.is_banned(inbox):
return click.echo(f"Error: Refusing to add banned inbox: {inbox}") return click.echo(f"Error: Refusing to add banned inbox: {inbox}")
if app.database.add_inbox(inbox): if obj.database.add_inbox(inbox):
app.database.save() obj.database.save()
return click.echo(f"Added inbox to the database: {inbox}") return click.echo(f"Added inbox to the database: {inbox}")
click.echo(f"Error: Inbox already in database: {inbox}") click.echo(f"Error: Inbox already in database: {inbox}")
@ -133,18 +141,19 @@ def cli_inbox_add(inbox):
@cli_inbox.command("remove") @cli_inbox.command("remove")
@click.argument("inbox") @click.argument("inbox")
def cli_inbox_remove(inbox): @click.pass_obj
def cli_inbox_remove(obj: Application, inbox):
"Remove an inbox from the database" "Remove an inbox from the database"
try: try:
dbinbox = app.database.get_inbox(inbox, fail=True) dbinbox = obj.database.get_inbox(inbox, fail=True)
except KeyError: except KeyError:
click.echo(f"Error: Inbox does not exist: {inbox}") click.echo(f"Error: Inbox does not exist: {inbox}")
return return
app.database.del_inbox(dbinbox["domain"]) obj.database.del_inbox(dbinbox["domain"])
app.database.save() obj.database.save()
click.echo(f"Removed inbox from the database: {inbox}") click.echo(f"Removed inbox from the database: {inbox}")
@ -156,28 +165,30 @@ def cli_instance():
@cli_instance.command("list") @cli_instance.command("list")
def cli_instance_list(): @click.pass_obj
def cli_instance_list(obj: Application):
"List all banned instances" "List all banned instances"
click.echo("Banned instances or relays:") click.echo("Banned instances or relays:")
for domain in app.config.blocked_instances: for domain in obj.config.blocked_instances:
click.echo(f"- {domain}") click.echo(f"- {domain}")
@cli_instance.command("ban") @cli_instance.command("ban")
@click.argument("target") @click.argument("target")
def cli_instance_ban(target): @click.pass_obj
def cli_instance_ban(obj: Application, target):
"Ban an instance and remove the associated inbox if it exists" "Ban an instance and remove the associated inbox if it exists"
if target.startswith("http"): if target.startswith("http"):
target = urlparse(target).hostname target = urlparse(target).hostname
if app.config.ban_instance(target): if obj.config.ban_instance(target):
app.config.save() obj.config.save()
if app.database.del_inbox(target): if obj.database.del_inbox(target):
app.database.save() obj.database.save()
click.echo(f"Banned instance: {target}") click.echo(f"Banned instance: {target}")
return return
@ -187,11 +198,12 @@ def cli_instance_ban(target):
@cli_instance.command("unban") @cli_instance.command("unban")
@click.argument("target") @click.argument("target")
def cli_instance_unban(target): @click.pass_obj
def cli_instance_unban(obj: Application, target):
"Unban an instance" "Unban an instance"
if app.config.unban_instance(target): if obj.config.unban_instance(target):
app.config.save() obj.config.save()
click.echo(f"Unbanned instance: {target}") click.echo(f"Unbanned instance: {target}")
return return
@ -206,12 +218,13 @@ def cli_software():
@cli_software.command("list") @cli_software.command("list")
def cli_software_list(): @click.pass_obj
def cli_software_list(obj: Application):
"List all banned software" "List all banned software"
click.echo("Banned software:") click.echo("Banned software:")
for software in app.config.blocked_software: for software in obj.config.blocked_software:
click.echo(f"- {software}") click.echo(f"- {software}")
@ -224,14 +237,15 @@ def cli_software_list():
help="Treat NAME like a domain and try to fet the software name from nodeinfo", help="Treat NAME like a domain and try to fet the software name from nodeinfo",
) )
@click.argument("name") @click.argument("name")
def cli_software_ban(name, fetch_nodeinfo): @click.pass_obj
def cli_software_ban(obj: Application, name, fetch_nodeinfo):
"Ban software. Use RELAYS for NAME to ban relays" "Ban software. Use RELAYS for NAME to ban relays"
if name == "RELAYS": if name == "RELAYS":
for name in relay_software_names: for name in relay_software_names:
app.config.ban_software(name) obj.config.ban_software(name)
app.config.save() obj.config.save()
return click.echo("Banned all relay software") return click.echo("Banned all relay software")
if fetch_nodeinfo: if fetch_nodeinfo:
@ -243,7 +257,7 @@ def cli_software_ban(name, fetch_nodeinfo):
name = software name = software
if config.ban_software(name): if config.ban_software(name):
app.config.save() obj.config.save()
return click.echo(f"Banned software: {name}") return click.echo(f"Banned software: {name}")
click.echo(f"Software already banned: {name}") click.echo(f"Software already banned: {name}")
@ -258,12 +272,13 @@ def cli_software_ban(name, fetch_nodeinfo):
help="Treat NAME like a domain and try to fet the software name from nodeinfo", help="Treat NAME like a domain and try to fet the software name from nodeinfo",
) )
@click.argument("name") @click.argument("name")
def cli_software_unban(name, fetch_nodeinfo): @click.pass_obj
def cli_software_unban(obj: Application, name, fetch_nodeinfo):
"Ban software. Use RELAYS for NAME to unban relays" "Ban software. Use RELAYS for NAME to unban relays"
if name == "RELAYS": if name == "RELAYS":
for name in relay_software_names: for name in relay_software_names:
app.config.unban_software(name) obj.config.unban_software(name)
config.save() config.save()
return click.echo("Unbanned all relay software") return click.echo("Unbanned all relay software")
@ -276,8 +291,8 @@ def cli_software_unban(name, fetch_nodeinfo):
name = software name = software
if app.config.unban_software(name): if obj.config.unban_software(name):
app.config.save() obj.config.save()
return click.echo(f"Unbanned software: {name}") return click.echo(f"Unbanned software: {name}")
click.echo(f"Software wasn't banned: {name}") click.echo(f"Software wasn't banned: {name}")
@ -290,81 +305,86 @@ def cli_whitelist():
@cli_whitelist.command("list") @cli_whitelist.command("list")
def cli_whitelist_list(): @click.pass_obj
def cli_whitelist_list(obj: Application):
click.echo("Current whitelisted domains") click.echo("Current whitelisted domains")
for domain in app.config.whitelist: for domain in obj.config.whitelist:
click.echo(f"- {domain}") click.echo(f"- {domain}")
@cli_whitelist.command("add") @cli_whitelist.command("add")
@click.argument("instance") @click.argument("instance")
def cli_whitelist_add(instance): @click.pass_obj
def cli_whitelist_add(obj: Application, instance):
"Add an instance to the whitelist" "Add an instance to the whitelist"
if not app.config.add_whitelist(instance): if not obj.config.add_whitelist(instance):
return click.echo(f"Instance already in the whitelist: {instance}") return click.echo(f"Instance already in the whitelist: {instance}")
app.config.save() obj.config.save()
click.echo(f"Instance added to the whitelist: {instance}") click.echo(f"Instance added to the whitelist: {instance}")
@cli_whitelist.command("remove") @cli_whitelist.command("remove")
@click.argument("instance") @click.argument("instance")
def cli_whitelist_remove(instance): @click.pass_obj
def cli_whitelist_remove(obj: Application, instance):
"Remove an instance from the whitelist" "Remove an instance from the whitelist"
if not app.config.del_whitelist(instance): if not obj.config.del_whitelist(instance):
return click.echo(f"Instance not in the whitelist: {instance}") return click.echo(f"Instance not in the whitelist: {instance}")
app.config.save() obj.config.save()
if app.config.whitelist_enabled: if obj.config.whitelist_enabled:
if app.database.del_inbox(inbox): if obj.database.del_inbox(inbox):
app.database.save() obj.database.save()
click.echo(f"Removed instance from the whitelist: {instance}") click.echo(f"Removed instance from the whitelist: {instance}")
@cli.command("setup") @cli.command("setup")
def relay_setup(): @click.pass_obj
def relay_setup(obj: Application):
"Generate a new config" "Generate a new config"
while True: while True:
app.config.host = click.prompt( obj.config.host = click.prompt(
"What domain will the relay be hosted on?", default=app.config.host "What domain will the relay be hosted on?", default=obj.config.host
) )
if not app.config.host.endswith("example.com"): if not obj.config.host.endswith("example.com"):
break break
click.echo("The domain must not be example.com") click.echo("The domain must not be example.com")
app.config.listen = click.prompt( obj.config.listen = click.prompt(
"Which address should the relay listen on?", default=app.config.listen "Which address should the relay listen on?", default=obj.config.listen
) )
while True: while True:
app.config.port = click.prompt( obj.config.port = click.prompt(
"What TCP port should the relay listen on?", "What TCP port should the relay listen on?",
default=app.config.port, default=obj.config.port,
type=int, type=int,
) )
break break
app.config.save() obj.config.save()
if not app["is_docker"] and click.confirm( if not obj["is_docker"] and click.confirm(
"Relay all setup! Would you like to run it now?" "Relay all setup! Would you like to run it now?"
): ):
relay_run.callback() relay_run.callback()
@cli.command("run") @cli.command("run")
def relay_run(): @click.pass_obj
def relay_run(obj: Application):
"Run the relay" "Run the relay"
if app.config.host.endswith("example.com"): if obj.config.host.endswith("example.com"):
return click.echo( return click.echo(
'Relay is not set up. Please edit your relay config or run "activityrelay setup".' 'Relay is not set up. Please edit your relay config or run "activityrelay setup".'
) )
@ -385,12 +405,12 @@ def relay_run():
) )
return click.echo(pip_command) return click.echo(pip_command)
if not misc.check_open_port(app.config.listen, app.config.port): if not misc.check_open_port(obj.config.listen, obj.config.port):
return click.echo( return click.echo(
f"Error: A server is already running on port {app.config.port}" f"Error: A server is already running on port {obj.config.port}"
) )
app.run() obj.run()
if __name__ == "__main__": if __name__ == "__main__":

View file

@ -1,35 +0,0 @@
import logging
import os
from pathlib import Path
## Get log level and file from environment if possible
env_log_level = os.environ.get("LOG_LEVEL", "INFO").upper()
try:
env_log_file = Path(os.environ.get("LOG_FILE")).expanduser().resolve()
except TypeError:
env_log_file = None
## Make sure the level from the environment is valid
try:
log_level = getattr(logging, env_log_level)
except AttributeError:
log_level = logging.INFO
## Set logging config
handlers = [logging.StreamHandler()]
if env_log_file:
handlers.append(logging.FileHandler(env_log_file))
logging.basicConfig(
level=log_level,
format="[%(asctime)s] %(levelname)s: %(message)s",
handlers=handlers,
)