finish up webui
This commit is contained in:
parent
c0d766c8b8
commit
ff42651808
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -114,4 +114,5 @@ dmypy.json
|
|||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
/data
|
||||
/data*
|
||||
/config*
|
||||
|
|
80
database.sql
Normal file
80
database.sql
Normal file
|
@ -0,0 +1,80 @@
|
|||
CREATE TABLE IF NOT EXISTS config (
|
||||
id SERIAL PRIMARY KEY,
|
||||
key TEXT NOT NULL,
|
||||
value TEXT
|
||||
);
|
||||
|
||||
|
||||
CREATE TABLE IF NOT EXISTS inboxes (
|
||||
id SERIAL PRIMARY KEY,
|
||||
domain TEXT NOT NULL,
|
||||
inbox TEXT NOT NULL,
|
||||
actor TEXT NOT NULL,
|
||||
timestamp float8 NOT NULL
|
||||
);
|
||||
|
||||
|
||||
CREATE TABLE IF NOT EXISTS retries (
|
||||
id SERIAL PRIMARY KEY,
|
||||
msgid TEXT NOT NULL,
|
||||
inbox TEXT NOT NULL,
|
||||
data TEXT NOT NULL,
|
||||
headers TEXT NOT NULL
|
||||
);
|
||||
|
||||
|
||||
CREATE TABLE IF NOT EXISTS requests (
|
||||
id SERIAL PRIMARY KEY,
|
||||
followid TEXT NOT NULL,
|
||||
domain TEXT NOT NULL,
|
||||
inbox TEXT NOT NULL,
|
||||
actor TEXT NOT NULL
|
||||
);
|
||||
|
||||
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id SERIAL PRIMARY KEY,
|
||||
handle TEXT NOT NULL,
|
||||
username TEXT NOT NULL,
|
||||
password TEXT NOT NULL,
|
||||
timestamp float8 NOT NULL
|
||||
);
|
||||
|
||||
|
||||
CREATE TABLE IF NOT EXISTS tokens (
|
||||
id SERIAL PRIMARY KEY,
|
||||
userid int NOT NULL,
|
||||
token TEXT NOT NULL,
|
||||
timestamp float8 NOT NULL
|
||||
);
|
||||
|
||||
|
||||
CREATE TABLE IF NOT EXISTS whitelist (
|
||||
id SERIAL PRIMARY KEY,
|
||||
domain TEXT NOT NULL,
|
||||
timestamp float8 NOT NULL
|
||||
);
|
||||
|
||||
|
||||
CREATE TABLE IF NOT EXISTS domainbans (
|
||||
id SERIAL PRIMARY KEY,
|
||||
domain TEXT NOT NULL,
|
||||
reason TEXT,
|
||||
timestamp float8 NOT NULL
|
||||
);
|
||||
|
||||
|
||||
CREATE TABLE IF NOT EXISTS userbans (
|
||||
id SERIAL PRIMARY KEY,
|
||||
username TEXT NOT NULL,
|
||||
domain TEXT NOT NULL,
|
||||
reason TEXT,
|
||||
timestamp float8 NOT NULL
|
||||
);
|
||||
|
||||
|
||||
CREATE TABLE IF NOT EXISTS keys (
|
||||
actor TEXT NOT NULL PRIMARY KEY,
|
||||
privkey TEXT NOT NULL,
|
||||
pubkey TEXT NOT NULL
|
||||
);
|
|
@ -42,7 +42,9 @@ for row in table.inbox.all():
|
|||
'domain': row['domain']
|
||||
}
|
||||
|
||||
put.inbox('add', urls)
|
||||
timestamp = row.get('timestamp')
|
||||
|
||||
put.inbox('add', urls, timestamp=timestamp)
|
||||
|
||||
db_actor_key = table.key.get(query.user == 'relay')
|
||||
|
||||
|
@ -103,5 +105,4 @@ if isfile(listfile):
|
|||
put.ban('add', domain)
|
||||
|
||||
for user in config['blocked_users']:
|
||||
username, domain = user.split('@')
|
||||
put.ban('add', username, domain)
|
||||
put.ban('add', user)
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
git+https://git.barkshark.xyz/izaliamae/jhaml.git#egg=jhaml
|
||||
pygresql==5.1
|
||||
dbutils==1.3
|
||||
sanic==19.12.2
|
||||
pycryptodome==3.9.1
|
||||
urllib3==1.25.7
|
||||
watchdog==0.8.3
|
||||
markdown==3.1.1
|
||||
jinja2==2.10.1
|
||||
jinja2-markdown==0.0.3
|
||||
hamlpy3==0.84.0
|
||||
colour==0.1.5
|
||||
argon2-cffi==19.2.0
|
||||
passlib==1.7.2
|
||||
markdown==3.1.1
|
||||
envbash==1.2.0
|
||||
|
|
|
@ -6,7 +6,7 @@ from urllib.parse import urlparse
|
|||
|
||||
from .log import logging
|
||||
from .functions import format_urls
|
||||
from .database import get, put
|
||||
from .database import get, put, bool_check
|
||||
from . import messages
|
||||
|
||||
|
||||
|
@ -64,7 +64,7 @@ def get_userbans():
|
|||
|
||||
for user in get.userban(None, 'all'):
|
||||
users.append({
|
||||
'user': user['user'],
|
||||
'user': user['username'],
|
||||
'domain': user['domain'],
|
||||
'reason': user['reason']
|
||||
})
|
||||
|
@ -72,17 +72,6 @@ def get_userbans():
|
|||
return users
|
||||
|
||||
|
||||
def bool_check(value):
|
||||
if value == True or value.lower() in ['yes', 'true', 'enable']:
|
||||
return True
|
||||
|
||||
elif value in [None, False] or value.lower() in ['no', 'false', 'disable', '']:
|
||||
return False
|
||||
|
||||
else:
|
||||
return value
|
||||
|
||||
|
||||
def sanitize(data, extras=None):
|
||||
if extras == 'spaces':
|
||||
extra = '\s'
|
||||
|
@ -121,6 +110,10 @@ def settings(data):
|
|||
|
||||
new_data = {}
|
||||
|
||||
for setting in ['info', 'rules']:
|
||||
if not data.get(setting):
|
||||
put.config({setting: None})
|
||||
|
||||
for k, v in data.items():
|
||||
if k == 'port':
|
||||
try:
|
||||
|
@ -140,7 +133,7 @@ def ban(data):
|
|||
domain = data['name']
|
||||
reason = data.get('reason')
|
||||
|
||||
if put.ban('add', data, reason=reason):
|
||||
if put.ban('add', domain, reason=reason):
|
||||
logging.info(f'Added {domain} to the banlist')
|
||||
|
||||
else:
|
||||
|
@ -178,14 +171,18 @@ def remove(data):
|
|||
|
||||
def accept(data):
|
||||
row = get.request(data.get('name'))
|
||||
print(data)
|
||||
|
||||
if not row:
|
||||
print('heck1')
|
||||
return
|
||||
|
||||
if not messages.accept(row['followid'], row):
|
||||
print('heck')
|
||||
return
|
||||
|
||||
if put.inboxes('add', row):
|
||||
if put.inbox('add', row):
|
||||
print('heck2')
|
||||
put.request('remove', row)
|
||||
|
||||
return True
|
||||
|
@ -212,6 +209,7 @@ def eject(data):
|
|||
|
||||
|
||||
def run(action, data):
|
||||
print(action, data)
|
||||
if action in ['accept', 'deny']:
|
||||
print(action, data)
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import pg, uuid, sys
|
||||
import pg, uuid, sys, random, string
|
||||
|
||||
from os.path import isfile
|
||||
|
||||
|
@ -46,9 +46,9 @@ db = dbconn(dbconfig['name'])
|
|||
|
||||
|
||||
def newtrans(func):
|
||||
def inner(*arg):
|
||||
def inner(*arg, **kwargs):
|
||||
db.begin()
|
||||
result = func(*arg)
|
||||
result = func(*arg, **kwargs)
|
||||
db.end()
|
||||
return result
|
||||
return inner
|
||||
|
@ -81,14 +81,15 @@ def setup():
|
|||
for k,v in settings.items():
|
||||
db.insert('config', key=k, value=v)
|
||||
|
||||
logging.info('Database setup finished :3')
|
||||
logging.info('Database setup finished :3')
|
||||
|
||||
|
||||
def query(table, data, one=True):
|
||||
def query(table, data, one=True, sort=None):
|
||||
items = data.items()
|
||||
k,v = list(items)[0]
|
||||
SORT = f'ORDER BY {sort} ASC' if sort else ''
|
||||
|
||||
row = db.query(f"SELECT * FROM {table} WHERE {k} = '{v}'")
|
||||
row = db.query(f"SELECT * FROM {table} WHERE {k} = '{v}' {SORT}")
|
||||
|
||||
try:
|
||||
return row.singledict() if one else row.dictresult()
|
||||
|
@ -97,8 +98,9 @@ def query(table, data, one=True):
|
|||
return
|
||||
|
||||
|
||||
def query_all(table):
|
||||
row = db.query(f"SELECT * FROM {table}")
|
||||
def query_all(table, sort=None):
|
||||
SORT = f'ORDER BY {sort} ASC' if sort else ''
|
||||
row = db.query(f"SELECT * FROM {table} {SORT}")
|
||||
|
||||
try:
|
||||
return row.dictresult()
|
||||
|
@ -107,7 +109,7 @@ def query_all(table):
|
|||
return
|
||||
|
||||
|
||||
def query_or(table, value, keys, one=True):
|
||||
def query_or(table, value, keys, one=True, sort=None):
|
||||
if type(keys) != list:
|
||||
return
|
||||
|
||||
|
@ -119,7 +121,8 @@ def query_or(table, value, keys, one=True):
|
|||
if keys[-1] != key:
|
||||
query_str += f' or '
|
||||
|
||||
row = db.query(f'SELECT * FROM {table} WHERE {query_str}')
|
||||
SORT = f'ORDER BY {sort} ASC' if sort else ''
|
||||
row = db.query(f'SELECT * FROM {table} WHERE {query_str} {SORT}')
|
||||
|
||||
try:
|
||||
return row.singledict() if one else row.dictresult()
|
||||
|
@ -128,7 +131,7 @@ def query_or(table, value, keys, one=True):
|
|||
pass
|
||||
|
||||
|
||||
def query_and(table, data, one=True):
|
||||
def query_and(table, data, one=True, sort=None):
|
||||
query_str = ''
|
||||
|
||||
items = data.items()
|
||||
|
@ -139,7 +142,8 @@ def query_and(table, data, one=True):
|
|||
if list(items)[-1][0] != k:
|
||||
query_str += f' and '
|
||||
|
||||
row = db.query(f'SELECT * FROM {table} WHERE {query_str}')
|
||||
SORT = f'ORDER BY {sort} ASC' if sort else ''
|
||||
row = db.query(f'SELECT * FROM {table} WHERE {query_str} {SORT}')
|
||||
|
||||
try:
|
||||
return row.singledict() if one else row.dictresult()
|
||||
|
@ -179,4 +183,18 @@ def randomgen(chars=20):
|
|||
return ''.join(random.choices(string.ascii_letters + string.digits, k=chars))
|
||||
|
||||
|
||||
def bool_check(value):
|
||||
if value in [True, False, None]:
|
||||
return value
|
||||
|
||||
if value.lower() in ['yes', 'true', 'enable']:
|
||||
return True
|
||||
|
||||
elif value.lower() in ['no', 'false', 'disable', '']:
|
||||
return False
|
||||
|
||||
else:
|
||||
return value
|
||||
|
||||
|
||||
__all__ = ['db', 'newtrans', 'HashContext', 'randomgen', 'query', 'query_all', 'query_or', 'query_and']
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
from . import newtrans, setup, get
|
||||
from ..log import logging
|
||||
|
||||
setup()
|
|
@ -3,6 +3,7 @@ import pg
|
|||
from Crypto.PublicKey import RSA
|
||||
|
||||
from . import *
|
||||
from . import bool_check as bcheck
|
||||
from ..log import logging
|
||||
|
||||
|
||||
|
@ -44,15 +45,15 @@ def config(data, *args):
|
|||
rows = query_all('config')
|
||||
|
||||
for row in rows:
|
||||
settings.update({row['key']: row['value']})
|
||||
settings.update({row['key']: bcheck(row['value'])})
|
||||
|
||||
else:
|
||||
for k,v in data.items():
|
||||
query_data = {'key': k, 'value': v}
|
||||
query_data = {'key': k, 'value': bcheck(v)}
|
||||
row = True if data == 'all' else query_and('config', query_data)
|
||||
|
||||
if row:
|
||||
settings.update({key, row['value']})
|
||||
settings.update({key, bcheck(row['value'])})
|
||||
|
||||
else:
|
||||
settings.update({key: None})
|
||||
|
@ -60,33 +61,33 @@ def config(data, *args):
|
|||
return settings
|
||||
|
||||
elif type(data) == str:
|
||||
row = query('config', {'key': data})
|
||||
row = query('config', {'key': bcheck(data)})
|
||||
|
||||
if row:
|
||||
return row['value']
|
||||
return bcheck(row['value'])
|
||||
|
||||
elif args:
|
||||
return args[0]
|
||||
|
||||
|
||||
def inbox(url):
|
||||
return query_all('inboxes') if url == 'all' else query_or('inboxes', url, ['inbox', 'domain', 'actor'])
|
||||
return query_all('inboxes', sort='domain') if url == 'all' else query_or('inboxes', url, ['inbox', 'domain', 'actor'], sort='domain')
|
||||
|
||||
|
||||
def domainban(domain):
|
||||
return query_all('domainbans') if domain == 'all' else query('domainbans', {'domain': domain})
|
||||
return query_all('domainbans', sort='domain') if domain == 'all' else query('domainbans', {'domain': domain}, sort='domain')
|
||||
|
||||
|
||||
def userban(user, domain):
|
||||
return query_all('userbans') if domain == 'all' else query_and('userbans', {'username': user, 'domain': domain})
|
||||
return query_all('userbans', sort='username') if domain == 'all' else query_and('userbans', {'username': user, 'domain': domain}, sort='username')
|
||||
|
||||
|
||||
def whitelist(domain):
|
||||
return query_all('whitelist') if domain == 'all' else query_and('whitelist', {'domain': domain})
|
||||
return query_all('whitelist', sort='domain') if domain == 'all' else query_and('whitelist', {'domain': domain}, sort='domain')
|
||||
|
||||
|
||||
def request(domain):
|
||||
return query_all('inboxes') if domain == 'all' else query_or('requests', domain, ['inbox', 'domain', 'actor'])
|
||||
return query_all('requests', sort='domain') if domain == 'all' else query_or('requests', domain, ['inbox', 'domain', 'actor'], sort='domain')
|
||||
|
||||
|
||||
def retries(data):
|
||||
|
@ -113,24 +114,39 @@ def retries(data):
|
|||
|
||||
|
||||
def user(data):
|
||||
if not data:
|
||||
return
|
||||
|
||||
if data == 'all':
|
||||
return query_all('users')
|
||||
|
||||
username = data.get('username')
|
||||
userid = data.get('userid')
|
||||
if type(data) == int:
|
||||
return query('users', {'id': data})
|
||||
|
||||
querydata = {'id': userid} if userid else {'username': username}
|
||||
|
||||
return query('users', querydata)
|
||||
else:
|
||||
return query_or('users', data, ['username', 'handle'])
|
||||
|
||||
|
||||
def token(token):
|
||||
return query('tokens', {'token': token})
|
||||
|
||||
|
||||
def verify_pass(username, password):
|
||||
user_data = user({'username': username})
|
||||
def verify_password(username, password):
|
||||
user_data = user(username)
|
||||
|
||||
return Hash.verify(password, user_data['password'])
|
||||
|
||||
|
||||
# generate an auth code if there are no admin users
|
||||
if len(user('all')) < 1:
|
||||
auth_code = randomgen()
|
||||
host = config('host')
|
||||
|
||||
if config('setup'):
|
||||
logging.warn(f'There are no admin users in the database. Please register an account at https://{host}/register?code={auth_code}')
|
||||
|
||||
else:
|
||||
logging.warn(f'The relay is not configured. Please set it up at https://{host}/setup?code={auth_code}')
|
||||
|
||||
else:
|
||||
auth_code = None
|
||||
|
|
|
@ -17,7 +17,7 @@ def config(data):
|
|||
configs = get.config('all')
|
||||
|
||||
for k,v in data.items():
|
||||
if configs and configs[k] == v:
|
||||
if configs and configs.get(k) == v:
|
||||
return
|
||||
|
||||
if not configs or k not in configs:
|
||||
|
@ -29,7 +29,12 @@ def config(data):
|
|||
db.insert('config', data)
|
||||
|
||||
else:
|
||||
db.update('keys', {'value': v}, key=k)
|
||||
row = query('config', {'key': k})
|
||||
|
||||
if not row:
|
||||
return
|
||||
|
||||
db.update('config', {'value': v}, id=row['id'])
|
||||
|
||||
|
||||
@newtrans
|
||||
|
@ -37,14 +42,14 @@ def rsa_key(name, keys):
|
|||
actor_key = get.rsa_key('default')
|
||||
|
||||
if not actor_key:
|
||||
pgdb.update('keys', {
|
||||
db.update('keys', {
|
||||
'pubkey': keys['pubkey'],
|
||||
'privkey': keys['privkey']
|
||||
}, actor='default')
|
||||
|
||||
|
||||
@newtrans
|
||||
def inbox(action, urls):
|
||||
def inbox(action, urls, timestamp=None):
|
||||
actor, inbox, domain = format_urls(urls)
|
||||
row = get.inbox(actor)
|
||||
|
||||
|
@ -56,13 +61,13 @@ def inbox(action, urls):
|
|||
'domain': domain,
|
||||
'inbox': inbox,
|
||||
'actor': actor,
|
||||
'timestamp': datetime.now().timestamp()
|
||||
'timestamp': datetime.now().timestamp() if not timestamp else timestamp
|
||||
}
|
||||
|
||||
db.insert('inboxes', data)
|
||||
|
||||
elif action == 'remove':
|
||||
db.remove('inboxes', id=row['id'])
|
||||
db.delete('inboxes', id=row['id'])
|
||||
|
||||
else:
|
||||
return
|
||||
|
@ -86,12 +91,12 @@ def request(action, urls, followid=None):
|
|||
'followid': followid,
|
||||
'timestamp': datetime.now().timestamp()
|
||||
}
|
||||
db.insert('request', data)
|
||||
db.insert('requests', data)
|
||||
|
||||
return True
|
||||
|
||||
if action == 'remove':
|
||||
db.remove('requests', id=row['id'])
|
||||
db.delete('requests', id=row['id'])
|
||||
|
||||
return True
|
||||
|
||||
|
@ -121,14 +126,14 @@ def del_retries(data):
|
|||
rows = get.retries(data)
|
||||
|
||||
for row in rows:
|
||||
db.remove('requests', id=row['id'])
|
||||
db.delete('requests', id=row['id'])
|
||||
|
||||
|
||||
@newtrans
|
||||
def ban(action, data, reason=None):
|
||||
if '@' in data:
|
||||
if data.startswith('@'):
|
||||
data.replace('@', '', 1)
|
||||
data = data.replace('@', '', 1)
|
||||
|
||||
username, domain = data.split('@')
|
||||
|
||||
|
@ -153,19 +158,44 @@ def ban(action, data, reason=None):
|
|||
data.update({'username': username})
|
||||
|
||||
if row:
|
||||
db.update(bantype, data, id=row['id'])
|
||||
return True if db.update(bantype, data, id=row['id']) else False
|
||||
|
||||
else:
|
||||
db.insert(bantype, data)
|
||||
return True if db.insert(bantype, data) else False
|
||||
|
||||
if action == 'remove':
|
||||
db.remove(bantype, id=row['id'])
|
||||
return True if db.delete(bantype, id=row['id']) else False
|
||||
|
||||
|
||||
@newtrans
|
||||
def whitelist(action, data, reason=None):
|
||||
domain = urlparse(data).netloc if data.startswith('https://') else data
|
||||
|
||||
row = get.whitelist(domain)
|
||||
|
||||
if action == 'remove' and not row:
|
||||
return True
|
||||
|
||||
if action == 'add':
|
||||
data = {
|
||||
'domain': domain,
|
||||
'timestamp': datetime.now().timestamp()
|
||||
}
|
||||
|
||||
if row:
|
||||
db.update('whitelist', data, id=row['id'])
|
||||
|
||||
else:
|
||||
db.insert('whitelist', data)
|
||||
|
||||
if action == 'remove':
|
||||
db.delete('whitelist', id=row['id'])
|
||||
|
||||
|
||||
@newtrans
|
||||
def user(handle, password):
|
||||
username = handle.lower()
|
||||
timestamp = timedate.now().timestamp()
|
||||
timestamp = datetime.now().timestamp()
|
||||
|
||||
if query('users', {'username': username}):
|
||||
return
|
||||
|
@ -177,22 +207,20 @@ def user(handle, password):
|
|||
'timestamp': timestamp
|
||||
}
|
||||
|
||||
db.insert('users', data)
|
||||
return db.insert('users', data)
|
||||
|
||||
|
||||
@newtrans
|
||||
def token(username):
|
||||
userdata = user(username)
|
||||
userdata = get.user(username)
|
||||
|
||||
if not userdata:
|
||||
return
|
||||
|
||||
tokendata = {
|
||||
'userid': userdata['id'],
|
||||
'token': randomgen(chars=40),
|
||||
'timestamp': datetime.now()
|
||||
}
|
||||
tokendata = {
|
||||
'userid': userdata['id'],
|
||||
'token': randomgen(chars=40),
|
||||
'timestamp': datetime.now().timestamp()
|
||||
}
|
||||
|
||||
db.insert('tokens', tokendata)
|
||||
|
||||
return tokendata
|
||||
return db.insert('tokens', tokendata)
|
||||
|
|
489
uncia/db.py
489
uncia/db.py
|
@ -1,489 +0,0 @@
|
|||
import sys, random, string, json
|
||||
|
||||
from json.decoder import JSONDecodeError
|
||||
from urllib.parse import urlparse
|
||||
from datetime import datetime
|
||||
from os.path import isfile
|
||||
|
||||
from tinydb import TinyDB, Query
|
||||
from tinydb_smartcache import SmartCacheTable
|
||||
from tinyrecord import transaction as trans
|
||||
from Crypto.PublicKey import RSA
|
||||
from passlib.context import CryptContext
|
||||
|
||||
from .config import stor_path
|
||||
from .log import logging
|
||||
|
||||
|
||||
try:
|
||||
db = TinyDB(f'{stor_path}/db.json', ensure_ascii=False, escape_forward_slashes=False, indent=4)
|
||||
|
||||
except JSONDecodeError as e:
|
||||
logging.critical(f'Failed to load DB: {e}')
|
||||
logging.info(f'Exiting...')
|
||||
sys.exit()
|
||||
|
||||
query = Query()
|
||||
|
||||
class table:
|
||||
config = db.table('config', cache_size=100)
|
||||
inbox = db.table('inbox', cache_size=100)
|
||||
retries = db.table('retries', cache_size=100)
|
||||
requests = db.table('requests', cache_size=100)
|
||||
users = db.table('users', cache_size=100)
|
||||
tokens = db.table('tokens', cache_size=100)
|
||||
whitelist = db.table('whitelist', cache_size=100)
|
||||
domainbans = db.table('domainbans', cache_size=100)
|
||||
userbans = db.table('userbans', cache_size=100)
|
||||
key = db.table('key', cache_size=100)
|
||||
|
||||
|
||||
def setup_db():
|
||||
rsa_key('default')
|
||||
defconfig = {
|
||||
'host': '127.0.0.1',
|
||||
'address': '0.0.0.0',
|
||||
'port': 3621,
|
||||
'name': 'Uncia Relay',
|
||||
'email': None,
|
||||
'admin': None,
|
||||
'show_domainbans': False,
|
||||
'show_userbans': False,
|
||||
'whitelist': False,
|
||||
'block_relays': True,
|
||||
'require_approval': True,
|
||||
'notifications': False,
|
||||
'log_level': 'INFO',
|
||||
'development': False,
|
||||
'setup': False
|
||||
}
|
||||
|
||||
set_config(defconfig)
|
||||
|
||||
|
||||
def rsa_key(actor):
|
||||
actor_key = table.key.get(query.actor == actor)
|
||||
|
||||
if not actor_key:
|
||||
logging.info('No RSA key. Generating one...')
|
||||
PRIV = RSA.generate(4096)
|
||||
PUB = PRIV.publickey()
|
||||
|
||||
keydata = {
|
||||
'actor': actor,
|
||||
'pubkey': PUB.exportKey('PEM').decode('utf-8'),
|
||||
'privkey': PRIV.exportKey('PEM').decode('utf-8')
|
||||
}
|
||||
|
||||
with trans(table.key) as tr:
|
||||
tr.insert(keydata)
|
||||
|
||||
return keydata
|
||||
|
||||
return actor_key
|
||||
|
||||
|
||||
def inboxes(action, urls):
|
||||
from .functions import format_urls
|
||||
|
||||
actor_url, inbox, domain = format_urls(urls)
|
||||
row = table.inbox.get(query.actor == actor_url)
|
||||
|
||||
if (row and action == 'add') or (not row and action == 'remove'):
|
||||
return True
|
||||
|
||||
with trans(table.inbox) as tr:
|
||||
if action == 'add':
|
||||
data = {
|
||||
'domain': domain,
|
||||
'inbox': inbox,
|
||||
'actor': actor_url,
|
||||
'timestamp': datetime.now()
|
||||
}
|
||||
|
||||
tr.insert(data)
|
||||
|
||||
elif action == 'remove':
|
||||
tr.remove(doc_ids=[row.doc_id])
|
||||
|
||||
else:
|
||||
return
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def get_inboxes():
|
||||
return [instance['inbox'] for instance in table.inbox.all()]
|
||||
|
||||
|
||||
def banlist(domain, action, reason=None):
|
||||
domain = domain.lower()
|
||||
data = domain.replace('@', '', 1) if domain.startswith('@') else domain
|
||||
check = ban_check(data)
|
||||
|
||||
if (action == 'add' and check) or (action == 'remove' and not check):
|
||||
return
|
||||
|
||||
if '@' in data:
|
||||
user, domain = data.split('@')
|
||||
|
||||
with trans(table.userbans) as tr:
|
||||
if action == 'add':
|
||||
data = {
|
||||
'user': user,
|
||||
'domain': domain,
|
||||
'reason': reason,
|
||||
'timestamp': datetime.now()
|
||||
}
|
||||
|
||||
tr.insert(data)
|
||||
|
||||
elif action == 'remove':
|
||||
tr.remove(doc_ids=[check.doc_id])
|
||||
|
||||
else:
|
||||
if data.startswith('https'):
|
||||
domain = urlparse(data).netloc
|
||||
|
||||
with trans(table.domainbans) as tr:
|
||||
if action == 'add':
|
||||
data = {
|
||||
'domain': domain,
|
||||
'reason': reason,
|
||||
'timestamp': datetime.now()
|
||||
}
|
||||
|
||||
tr.insert(data)
|
||||
|
||||
elif action == 'remove':
|
||||
tr.remove(doc_ids=[check.doc_id])
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def get_bans(bantype='domain'):
|
||||
types = {'domain': table.domainbans, 'user': table.userbans}
|
||||
|
||||
return types[bantype].all()
|
||||
|
||||
|
||||
def ban_check(name):
|
||||
if name.startswith('https'):
|
||||
name = urlparse(name).netloc
|
||||
|
||||
if '@' in name:
|
||||
if name.startswith('@'):
|
||||
name = name.replace('@', '', 1)
|
||||
|
||||
user, domain = name.split('@')
|
||||
userban = table.userbans.search(query.user == user)
|
||||
|
||||
if not userban:
|
||||
return False
|
||||
|
||||
for line in userban:
|
||||
if line['domain'] in [domain, 'any']:
|
||||
return line
|
||||
|
||||
return False
|
||||
|
||||
else:
|
||||
return table.domainbans.get(query.domain == name)
|
||||
|
||||
|
||||
def get_config(setting):
|
||||
if setting == 'all':
|
||||
return {row['key']: row['value'] for row in table.config.all()}
|
||||
|
||||
elif type(setting) in [list, tuple]:
|
||||
return_dict = {}
|
||||
for k, v in setting:
|
||||
setdata = table.config.search(query.key == k)
|
||||
|
||||
if setdata:
|
||||
return_dict.update({setdata['key']: setdata['value']})
|
||||
|
||||
return setdata
|
||||
|
||||
else:
|
||||
row = table.config.get(query.key == setting)
|
||||
return row['value'] if row else None
|
||||
|
||||
|
||||
def set_config(settings):
|
||||
if type(settings) != dict:
|
||||
logging.error('Unwilling to set config options. Data is not a dict')
|
||||
return
|
||||
|
||||
with trans(table.config) as tr:
|
||||
for k, v in settings.items():
|
||||
data = {'key': k, 'value': v}
|
||||
row = table.config.get(query.key == k)
|
||||
|
||||
if row:
|
||||
tr.update(data, doc_ids=[row.doc_id])
|
||||
|
||||
else:
|
||||
tr.insert(data)
|
||||
|
||||
config = get_config('all')
|
||||
|
||||
|
||||
def whitelist(domain, action):
|
||||
if domain.startswith('https'):
|
||||
domain = urlparse(domain).netloc
|
||||
|
||||
row = table.whitelist.get(query.domain == domain)
|
||||
|
||||
if (action == 'add' and row) or (action == 'remove' and not row):
|
||||
return
|
||||
|
||||
with trans(table.whitelist) as tr:
|
||||
if action == 'add':
|
||||
data = {
|
||||
'domain': domain,
|
||||
'timestamp': datetime.now()
|
||||
}
|
||||
|
||||
tr.insert(data)
|
||||
|
||||
elif action == 'remove':
|
||||
tr.remove(doc_ids=[row.doc_id])
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def retries(action, msgid, inbox, data=None, headers=None):
|
||||
from .functions import fetch
|
||||
row = table.retries.get(query.msgid == msgid and query.inbox == inbox)
|
||||
|
||||
if not fetch(data['actor'], cached=False):
|
||||
logging.info(f'Failed to fetch actor before retrying: {data["actor"]}')
|
||||
return
|
||||
|
||||
if action == 'add' and None in [data, headers]:
|
||||
logging.warning(f'Missing data or headers for {msgid}')
|
||||
return
|
||||
|
||||
with trans(table.retries) as tr:
|
||||
if action == 'add':
|
||||
if not row:
|
||||
tr.insert({
|
||||
'msgid': msgid,
|
||||
'inbox': inbox,
|
||||
'data': data,
|
||||
'headers': headers
|
||||
})
|
||||
|
||||
return True
|
||||
|
||||
if action == 'remove':
|
||||
if row:
|
||||
tr.remove(doc_ids=[row.doc_id])
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def request(action, urls, followid=None):
|
||||
from .functions import format_urls
|
||||
|
||||
actor, inbox, domain = format_urls(urls)
|
||||
row = table.requests.get(query.domain == domain)
|
||||
|
||||
if (row and action == 'add') or (not row and action == 'remove'):
|
||||
return True
|
||||
|
||||
if action == 'add' and followid:
|
||||
with trans(table.requests) as tr:
|
||||
tr.insert({
|
||||
'domain': domain,
|
||||
'inbox': inbox,
|
||||
'actor': actor,
|
||||
'followid': followid,
|
||||
'timestamp': datetime.now()
|
||||
})
|
||||
|
||||
return True
|
||||
|
||||
if action == 'remove':
|
||||
with trans(table.requests) as tr:
|
||||
tr.remove(doc_ids=[row.doc_id])
|
||||
|
||||
return True
|
||||
|
||||
def admin_user(action, data):
|
||||
username = data.get('user')
|
||||
password = data.get('pass')
|
||||
|
||||
if not username or (action == 'add' and not password):
|
||||
return
|
||||
|
||||
displayname = username
|
||||
username = username.lower()
|
||||
user = get_user(username)
|
||||
|
||||
if (action == 'add' and user) or (action in ['remove', 'update'] and not user):
|
||||
return
|
||||
|
||||
with trans(table.users) as tr:
|
||||
if action == 'add':
|
||||
data = {
|
||||
'displayname': displayname,
|
||||
'username': username,
|
||||
'password': hashes.encrypt(password),
|
||||
'timestamp': datetime.now()
|
||||
}
|
||||
|
||||
tr.insert(data)
|
||||
|
||||
if action == 'remove':
|
||||
tr.remove(doc_ids=[user.doc_id])
|
||||
|
||||
with trans(table.tokens) as tr:
|
||||
if action == 'add':
|
||||
return create_token(username)
|
||||
|
||||
if action == 'remove':
|
||||
tr.remove(doc_ids=[row.doc_id for row in table.tokens.search(query.username)])
|
||||
|
||||
|
||||
def get_user(username):
|
||||
if not username:
|
||||
return
|
||||
|
||||
username = username.lower()
|
||||
userdata = table.users.get(query.username == username)
|
||||
|
||||
return userdata if userdata else None
|
||||
|
||||
|
||||
def create_token(username):
|
||||
if not username:
|
||||
return
|
||||
|
||||
userdata = get_user(username)
|
||||
|
||||
if not userdata:
|
||||
return
|
||||
|
||||
with trans(table.tokens) as tr:
|
||||
tokendata = {
|
||||
'username': username,
|
||||
'token': randomgen(chars=40),
|
||||
'timestamp': datetime.now()
|
||||
}
|
||||
|
||||
tr.insert(tokendata)
|
||||
|
||||
return tokendata
|
||||
|
||||
|
||||
def del_token(token):
|
||||
if not token:
|
||||
return
|
||||
|
||||
tokendata = table.tokens.get(query.token == token)
|
||||
|
||||
if tokendata:
|
||||
with trans(table.tokens) as tr:
|
||||
tr.remove(doc_ids=[tokendata.doc_id])
|
||||
|
||||
|
||||
def get_token(token):
|
||||
if not token:
|
||||
return
|
||||
|
||||
tokendata = table.tokens.get(query.token == token)
|
||||
|
||||
if not tokendata:
|
||||
return
|
||||
|
||||
one_day = 60*60*24
|
||||
two_weeks = one_day*14
|
||||
token_time = tokendata['timestamp']
|
||||
current_time = datetime.now().timestamp()
|
||||
|
||||
if current_time - two_weeks > token_time:
|
||||
with trans(table.tokens) as tr:
|
||||
tr.remove(doc_ids=[tokendata.doc_id])
|
||||
|
||||
return
|
||||
|
||||
if current_time - one_day > token_time:
|
||||
with trans(table.tokens) as tr:
|
||||
tr.update({'timestamp': datetime.now()}, doc_ids=[tokendata.doc_id])
|
||||
|
||||
tokendata['timestamp'] = current_time
|
||||
|
||||
return tokendata
|
||||
|
||||
|
||||
def check_password(username, password):
|
||||
user = get_user(username)
|
||||
|
||||
if not user:
|
||||
return
|
||||
|
||||
if not hashes.verify(password, user['password']):
|
||||
return
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def randomgen(chars=20):
|
||||
if type(chars) != int:
|
||||
logging.warning(f'Invalid character length. Must be an int: {chars}')
|
||||
chars = 20
|
||||
|
||||
return ''.join(random.choices(string.ascii_letters + string.digits, k=chars))
|
||||
|
||||
|
||||
class HashContext:
|
||||
def __init__(self, schemes=['argon2'], default='argon2', rounds=25):
|
||||
self.saltfile = f'{stor_path}/salt.txt'
|
||||
self.salt = None
|
||||
self.hasher = CryptContext(schemes=schemes, default=default, argon2__default_rounds=rounds)
|
||||
|
||||
def encrypt(self, string):
|
||||
return self.hasher.encrypt(string+self.salt)
|
||||
|
||||
def verify(self, string, hashed):
|
||||
return self.hasher.verify(string+self.salt, hashed)
|
||||
|
||||
def set_salt(self):
|
||||
if not isfile(self.saltfile):
|
||||
self.salt = randomgen()
|
||||
|
||||
with open(self.saltfile, 'w') as newfile:
|
||||
newfile.write(self.salt)
|
||||
|
||||
else:
|
||||
self.salt = open(self.saltfile).read()
|
||||
|
||||
|
||||
# initialize password hasher
|
||||
hashes = HashContext()
|
||||
hashes.set_salt()
|
||||
|
||||
|
||||
# initialize the db
|
||||
if get_config('setup') == None:
|
||||
setup_db()
|
||||
|
||||
|
||||
# generate an auth code if there are no admin users
|
||||
if len(table.users.all()) < 1:
|
||||
auth_code = randomgen()
|
||||
host = get_config('host')
|
||||
|
||||
if get_config('setup'):
|
||||
logging.warn(f'There are no admin users in the database. Please register an account at https://{host}/register?code={auth_code}')
|
||||
|
||||
else:
|
||||
logging.warn(f'The relay is not configured. Please set it up at https://{host}/setup?code={auth_code}')
|
||||
|
||||
else:
|
||||
auth_code = None
|
||||
|
||||
|
||||
config = get_config('all')
|
|
@ -239,6 +239,10 @@ tr:last-child .col2 {
|
|||
height: 16em;
|
||||
}
|
||||
|
||||
#network input {
|
||||
width: calc(100% - 20px);
|
||||
}
|
||||
|
||||
#submit {
|
||||
text-align: center;
|
||||
}
|
||||
|
@ -248,7 +252,6 @@ tr:last-child .col2 {
|
|||
}
|
||||
|
||||
.settings label {
|
||||
height: 26px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-left: 15px;
|
||||
|
|
|
@ -184,23 +184,25 @@
|
|||
|
||||
%label General Info
|
||||
%textarea{'name': 'info', 'placeholder': 'Relay Info'}<
|
||||
{{config.info}}
|
||||
-if config.info
|
||||
{{config.info}}
|
||||
|
||||
%label Relay Rules
|
||||
%textarea{'name': 'rules', 'placeholder': 'Relay Rules'}<
|
||||
{{config.rules}}
|
||||
-if config.rules
|
||||
{{config.rules}}
|
||||
|
||||
%div{'class': 'section settings', 'id': 'settings'}
|
||||
%p{'class': 'sec-header'} Relay Settings
|
||||
%div{'class': 'grid-container'}
|
||||
- if config.admin
|
||||
- set admin = config.admin
|
||||
- else
|
||||
- set admin = 'None'
|
||||
-if config.admin
|
||||
-set admin = config.admin
|
||||
-else
|
||||
-set admin = 'None'
|
||||
|
||||
- if config.email
|
||||
- set email = config.email
|
||||
- else
|
||||
-if config.email
|
||||
-set email = config.email
|
||||
-else
|
||||
-set email = 'None'
|
||||
|
||||
%div{'class': 'grid-item col1'}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
- set default_open = 'open'
|
||||
- set config = get.config('all')
|
||||
- set cookie = get.token(request.cookies.token)
|
||||
- set user = get.user(cookie.username)
|
||||
- set user = get.user(cookie.userid)
|
||||
!!!
|
||||
%html
|
||||
%head
|
||||
|
@ -61,11 +60,15 @@
|
|||
@{{config.admin}}
|
||||
|
||||
%div{'class': 'grid-item acct', 'style': 'display: inline'}
|
||||
- if user != None
|
||||
{{user.displayname}} [<a href='/logout'>logout</a>]
|
||||
- if config.setup
|
||||
- if user != None
|
||||
{{user.handle}} [<a href='/logout'>logout</a>]
|
||||
|
||||
- else
|
||||
Guest [<a href='/login'>login</a>]
|
||||
- else
|
||||
Guest [<a href='/login'>login</a>]
|
||||
|
||||
- else
|
||||
%p UvU
|
||||
|
||||
%div{'class': 'grid-item col2'}
|
||||
%p
|
||||
|
|
|
@ -38,8 +38,7 @@
|
|||
- if config.require_approval
|
||||
Note: This relay requires approval from the admin, so it will show as "Waiting for relay's approvel" in Mastodon until accepted.
|
||||
|
||||
- if rules != None
|
||||
- if config.rules
|
||||
%div{'class': 'section'}
|
||||
%h2{'class': 'title'} What are the rules?
|
||||
{{markdown(config.rules)}}
|
||||
|
||||
|
|
|
@ -5,7 +5,11 @@
|
|||
- block content
|
||||
%div{'class': 'section'}
|
||||
%h2{'class': 'title'} Info
|
||||
{{markdown(config.info)}}
|
||||
- if config.info
|
||||
{{markdown(config.info)}}
|
||||
|
||||
- else
|
||||
empty :/
|
||||
|
||||
%div{'class': 'section', 'id': 'home_instances'}
|
||||
%h2{'class': 'title'} Registered Instances
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
- set page = 'Setup'
|
||||
|
||||
- block content
|
||||
- if get.config('setup') == 'False'
|
||||
- if config.setup
|
||||
%div{'class': 'section setup'}
|
||||
%p{'class': 'sec-header'} Setup
|
||||
%p<
|
||||
|
@ -78,19 +78,17 @@
|
|||
%option{'value': '{{level}}'}
|
||||
{{level}}
|
||||
|
||||
%div{'class': 'section settings', 'id': 'network'}
|
||||
%div{'class': 'section settings network', 'id': 'network'}
|
||||
%p{'class': 'sec-header'} Network
|
||||
%div{'class': 'grid-container'}
|
||||
%div{'class': 'grid-item col1'}
|
||||
|
||||
%label Listen Address (IP address the relay will listen on)
|
||||
%input{'type': 'text', 'name': 'address', 'placeholder': 'default: 127.0.0.1'}
|
||||
%label Listen Address (IP address the relay will listen on)
|
||||
%input{'type': 'text', 'name': 'address', 'placeholder': '127.0.0.1'}
|
||||
|
||||
%label Listen Port (Port the relay will listen on)
|
||||
%input{'type': 'numeric', 'name': 'port', 'placeholder': 'default: 3621'}
|
||||
%label Listen Port (Port the relay will listen on)
|
||||
%input{'type': 'numeric', 'name': 'port', 'placeholder': '3621'}
|
||||
|
||||
%label Hostname (Domain the relay is served from)
|
||||
%input{'type': 'text', 'name': 'host', 'placeholder': 'relay.example.com'}
|
||||
%label Hostname (Domain the relay is served from)
|
||||
%input{'type': 'text', 'name': 'host', 'placeholder': 'relay.example.com'}
|
||||
|
||||
%div{'class': 'section setup', 'id': 'submit'}
|
||||
- if code
|
||||
|
|
|
@ -11,12 +11,12 @@ import urllib3
|
|||
from sanic import response
|
||||
from colour import Color
|
||||
from Crypto.PublicKey import RSA
|
||||
from jhaml.jhaml import convert
|
||||
|
||||
import urllib3
|
||||
|
||||
from .config import script_path, stor_path, version, pyv
|
||||
|
||||
|
||||
httpclient = urllib3.PoolManager(num_pools=100, timeout=urllib3.Timeout(connect=5, read=5))
|
||||
|
||||
|
||||
|
@ -66,7 +66,7 @@ def get_id(data):
|
|||
return object_id
|
||||
|
||||
|
||||
def get_user(data):
|
||||
def get_post_user(data):
|
||||
from .messages import fetch
|
||||
if data['type'] in ['Follow', 'Undo']:
|
||||
return
|
||||
|
@ -100,48 +100,6 @@ def get_user(data):
|
|||
return (user, domain)
|
||||
|
||||
|
||||
def build_templates():
|
||||
timefile = f'{stor_path}/build/times.json'
|
||||
updated = False
|
||||
|
||||
if not isdir(f'{stor_path}/build'):
|
||||
os.makedirs(f'{stor_path}/build')
|
||||
|
||||
if isfile(timefile):
|
||||
try:
|
||||
times = json.load(open(timefile))
|
||||
|
||||
except:
|
||||
times = {}
|
||||
|
||||
else:
|
||||
times = {}
|
||||
|
||||
for filename in os.listdir(f'{script_path}/frontend/templates'):
|
||||
modtime = getmtime(f'{script_path}/frontend/templates/{filename}')
|
||||
base, ext = filename.split('.')
|
||||
|
||||
if ext != 'haml':
|
||||
pass
|
||||
|
||||
elif base not in times or times.get(base) != modtime:
|
||||
updated = True
|
||||
logging.info(f"Template '{filename}' was changed. Building...")
|
||||
|
||||
try:
|
||||
convert(f'{script_path}/frontend/templates/{filename}', destination=f'{stor_path}/build/{base}.html')
|
||||
logging.info(f"Template '{filename}' has been built")
|
||||
|
||||
except:
|
||||
traceback.print_exc()
|
||||
logging.error(f'Failed to build {filename}')
|
||||
|
||||
times[base] = modtime
|
||||
|
||||
if updated:
|
||||
with open(timefile, 'w') as filename:
|
||||
filename.write(json.dumps(times))
|
||||
|
||||
|
||||
def format_date(timestamp):
|
||||
if timestamp:
|
||||
|
|
|
@ -7,7 +7,6 @@ from .templates import error
|
|||
from .signatures import validate
|
||||
from .views import Login
|
||||
from .database import get, put
|
||||
from .admin import bool_check
|
||||
|
||||
|
||||
async def access_log(request, response):
|
||||
|
@ -32,9 +31,6 @@ async def authentication(request):
|
|||
if not get.config('setup') and not request.path.startswith(('/setup', '/style')):
|
||||
return response.redirect('/setup') if not accept else response.json({'error': 'relay not setup yet'}, status=401)
|
||||
|
||||
if not bool_check(get.config('setup')) and not request.path.startswith('/setup'):
|
||||
return response.redirect('/setup')
|
||||
|
||||
apitoken = request.headers.get('token')
|
||||
token = request.cookies.get('token')
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ from datetime import datetime
|
|||
from .log import logging
|
||||
from .messages import fetch, accept, announce, forward, notification
|
||||
from .database import get, put
|
||||
from .functions import get_inbox, cache, get_id, get_user, format_urls
|
||||
from .functions import get_inbox, cache, get_id, get_post_user, format_urls
|
||||
|
||||
|
||||
def relay_announce(data, actor, urls):
|
||||
|
@ -21,7 +21,7 @@ def relay_announce(data, actor, urls):
|
|||
logging.debug(f'Already relayed {object_id}')
|
||||
return
|
||||
|
||||
username = get_user(data)
|
||||
username = get_post_user(data)
|
||||
|
||||
if username:
|
||||
user, domain = username
|
||||
|
@ -69,7 +69,7 @@ def relay_undo(data, actor, urls):
|
|||
if action in ['announce', 'create']:
|
||||
relay_forward(data, actor, urls)
|
||||
|
||||
if action == 'follow':
|
||||
elif action == 'follow':
|
||||
put.inbox('remove', urls)
|
||||
|
||||
else:
|
||||
|
|
|
@ -9,10 +9,10 @@ from watchdog.events import FileSystemEventHandler
|
|||
|
||||
from .log import logging, LOG
|
||||
from .config import script_path, fwsecret
|
||||
from .database import get, setup
|
||||
from .functions import build_templates
|
||||
from .database import get, setup, randomgen
|
||||
from .messages import run_retries
|
||||
from .admin import bool_check
|
||||
from .templates import build_templates
|
||||
from . import errors, views, middleware as mw
|
||||
|
||||
|
||||
|
@ -53,7 +53,7 @@ app.add_route(views.Style.as_view(), '/style-<timestamp>')
|
|||
app.static('/favicon.ico', f'{script_path}/frontend/favicon.png')
|
||||
|
||||
|
||||
# Return setup page if this is the first run
|
||||
# Enable setup page if this is the first run
|
||||
if not bool_check(get.config('setup')):
|
||||
app.add_route(views.Setup.as_view(), '/setup')
|
||||
|
||||
|
|
|
@ -1,4 +1,11 @@
|
|||
import codecs, traceback
|
||||
import ujson as json
|
||||
|
||||
from os import listdir
|
||||
from os.path import isfile, isdir, getmtime
|
||||
|
||||
from jinja2 import Environment, FileSystemLoader, ChoiceLoader
|
||||
from hamlpy.hamlpy import Compiler
|
||||
from sanic import response
|
||||
from markdown import markdown
|
||||
|
||||
|
@ -33,6 +40,7 @@ env = Environment(
|
|||
def render(tplfile, request, context, headers=None, status=200):
|
||||
data = global_variables.copy()
|
||||
data['request'] = request
|
||||
data['config'] = get.config('all')
|
||||
data.update(context)
|
||||
|
||||
if type(context) != dict:
|
||||
|
@ -50,7 +58,61 @@ def error(request, msg, status):
|
|||
if 'json' in request.headers.get('accept', '') or (request.path == '/inbox' and 'mozilla' not in request.headers.get('user-agent', '').lower()):
|
||||
return response.json({'err': msg}, status=status)
|
||||
|
||||
data = {'msg': msg, 'code': str(status), 'config': get_config('all')}
|
||||
data = {'msg': msg, 'code': str(status), 'config': get.config('all')}
|
||||
|
||||
return render('error.html', request, data, status=status)
|
||||
|
||||
|
||||
def build_templates():
|
||||
timefile = f'{stor_path}/build/times.json'
|
||||
updated = False
|
||||
|
||||
if not isdir(f'{stor_path}/build'):
|
||||
os.makedirs(f'{stor_path}/build')
|
||||
|
||||
if isfile(timefile):
|
||||
try:
|
||||
times = json.load(open(timefile))
|
||||
|
||||
except:
|
||||
times = {}
|
||||
|
||||
else:
|
||||
times = {}
|
||||
|
||||
for filename in listdir(f'{script_path}/frontend/templates'):
|
||||
modtime = getmtime(f'{script_path}/frontend/templates/{filename}')
|
||||
base, ext = filename.split('.')
|
||||
|
||||
if ext != 'haml':
|
||||
pass
|
||||
|
||||
elif base not in times or times.get(base) != modtime:
|
||||
updated = True
|
||||
logging.info(f"Template '{filename}' was changed. Building...")
|
||||
|
||||
try:
|
||||
template = f'{script_path}/frontend/templates/{filename}'
|
||||
destination = f'{stor_path}/build/{base}.html'
|
||||
haml_lines = codecs.open(template, 'r', encoding='utf-8').read().splitlines()
|
||||
|
||||
if not isfile(template):
|
||||
return False
|
||||
|
||||
compiler = Compiler()
|
||||
output = compiler.process_lines(haml_lines)
|
||||
|
||||
outfile = codecs.open(destination, 'w', encoding='utf-8')
|
||||
outfile.write(output)
|
||||
|
||||
logging.info(f"Template '{filename}' has been built")
|
||||
|
||||
except:
|
||||
traceback.print_exc()
|
||||
logging.error(f'Failed to build {filename}')
|
||||
|
||||
times[base] = modtime
|
||||
|
||||
if updated:
|
||||
with open(timefile, 'w') as filename:
|
||||
filename.write(json.dumps(times))
|
||||
|
|
|
@ -11,12 +11,12 @@ from sanic.exceptions import ServerError
|
|||
|
||||
from .log import logging
|
||||
from .config import script_path, version
|
||||
from .functions import cache, get_user, get_inbox
|
||||
from .functions import cache, get_inbox
|
||||
from .processing import process
|
||||
from .messages import fetch
|
||||
from .database import get
|
||||
from .admin import run, sanitize, get_instance_data, get_whitelist_data, get_domainbans, get_userbans
|
||||
from .database import get, put
|
||||
from .templates import render, error
|
||||
from . import admin
|
||||
|
||||
|
||||
host = get.config('host')
|
||||
|
@ -218,7 +218,7 @@ class WellknowWebfinger(HTTPMethodView):
|
|||
# Frontend
|
||||
class Home(HTTPMethodView):
|
||||
async def get(self, request):
|
||||
data = {'instances': get_instance_data()}
|
||||
data = {'instances': admin.get_instance_data()}
|
||||
return render('home.html', request, data)
|
||||
|
||||
|
||||
|
@ -230,15 +230,15 @@ class Faq(HTTPMethodView):
|
|||
|
||||
class Admin(HTTPMethodView):
|
||||
async def get(self, request, *args):
|
||||
whitelist = get_whitelist_data()
|
||||
whitelist = admin.get_whitelist_data()
|
||||
|
||||
data = {
|
||||
'instances': get_instance_data(),
|
||||
'instances': admin.get_instance_data(),
|
||||
'whitelist': whitelist,
|
||||
'wldomains': [row['domain'] for row in whitelist],
|
||||
'requests': get.requests('all'),
|
||||
'domainban': get_domainbans(),
|
||||
'userban': get_userbans()
|
||||
'requests': get.request('all'),
|
||||
'domainban': admin.get_domainbans(),
|
||||
'userban': admin.get_userbans()
|
||||
}
|
||||
|
||||
context = {'msg': 'UvU', 'data': data}
|
||||
|
@ -248,7 +248,7 @@ class Admin(HTTPMethodView):
|
|||
action = re.sub(r'[^a-z]+', '', action.lower())
|
||||
data = request['form']
|
||||
|
||||
run(action, data)
|
||||
admin.run(action, data)
|
||||
|
||||
return response.redirect('/admin')
|
||||
|
||||
|
@ -289,10 +289,11 @@ class Login(HTTPMethodView):
|
|||
if None in [username, password]:
|
||||
return await reterror(Login, request, 'Missing username or password')
|
||||
|
||||
if not check_password(username, password):
|
||||
if not get.verify_password(username, password):
|
||||
return await reterror(Login, request, 'Invalid username or password')
|
||||
|
||||
tokendata = create_token(username)
|
||||
tokendata = put.token(username)
|
||||
print(tokendata)
|
||||
|
||||
if not tokendata:
|
||||
return await reterror(Login, request, 'Failed to create token')
|
||||
|
@ -349,8 +350,8 @@ class Register(HTTPMethodView):
|
|||
else:
|
||||
data[key] = re.sub(r'[^a-zA-Z0-9@_.\-\!\'d,%{}]+', '', data[key]).strip()
|
||||
|
||||
if data['code'] != auth_code:
|
||||
return await reterror(Register, request, 'Invalid authentication code')
|
||||
#if data['code'] != get.auth_code:
|
||||
# return await reterror(Register, request, 'Invalid authentication code')
|
||||
|
||||
if get.user(data['username'].lower()):
|
||||
return await reterror(Register, request, 'User already exists')
|
||||
|
@ -358,8 +359,12 @@ class Register(HTTPMethodView):
|
|||
if data['password'] != data['password2']:
|
||||
return await reterror(Register, request, 'Passwords don\'t match')
|
||||
|
||||
userdata = {'user': data['username'], 'pass': data['password']}
|
||||
tokendata = admin_user('add', userdata)
|
||||
userdata = put.user(data['username'], data['password'])
|
||||
|
||||
if not userdata:
|
||||
return await reterror(Register, request, 'Failed to create user')
|
||||
|
||||
tokendata = put.token(userdata['id'])
|
||||
|
||||
if not tokendata:
|
||||
return await reterror(Register, request, 'Failed to create user')
|
||||
|
@ -374,7 +379,7 @@ class Register(HTTPMethodView):
|
|||
class Style(HTTPMethodView):
|
||||
async def get(self, request, **kwargs):
|
||||
maxage = 60*60*24*7 #ONE WEEK
|
||||
data = ({'msg': 'uvu'})
|
||||
data = {'msg': 'uvu'}
|
||||
headers = {'Content-Type': 'text/css', 'Cache-Control': f'public,max-age={maxage}, immutable'}
|
||||
return render('color.css', request, data, headers=headers)
|
||||
|
||||
|
@ -382,16 +387,16 @@ class Style(HTTPMethodView):
|
|||
class Setup(HTTPMethodView):
|
||||
async def get(self, request, *args):
|
||||
data = {'code': request['query'].get('code')}
|
||||
print('heck')
|
||||
|
||||
return render('setup.html', request, data)
|
||||
|
||||
async def post(self, request, action=''):
|
||||
from . import main
|
||||
data = request['form']
|
||||
|
||||
#if data.get('code') != get.auth_code:
|
||||
# return response.redirect('/setup')
|
||||
|
||||
run('settings', data)
|
||||
set_config({'setup': True})
|
||||
request.app.stop()
|
||||
put.config({'setup': True})
|
||||
|
||||
return await self.get(request)
|
||||
|
|
Loading…
Reference in a new issue