2022-11-27 00:59:20 +00:00
|
|
|
import os
|
2022-12-13 13:27:09 +00:00
|
|
|
import sys
|
2022-05-06 07:04:51 +00:00
|
|
|
import yaml
|
|
|
|
|
2022-11-27 00:59:20 +00:00
|
|
|
from functools import cached_property
|
2022-05-06 07:04:51 +00:00
|
|
|
from pathlib import Path
|
2022-12-20 11:01:20 +00:00
|
|
|
from platform import system
|
2022-05-06 07:04:51 +00:00
|
|
|
|
2022-12-13 13:27:09 +00:00
|
|
|
from .misc import AppBase, DotDict
|
|
|
|
|
|
|
|
|
|
|
|
DEFAULTS = {
|
|
|
|
'general_listen': '0.0.0.0',
|
|
|
|
'general_port': 8080,
|
|
|
|
'general_host': 'relay.example.com',
|
|
|
|
'database_type': 'sqlite',
|
|
|
|
'database_min_connections': 0,
|
|
|
|
'database_max_connections': 10,
|
2022-12-20 11:01:20 +00:00
|
|
|
'sqlite_database': Path('relay.sqlite3'),
|
2022-12-13 13:27:09 +00:00
|
|
|
'postgres_database': 'activityrelay',
|
|
|
|
'postgres_hostname': None,
|
|
|
|
'postgres_port': None,
|
|
|
|
'postgres_username': None,
|
|
|
|
'postgres_password': None,
|
|
|
|
'mysql_database': 'activityrelay',
|
|
|
|
'mysql_hostname': None,
|
|
|
|
'mysql_port': None,
|
|
|
|
'mysql_username': None,
|
|
|
|
'mysql_password': None
|
|
|
|
}
|
|
|
|
|
|
|
|
CATEGORY_NAMES = [
|
|
|
|
'general',
|
|
|
|
'database',
|
2022-12-20 12:59:58 +00:00
|
|
|
'sqlite',
|
2022-12-13 13:27:09 +00:00
|
|
|
'postgres',
|
|
|
|
'mysql'
|
2022-05-06 07:04:51 +00:00
|
|
|
]
|
|
|
|
|
|
|
|
|
2022-12-13 13:27:09 +00:00
|
|
|
def get_config_dir():
|
2022-12-20 11:01:20 +00:00
|
|
|
cwd = Path.cwd().joinpath('config.yaml')
|
|
|
|
plat = system()
|
|
|
|
|
|
|
|
if cwd.exists():
|
|
|
|
return cwd
|
|
|
|
|
|
|
|
elif plat == 'Linux':
|
|
|
|
cfgpath = Path('~/.config/activityrelay/config.yaml').expanduser()
|
2022-11-10 17:38:08 +00:00
|
|
|
|
2022-12-13 13:27:09 +00:00
|
|
|
if cfgpath.exists():
|
|
|
|
return cfgpath
|
2022-05-06 07:04:51 +00:00
|
|
|
|
2022-12-13 13:27:09 +00:00
|
|
|
etcpath = Path('/etc/activityrelay/config.yaml')
|
2022-05-06 07:04:51 +00:00
|
|
|
|
2022-12-20 11:01:20 +00:00
|
|
|
if etcpath.exists() and os.getuid() == etcpath.stat().st_uid:
|
|
|
|
return etcpath
|
|
|
|
|
|
|
|
elif plat == 'Windows':
|
|
|
|
cfgpath = Path('~/AppData/Roaming/activityrelay/config.yaml').expanduer()
|
|
|
|
|
|
|
|
if cfgpath.exists():
|
2022-12-13 13:27:09 +00:00
|
|
|
return cfgpath
|
2022-05-06 07:04:51 +00:00
|
|
|
|
2022-12-20 11:01:20 +00:00
|
|
|
elif plat == 'Darwin':
|
|
|
|
cfgpath = Path('~/Library/Application Support/activityaelay/config.yaml')
|
|
|
|
|
|
|
|
return cwd
|
2022-05-06 07:04:51 +00:00
|
|
|
|
|
|
|
|
2022-12-13 13:27:09 +00:00
|
|
|
class Config(AppBase, dict):
|
|
|
|
def __init__(self, path=None):
|
|
|
|
DotDict.__init__(self, DEFAULTS)
|
2022-05-06 07:04:51 +00:00
|
|
|
|
2022-12-13 13:27:09 +00:00
|
|
|
if self.is_docker:
|
|
|
|
path = Path('/data/config.yaml')
|
2022-05-06 07:04:51 +00:00
|
|
|
|
2022-12-13 13:27:09 +00:00
|
|
|
elif not path:
|
|
|
|
path = get_config_dir()
|
2022-05-06 07:04:51 +00:00
|
|
|
|
2022-12-13 13:27:09 +00:00
|
|
|
else:
|
2022-12-20 11:01:20 +00:00
|
|
|
path = Path(path).expanduser()
|
2022-05-06 07:04:51 +00:00
|
|
|
|
2022-12-13 13:27:09 +00:00
|
|
|
self._path = path
|
|
|
|
self.load()
|
2022-05-06 07:04:51 +00:00
|
|
|
|
|
|
|
|
2022-12-13 13:27:09 +00:00
|
|
|
def __setitem__(self, key, value):
|
2022-12-20 12:59:58 +00:00
|
|
|
if key in {'database', 'hostname', 'port', 'username', 'password'}:
|
|
|
|
key = f'{self.dbtype}_{key}'
|
|
|
|
|
2022-12-13 13:27:09 +00:00
|
|
|
if (self.is_docker and key in {'general_host', 'general_port'}) or value == '__DEFAULT__':
|
|
|
|
value = DEFAULTS[key]
|
2022-05-06 07:04:51 +00:00
|
|
|
|
2022-12-13 13:27:09 +00:00
|
|
|
elif key in {'general_port', 'database_min_connections', 'database_max_connections'}:
|
|
|
|
value = int(value)
|
2022-05-06 07:04:51 +00:00
|
|
|
|
2022-12-20 11:01:20 +00:00
|
|
|
elif key == 'sqlite_database':
|
|
|
|
if not isinstance(value, Path):
|
|
|
|
value = Path(value)
|
|
|
|
|
2022-12-13 13:27:09 +00:00
|
|
|
dict.__setitem__(self, key, value)
|
2022-05-06 07:04:51 +00:00
|
|
|
|
|
|
|
|
|
|
|
@property
|
2022-12-13 13:27:09 +00:00
|
|
|
def dbconfig(self):
|
|
|
|
config = {
|
|
|
|
'type': self['database_type'],
|
|
|
|
'min_conn': self['database_min_connections'],
|
|
|
|
'max_conn': self['database_max_connections']
|
|
|
|
}
|
2022-05-06 07:04:51 +00:00
|
|
|
|
2022-12-13 13:27:09 +00:00
|
|
|
if self.dbtype == 'sqlite':
|
2022-12-20 11:01:20 +00:00
|
|
|
if not self['sqlite_database'].is_absolute():
|
|
|
|
config['database'] = self.path.with_name(str(self['sqlite_database'])).resolve()
|
|
|
|
|
|
|
|
else:
|
|
|
|
config['database'] = self['sqlite_database'].resolve()
|
2022-05-06 07:04:51 +00:00
|
|
|
|
2022-12-13 13:27:09 +00:00
|
|
|
else:
|
|
|
|
for key, value in self.items():
|
|
|
|
cat, name = key.split('_', 1)
|
2022-05-06 07:04:51 +00:00
|
|
|
|
2022-12-13 13:27:09 +00:00
|
|
|
if self.dbtype == cat:
|
|
|
|
config[name] = value
|
2022-05-06 07:04:51 +00:00
|
|
|
|
2022-12-13 13:27:09 +00:00
|
|
|
return config
|
2022-05-06 07:04:51 +00:00
|
|
|
|
|
|
|
|
2022-12-13 13:27:09 +00:00
|
|
|
@cached_property
|
|
|
|
def is_docker(self):
|
|
|
|
return bool(os.getenv('DOCKER_RUNNING'))
|
2022-05-06 07:04:51 +00:00
|
|
|
|
|
|
|
|
2022-12-13 13:27:09 +00:00
|
|
|
@property
|
|
|
|
def path(self):
|
|
|
|
return self._path
|
2022-05-06 07:04:51 +00:00
|
|
|
|
|
|
|
|
2022-12-13 13:27:09 +00:00
|
|
|
## General config
|
|
|
|
@property
|
|
|
|
def host(self):
|
|
|
|
return self['general_host']
|
2022-05-06 07:04:51 +00:00
|
|
|
|
|
|
|
|
2022-12-13 13:27:09 +00:00
|
|
|
@property
|
|
|
|
def listen(self):
|
|
|
|
return self['general_listen']
|
2022-05-06 07:04:51 +00:00
|
|
|
|
|
|
|
|
2022-12-13 13:27:09 +00:00
|
|
|
@property
|
|
|
|
def port(self):
|
|
|
|
return self['general_port']
|
2022-05-06 07:04:51 +00:00
|
|
|
|
|
|
|
|
2022-12-13 13:27:09 +00:00
|
|
|
## Database config
|
|
|
|
@property
|
|
|
|
def dbtype(self):
|
|
|
|
return self['database_type']
|
2022-05-06 07:04:51 +00:00
|
|
|
|
|
|
|
|
2022-12-13 13:27:09 +00:00
|
|
|
## AP URLs
|
|
|
|
@property
|
|
|
|
def actor(self):
|
|
|
|
return f'https://{self.host}/actor'
|
2022-05-06 07:04:51 +00:00
|
|
|
|
|
|
|
|
2022-12-13 13:27:09 +00:00
|
|
|
@property
|
|
|
|
def inbox(self):
|
|
|
|
return f'https://{self.host}/inbox'
|
2022-05-06 07:04:51 +00:00
|
|
|
|
|
|
|
|
2022-12-13 13:27:09 +00:00
|
|
|
@property
|
|
|
|
def keyid(self):
|
|
|
|
return f'{self.actor}#main-key'
|
2022-05-06 07:04:51 +00:00
|
|
|
|
|
|
|
|
2022-12-13 13:27:09 +00:00
|
|
|
def reset(self):
|
|
|
|
self.clear()
|
|
|
|
self.update(DEFAULTS)
|
2022-05-06 07:04:51 +00:00
|
|
|
|
|
|
|
|
|
|
|
def load(self):
|
|
|
|
options = {}
|
|
|
|
|
|
|
|
try:
|
|
|
|
options['Loader'] = yaml.FullLoader
|
|
|
|
|
|
|
|
except AttributeError:
|
|
|
|
pass
|
|
|
|
|
|
|
|
try:
|
|
|
|
with open(self.path) as fd:
|
|
|
|
config = yaml.load(fd, **options)
|
|
|
|
|
|
|
|
except FileNotFoundError:
|
|
|
|
return False
|
|
|
|
|
2022-12-13 13:27:09 +00:00
|
|
|
for key, value in DEFAULTS.items():
|
|
|
|
cat, name = key.split('_', 1)
|
|
|
|
self[key] = config.get(cat, {}).get(name, DEFAULTS[key])
|
2022-05-06 07:04:51 +00:00
|
|
|
|
2022-11-20 11:14:37 +00:00
|
|
|
|
2022-12-13 13:27:09 +00:00
|
|
|
def save(self):
|
|
|
|
config = {key: {} for key in CATEGORY_NAMES}
|
2022-05-06 07:04:51 +00:00
|
|
|
|
2022-12-13 13:27:09 +00:00
|
|
|
for key, value in self.items():
|
|
|
|
cat, name = key.split('_', 1)
|
2022-05-06 07:04:51 +00:00
|
|
|
|
2022-12-13 13:27:09 +00:00
|
|
|
if isinstance(value, Path):
|
|
|
|
value = str(value)
|
2022-05-06 07:04:51 +00:00
|
|
|
|
2022-12-13 13:27:09 +00:00
|
|
|
config[cat][name] = value
|
2022-05-06 07:04:51 +00:00
|
|
|
|
2022-12-13 13:27:09 +00:00
|
|
|
with open(self.path, 'w') as fd:
|
2022-05-06 07:04:51 +00:00
|
|
|
yaml.dump(config, fd, sort_keys=False)
|