mirror of
https://git.pleroma.social/pleroma/relay.git
synced 2024-11-14 03:27:59 +00:00
Compare commits
7 commits
8bceca5c67
...
beaddca708
Author | SHA1 | Date | |
---|---|---|---|
beaddca708 | |||
b1a2989ba7 | |||
fd0b9fb723 | |||
ffed955ab3 | |||
71de40dfca | |||
7064adb000 | |||
437075e512 |
|
@ -5,7 +5,7 @@ ENV DOCKER_RUNNING=true
|
|||
|
||||
# setup various container properties
|
||||
VOLUME ["/data"]
|
||||
CMD ["python3", "-m", "relay"]
|
||||
CMD ["python3", "-m", "relay", "run"]
|
||||
EXPOSE 8080/tcp
|
||||
WORKDIR /opt/activityrelay
|
||||
|
||||
|
|
17
relay/dev.py → dev.py
Normal file → Executable file
17
relay/dev.py → dev.py
Normal file → Executable file
|
@ -1,3 +1,4 @@
|
|||
#!/usr/bin/env python3
|
||||
import click
|
||||
import platform
|
||||
import shutil
|
||||
|
@ -7,12 +8,10 @@ import time
|
|||
|
||||
from datetime import datetime, timedelta
|
||||
from pathlib import Path
|
||||
from relay import __version__, logger as logging
|
||||
from tempfile import TemporaryDirectory
|
||||
from typing import Sequence
|
||||
|
||||
from . import __version__
|
||||
from . import logger as logging
|
||||
|
||||
try:
|
||||
from watchdog.observers import Observer
|
||||
from watchdog.events import PatternMatchingEventHandler
|
||||
|
@ -22,8 +21,7 @@ except ImportError:
|
|||
pass
|
||||
|
||||
|
||||
SCRIPT = Path(__file__).parent
|
||||
REPO = SCRIPT.parent
|
||||
REPO = Path(__file__).parent
|
||||
IGNORE_EXT = {
|
||||
'.py',
|
||||
'.pyc'
|
||||
|
@ -58,8 +56,8 @@ def cli_lint(path: Path, strict: bool, watch: bool) -> None:
|
|||
handle_run_watcher([sys.executable, "-m", "relay.dev", "lint", str(path)], wait = True)
|
||||
return
|
||||
|
||||
flake8 = [sys.executable, '-m', 'flake8', str(path)]
|
||||
mypy = [sys.executable, '-m', 'mypy', str(path)]
|
||||
flake8 = [sys.executable, '-m', 'flake8', "dev.py", str(path)]
|
||||
mypy = [sys.executable, '-m', 'mypy', "dev.py", str(path)]
|
||||
|
||||
if strict:
|
||||
mypy.append('--strict')
|
||||
|
@ -108,7 +106,7 @@ def cli_build():
|
|||
cmd.append('--console')
|
||||
|
||||
# putting the spec path on a different drive than the source dir breaks
|
||||
if str(SCRIPT)[0] == tmp[0]:
|
||||
if str(REPO)[0] == tmp[0]:
|
||||
cmd.extend(['--specpath', tmp])
|
||||
|
||||
else:
|
||||
|
@ -136,7 +134,7 @@ def handle_run_watcher(*commands: Sequence[str], wait: bool = False):
|
|||
handler.run_procs()
|
||||
|
||||
watcher = Observer()
|
||||
watcher.schedule(handler, str(SCRIPT), recursive=True)
|
||||
watcher.schedule(handler, str(REPO), recursive=True)
|
||||
watcher.start()
|
||||
|
||||
try:
|
||||
|
@ -151,7 +149,6 @@ def handle_run_watcher(*commands: Sequence[str], wait: bool = False):
|
|||
watcher.join()
|
||||
|
||||
|
||||
|
||||
class WatchHandler(PatternMatchingEventHandler):
|
||||
patterns = ['*.py']
|
||||
|
23
docker.sh
23
docker.sh
|
@ -2,9 +2,21 @@
|
|||
|
||||
case $1 in
|
||||
install)
|
||||
if [[ -z ${2#$} ]]; then
|
||||
host=127.0.0.1
|
||||
else
|
||||
host=$2
|
||||
fi
|
||||
|
||||
if [[ -z ${3#$} ]]; then
|
||||
port=8080
|
||||
else
|
||||
port=$3
|
||||
fi
|
||||
|
||||
docker build -f Dockerfile -t activityrelay . && \
|
||||
docker volume create activityrelay-data && \
|
||||
docker run -it -p 8080:8080 -v activityrelay-data:/data --name activityrelay activityrelay
|
||||
docker run -it -p target=8080,published=${host}:${port} -v activityrelay-data:/data --name activityrelay activityrelay
|
||||
;;
|
||||
|
||||
uninstall)
|
||||
|
@ -22,6 +34,10 @@ case $1 in
|
|||
docker stop activityrelay
|
||||
;;
|
||||
|
||||
restart)
|
||||
docker restart activityrelay
|
||||
;;
|
||||
|
||||
manage)
|
||||
shift
|
||||
docker exec -it activityrelay python3 -m relay "$@"
|
||||
|
@ -61,10 +77,7 @@ case $1 in
|
|||
printf "$COLS" "- edit" "Edit the relay's config in \$EDITOR"
|
||||
printf "$COLS" "- shell" "Drop into a bash shell on the running container"
|
||||
printf "$COLS" "- rescue" "Drop into a bash shell on a temp container with the data volume mounted"
|
||||
printf "$COLS" "- install" "Build the image, create a new container and volume, and run relay setup"
|
||||
printf "$COLS" "- install [address] [port]" "Build the image, create a new container and volume, and run relay setup"
|
||||
printf "$COLS" "- uninstall" "Delete the relay image, container, and volume"
|
||||
|
||||
echo ""
|
||||
echo "Note: This script may not work. It is recommended to manually install and manage the container if you know what you're doing."
|
||||
;;
|
||||
esac
|
||||
|
|
|
@ -154,6 +154,8 @@ When enabled, instances that try to follow the relay will have to be manually ap
|
|||
|
||||
Maximum level of messages to log.
|
||||
|
||||
Note: Changing this setting via CLI does not actually take effect until restart.
|
||||
|
||||
Valid values: `DEBUG`, `VERBOSE`, `INFO`, `WARNING`, `ERROR`, `CRITICAL`
|
||||
|
||||
log-level: INFO
|
||||
|
|
|
@ -13,9 +13,9 @@ the [official pipx docs](https://pypa.github.io/pipx/installation/) for more in-
|
|||
|
||||
python3 -m pip install pipx
|
||||
|
||||
Now simply install ActivityRelay directly from git
|
||||
Now simply install ActivityRelay from pypi
|
||||
|
||||
pipx install git+https://git.pleroma.social/pleroma/relay@0.3.0
|
||||
pipx install activityrelay
|
||||
|
||||
Or from a cloned git repo.
|
||||
|
||||
|
@ -36,10 +36,9 @@ be installed via [pyenv](https://github.com/pyenv/pyenv).
|
|||
|
||||
## Pip
|
||||
|
||||
The instructions for installation via pip are very similar to pipx. Installation can be done from
|
||||
git
|
||||
The instructions for installation via pip are very similar to pipx
|
||||
|
||||
python3 -m pip install git+https://git.pleroma.social/pleroma/relay@0.3.0
|
||||
python3 -m pip install activityrelay
|
||||
|
||||
or a cloned git repo.
|
||||
|
||||
|
@ -58,10 +57,11 @@ And start the relay when finished
|
|||
|
||||
Installation and management via Docker can be handled with the `docker.sh` script. To install
|
||||
ActivityRelay, run the install command. Once the image is built and the container is created,
|
||||
your will be asked to fill out some config options for your relay.
|
||||
you will be asked to fill out some config options for your relay. An address and port can be
|
||||
specified to change what the relay listens on.
|
||||
|
||||
./docker.sh install
|
||||
./docker.sh install 0.0.0.0 6942
|
||||
|
||||
Finally start it up. It will be listening on TCP port 8080.
|
||||
Finally start it up. It will be listening on TCP localhost:8080 by default.
|
||||
|
||||
./docker.sh start
|
||||
|
|
|
@ -55,7 +55,7 @@ class Application(web.Application):
|
|||
DEFAULT: Application | None = None
|
||||
|
||||
|
||||
def __init__(self, cfgpath: str | None, dev: bool = False):
|
||||
def __init__(self, cfgpath: Path | None, dev: bool = False):
|
||||
web.Application.__init__(self,
|
||||
middlewares = [
|
||||
handle_api_path,
|
||||
|
|
|
@ -33,7 +33,7 @@ else:
|
|||
DOCKER_VALUES = {
|
||||
'listen': '0.0.0.0',
|
||||
'port': 8080,
|
||||
'sq_path': '/data/relay.jsonld'
|
||||
'sq_path': '/data/relay.sqlite3'
|
||||
}
|
||||
|
||||
|
||||
|
@ -65,7 +65,7 @@ class Config:
|
|||
rd_prefix: str = 'activityrelay'
|
||||
|
||||
|
||||
def __init__(self, path: str | None = None, load: bool = False):
|
||||
def __init__(self, path: Path | None = None, load: bool = False):
|
||||
self.path = Config.get_config_dir(path)
|
||||
self.reset()
|
||||
|
||||
|
@ -92,9 +92,12 @@ class Config:
|
|||
|
||||
|
||||
@staticmethod
|
||||
def get_config_dir(path: str | None = None) -> Path:
|
||||
if path:
|
||||
return Path(path).expanduser().resolve()
|
||||
def get_config_dir(path: Path | str | None = None) -> Path:
|
||||
if isinstance(path, str):
|
||||
path = Path(path)
|
||||
|
||||
if path is not None:
|
||||
return path.expanduser().resolve()
|
||||
|
||||
paths = (
|
||||
Path("relay.yaml").resolve(),
|
||||
|
|
271
relay/manage.py
271
relay/manage.py
|
@ -1,11 +1,10 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import Crypto
|
||||
import aputils
|
||||
import asyncio
|
||||
import click
|
||||
import json
|
||||
import os
|
||||
import platform
|
||||
import typing
|
||||
|
||||
from pathlib import Path
|
||||
|
@ -32,139 +31,152 @@ def check_alphanumeric(text: str) -> str:
|
|||
return text
|
||||
|
||||
|
||||
@click.group('cli', context_settings={'show_default': True}, invoke_without_command=True)
|
||||
@click.option('--config', '-c', help='path to the relay\'s config')
|
||||
@click.version_option(version=__version__, prog_name='ActivityRelay')
|
||||
@click.group('cli', context_settings = {'show_default': True})
|
||||
@click.option('--config', '-c', type = Path, help = 'path to the relay\'s config')
|
||||
@click.version_option(version = __version__, prog_name = 'ActivityRelay')
|
||||
@click.pass_context
|
||||
def cli(ctx: click.Context, config: str | None) -> None:
|
||||
def cli(ctx: click.Context, config: Path | None) -> None:
|
||||
if IS_DOCKER:
|
||||
config = Path("/data/relay.yaml")
|
||||
|
||||
# The database was named "relay.jsonld" even though it's an sqlite file. Fix it.
|
||||
db = Path('/data/relay.sqlite3')
|
||||
wrongdb = Path('/data/relay.jsonld')
|
||||
|
||||
if wrongdb.exists() and not db.exists():
|
||||
try:
|
||||
with wrongdb.open('rb') as fd:
|
||||
json.load(fd)
|
||||
|
||||
except json.JSONDecodeError:
|
||||
wrongdb.rename(db)
|
||||
|
||||
ctx.obj = Application(config)
|
||||
|
||||
if not ctx.invoked_subcommand:
|
||||
if ctx.obj.config.domain.endswith('example.com'):
|
||||
cli_setup.callback() # type: ignore
|
||||
|
||||
else:
|
||||
click.echo(
|
||||
'[DEPRECATED] Running the relay without the "run" command will be removed in the ' +
|
||||
'future.'
|
||||
)
|
||||
|
||||
cli_run.callback() # type: ignore
|
||||
|
||||
|
||||
@cli.command('setup')
|
||||
@click.option('--skip-questions', '-s', is_flag = True, help = 'Just setup the database')
|
||||
@click.pass_context
|
||||
def cli_setup(ctx: click.Context) -> None:
|
||||
def cli_setup(ctx: click.Context, skip_questions: bool) -> None:
|
||||
'Generate a new config and create the database'
|
||||
|
||||
while True:
|
||||
ctx.obj.config.domain = click.prompt(
|
||||
'What domain will the relay be hosted on?',
|
||||
default = ctx.obj.config.domain
|
||||
if ctx.obj.signer is not None:
|
||||
if not click.prompt('The database is already setup. Are you sure you want to continue?'):
|
||||
return
|
||||
|
||||
if skip_questions and ctx.obj.config.domain.endswith('example.com'):
|
||||
click.echo('You cannot skip the questions if the relay is not configured yet')
|
||||
return
|
||||
|
||||
if not skip_questions:
|
||||
while True:
|
||||
ctx.obj.config.domain = click.prompt(
|
||||
'What domain will the relay be hosted on?',
|
||||
default = ctx.obj.config.domain
|
||||
)
|
||||
|
||||
if not ctx.obj.config.domain.endswith('example.com'):
|
||||
break
|
||||
|
||||
click.echo('The domain must not end with "example.com"')
|
||||
|
||||
if not IS_DOCKER:
|
||||
ctx.obj.config.listen = click.prompt(
|
||||
'Which address should the relay listen on?',
|
||||
default = ctx.obj.config.listen
|
||||
)
|
||||
|
||||
ctx.obj.config.port = click.prompt(
|
||||
'What TCP port should the relay listen on?',
|
||||
default = ctx.obj.config.port,
|
||||
type = int
|
||||
)
|
||||
|
||||
ctx.obj.config.db_type = click.prompt(
|
||||
'Which database backend will be used?',
|
||||
default = ctx.obj.config.db_type,
|
||||
type = click.Choice(['postgres', 'sqlite'], case_sensitive = False)
|
||||
)
|
||||
|
||||
if not ctx.obj.config.domain.endswith('example.com'):
|
||||
break
|
||||
if ctx.obj.config.db_type == 'sqlite' and not IS_DOCKER:
|
||||
ctx.obj.config.sq_path = click.prompt(
|
||||
'Where should the database be stored?',
|
||||
default = ctx.obj.config.sq_path
|
||||
)
|
||||
|
||||
click.echo('The domain must not end with "example.com"')
|
||||
elif ctx.obj.config.db_type == 'postgres':
|
||||
ctx.obj.config.pg_name = click.prompt(
|
||||
'What is the name of the database?',
|
||||
default = ctx.obj.config.pg_name
|
||||
)
|
||||
|
||||
if not IS_DOCKER:
|
||||
ctx.obj.config.listen = click.prompt(
|
||||
'Which address should the relay listen on?',
|
||||
default = ctx.obj.config.listen
|
||||
ctx.obj.config.pg_host = click.prompt(
|
||||
'What IP address, hostname, or unix socket does the server listen on?',
|
||||
default = ctx.obj.config.pg_host,
|
||||
type = int
|
||||
)
|
||||
|
||||
ctx.obj.config.pg_port = click.prompt(
|
||||
'What port does the server listen on?',
|
||||
default = ctx.obj.config.pg_port,
|
||||
type = int
|
||||
)
|
||||
|
||||
ctx.obj.config.pg_user = click.prompt(
|
||||
'Which user will authenticate with the server?',
|
||||
default = ctx.obj.config.pg_user
|
||||
)
|
||||
|
||||
ctx.obj.config.pg_pass = click.prompt(
|
||||
'User password',
|
||||
hide_input = True,
|
||||
show_default = False,
|
||||
default = ctx.obj.config.pg_pass or ""
|
||||
) or None
|
||||
|
||||
ctx.obj.config.ca_type = click.prompt(
|
||||
'Which caching backend?',
|
||||
default = ctx.obj.config.ca_type,
|
||||
type = click.Choice(['database', 'redis'], case_sensitive = False)
|
||||
)
|
||||
|
||||
ctx.obj.config.port = click.prompt(
|
||||
'What TCP port should the relay listen on?',
|
||||
default = ctx.obj.config.port,
|
||||
type = int
|
||||
)
|
||||
if ctx.obj.config.ca_type == 'redis':
|
||||
ctx.obj.config.rd_host = click.prompt(
|
||||
'What IP address, hostname, or unix socket does the server listen on?',
|
||||
default = ctx.obj.config.rd_host
|
||||
)
|
||||
|
||||
ctx.obj.config.db_type = click.prompt(
|
||||
'Which database backend will be used?',
|
||||
default = ctx.obj.config.db_type,
|
||||
type = click.Choice(['postgres', 'sqlite'], case_sensitive = False)
|
||||
)
|
||||
ctx.obj.config.rd_port = click.prompt(
|
||||
'What port does the server listen on?',
|
||||
default = ctx.obj.config.rd_port,
|
||||
type = int
|
||||
)
|
||||
|
||||
if ctx.obj.config.db_type == 'sqlite':
|
||||
ctx.obj.config.sq_path = click.prompt(
|
||||
'Where should the database be stored?',
|
||||
default = ctx.obj.config.sq_path
|
||||
)
|
||||
ctx.obj.config.rd_user = click.prompt(
|
||||
'Which user will authenticate with the server',
|
||||
default = ctx.obj.config.rd_user
|
||||
)
|
||||
|
||||
elif ctx.obj.config.db_type == 'postgres':
|
||||
ctx.obj.config.pg_name = click.prompt(
|
||||
'What is the name of the database?',
|
||||
default = ctx.obj.config.pg_name
|
||||
)
|
||||
ctx.obj.config.rd_pass = click.prompt(
|
||||
'User password',
|
||||
hide_input = True,
|
||||
show_default = False,
|
||||
default = ctx.obj.config.rd_pass or ""
|
||||
) or None
|
||||
|
||||
ctx.obj.config.pg_host = click.prompt(
|
||||
'What IP address, hostname, or unix socket does the server listen on?',
|
||||
default = ctx.obj.config.pg_host,
|
||||
type = int
|
||||
)
|
||||
ctx.obj.config.rd_database = click.prompt(
|
||||
'Which database number to use?',
|
||||
default = ctx.obj.config.rd_database,
|
||||
type = int
|
||||
)
|
||||
|
||||
ctx.obj.config.pg_port = click.prompt(
|
||||
'What port does the server listen on?',
|
||||
default = ctx.obj.config.pg_port,
|
||||
type = int
|
||||
)
|
||||
ctx.obj.config.rd_prefix = click.prompt(
|
||||
'What text should each cache key be prefixed with?',
|
||||
default = ctx.obj.config.rd_database,
|
||||
type = check_alphanumeric
|
||||
)
|
||||
|
||||
ctx.obj.config.pg_user = click.prompt(
|
||||
'Which user will authenticate with the server?',
|
||||
default = ctx.obj.config.pg_user
|
||||
)
|
||||
|
||||
ctx.obj.config.pg_pass = click.prompt(
|
||||
'User password',
|
||||
hide_input = True,
|
||||
show_default = False,
|
||||
default = ctx.obj.config.pg_pass or ""
|
||||
) or None
|
||||
|
||||
ctx.obj.config.ca_type = click.prompt(
|
||||
'Which caching backend?',
|
||||
default = ctx.obj.config.ca_type,
|
||||
type = click.Choice(['database', 'redis'], case_sensitive = False)
|
||||
)
|
||||
|
||||
if ctx.obj.config.ca_type == 'redis':
|
||||
ctx.obj.config.rd_host = click.prompt(
|
||||
'What IP address, hostname, or unix socket does the server listen on?',
|
||||
default = ctx.obj.config.rd_host
|
||||
)
|
||||
|
||||
ctx.obj.config.rd_port = click.prompt(
|
||||
'What port does the server listen on?',
|
||||
default = ctx.obj.config.rd_port,
|
||||
type = int
|
||||
)
|
||||
|
||||
ctx.obj.config.rd_user = click.prompt(
|
||||
'Which user will authenticate with the server',
|
||||
default = ctx.obj.config.rd_user
|
||||
)
|
||||
|
||||
ctx.obj.config.rd_pass = click.prompt(
|
||||
'User password',
|
||||
hide_input = True,
|
||||
show_default = False,
|
||||
default = ctx.obj.config.rd_pass or ""
|
||||
) or None
|
||||
|
||||
ctx.obj.config.rd_database = click.prompt(
|
||||
'Which database number to use?',
|
||||
default = ctx.obj.config.rd_database,
|
||||
type = int
|
||||
)
|
||||
|
||||
ctx.obj.config.rd_prefix = click.prompt(
|
||||
'What text should each cache key be prefixed with?',
|
||||
default = ctx.obj.config.rd_database,
|
||||
type = check_alphanumeric
|
||||
)
|
||||
|
||||
ctx.obj.config.save()
|
||||
ctx.obj.config.save()
|
||||
|
||||
config = {
|
||||
'private-key': aputils.Signer.new('n/a').export()
|
||||
|
@ -174,7 +186,11 @@ def cli_setup(ctx: click.Context) -> None:
|
|||
for key, value in config.items():
|
||||
conn.put_config(key, value)
|
||||
|
||||
if not IS_DOCKER and click.confirm('Relay all setup! Would you like to run it now?'):
|
||||
if IS_DOCKER:
|
||||
click.echo("Relay all setup! Start the container to run the relay.")
|
||||
return
|
||||
|
||||
if click.confirm('Relay all setup! Would you like to run it now?'):
|
||||
cli_run.callback() # type: ignore
|
||||
|
||||
|
||||
|
@ -184,28 +200,13 @@ def cli_setup(ctx: click.Context) -> None:
|
|||
def cli_run(ctx: click.Context, dev: bool = False) -> None:
|
||||
'Run the relay'
|
||||
|
||||
if ctx.obj.config.domain.endswith('example.com') or not ctx.obj.signer:
|
||||
click.echo(
|
||||
'Relay is not set up. Please edit your relay config or run "activityrelay setup".'
|
||||
)
|
||||
if ctx.obj.config.domain.endswith('example.com') or ctx.obj.signer is None:
|
||||
if not IS_DOCKER:
|
||||
click.echo('Relay is not set up. Please run "activityrelay setup".')
|
||||
|
||||
return
|
||||
|
||||
vers_split = platform.python_version().split('.')
|
||||
pip_command = 'pip3 uninstall pycrypto && pip3 install pycryptodome'
|
||||
|
||||
if Crypto.__version__ == '2.6.1':
|
||||
if int(vers_split[1]) > 7:
|
||||
click.echo(
|
||||
'Error: PyCrypto is broken on Python 3.8+. Please replace it with pycryptodome ' +
|
||||
'before running again. Exiting...'
|
||||
)
|
||||
|
||||
click.echo(pip_command)
|
||||
return
|
||||
|
||||
click.echo('Warning: PyCrypto is old and should be replaced with pycryptodome')
|
||||
click.echo(pip_command)
|
||||
cli_setup.callback() # type: ignore
|
||||
return
|
||||
|
||||
ctx.obj['dev'] = dev
|
||||
|
|
Loading…
Reference in a new issue