This commit is contained in:
b3yond 2019-03-06 19:43:59 +00:00 committed by GitHub
commit 7d41a1636d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
70 changed files with 160 additions and 37 deletions

3
.gitignore vendored
View file

@ -1,5 +1,7 @@
*.swp *.swp
*.pyc *.pyc
*.egg-info/
*.eggs
.idea/ .idea/
__pycache__/ __pycache__/
last_mention last_mention
@ -17,4 +19,3 @@ include/
lib/ lib/
share/ share/
local/ local/
venv/

2
setup.cfg Normal file
View file

@ -0,0 +1,2 @@
[aliases]
test=pytest

37
setup.py Normal file
View file

@ -0,0 +1,37 @@
from setuptools import setup
import sys
import os
PACKAGE_NAME = "ticketfrei"
sys.path.insert(0, os.path.join(os.path.dirname(os.path.realpath(__file__)), PACKAGE_NAME))
setup(
name=PACKAGE_NAME,
version='',
packages=[
PACKAGE_NAME
],
url='https://github.com/ticketfrei/ticketfrei',
license='ISC',
author='',
author_email='',
description='',
setup_requires=[
'pytest-runner',
],
install_requires=[
'bottle',
'gitpython',
'pyjwt',
'Markdown',
'Mastodon.py',
'pylibscrypt',
'pytoml',
'tweepy',
'twx',
],
tests_require=[
'pytest',
],
)

View file

@ -1,2 +0,0 @@
% rebase('template/wrapper.tpl', title='Login')
% include('template/login-plain.tpl')

View file

@ -17,7 +17,7 @@ def shutdown():
if __name__ == '__main__': if __name__ == '__main__':
logger = logging.getLogger() logger = logging.getLogger()
fh = logging.FileHandler('/var/log/ticketfrei/backend.log') fh = logging.FileHandler(config["log"]["log_backend"])
fh.setLevel(logging.DEBUG) fh.setLevel(logging.DEBUG)
logger.addHandler(fh) logger.addHandler(fh)

View file

View file

@ -1,6 +1,10 @@
import pytoml as toml import pytoml as toml
import os import os
ROOT_DIR = os.path.dirname(os.path.realpath(__file__))
TEMPLATE_DIR = os.path.join(ROOT_DIR, 'template', '')
STATIC_DIR = os.path.join(ROOT_DIR, 'static', '')
BOT_DIR = os.path.join(ROOT_DIR, 'bots')
def load_env(): def load_env():
""" """
@ -9,7 +13,7 @@ def load_env():
:return: config dictionary of dictionaries. :return: config dictionary of dictionaries.
""" """
with open('config.toml.example') as defaultconf: with open(os.path.join(ROOT_DIR, 'config.toml.example')) as defaultconf:
configdict = toml.load(defaultconf) configdict = toml.load(defaultconf)
try: try:
@ -54,12 +58,24 @@ def load_env():
except KeyError: except KeyError:
pass pass
try:
if os.environ['LOG_FRONTEND'] != "":
configdict['log']['log_frontend'] = os.environ['LOG_FRONTEND']
except KeyError:
pass
try:
if os.environ['LOG_BACKEND'] != "":
configdict['log']['log_backend'] = os.environ['LOG_BACKEND']
except KeyError:
pass
return configdict return configdict
# read config in TOML format (https://github.com/toml-lang/toml#toml) # read config in TOML format (https://github.com/toml-lang/toml#toml)
try: try:
with open('config.toml') as configfile: with open(os.path.join(ROOT_DIR, 'config.toml')) as configfile:
config = toml.load(configfile) config = toml.load(configfile)
except FileNotFoundError: except FileNotFoundError:
config = load_env() config = load_env()

View file

@ -11,6 +11,11 @@ contact = "b3yond@riseup.net"
[mail] [mail]
mbox_user = "root" mbox_user = "root"
aliases_path = "/etc/aliases"
[database] [database]
db_path = "/var/ticketfrei/db.sqlite" db_path = "/var/ticketfrei/db.sqlite"
[log]
log_frontend = "/var/ticketfrei/frontend.log"
log_backend = "/var/log/ticketfrei/backend.log"

View file

@ -243,7 +243,7 @@ u\d\d?"""
(uid, "bastard")) (uid, "bastard"))
else: else:
uid = json['uid'] uid = json['uid']
with open("/etc/aliases", "a+") as f: with open(config['mail']['aliases_path'], "a+") as f:
f.write(city + ": " + config["mail"]["mbox_user"] + "\n") f.write(city + ": " + config["mail"]["mbox_user"] + "\n")
self.execute("INSERT INTO email (user_id, email) VALUES(?, ?);", self.execute("INSERT INTO email (user_id, email) VALUES(?, ?);",
(uid, json['email'])) (uid, json['email']))

View file

@ -2,7 +2,7 @@
import bottle import bottle
from os import listdir, path from os import listdir, path
from bottle import get, post, redirect, request, response, view from bottle import get, post, redirect, request, response, view
from config import config from config import config, STATIC_DIR, TEMPLATE_DIR
from db import db from db import db
import logging import logging
import tweepy import tweepy
@ -19,13 +19,13 @@ def url(route):
@get('/') @get('/')
@view('template/propaganda.tpl') @view('propaganda.tpl')
def propaganda(): def propaganda():
pass pass
@post('/register') @post('/register')
@view('template/register.tpl') @view('register.tpl')
def register_post(): def register_post():
try: try:
email = request.forms['email'] email = request.forms['email']
@ -55,7 +55,7 @@ def register_post():
@get('/confirm/<city>/<token>') @get('/confirm/<city>/<token>')
@view('template/propaganda.tpl') @view('propaganda.tpl')
def confirm(city, token): def confirm(city, token):
# check whether city already exists # check whether city already exists
if db.by_city(city): if db.by_city(city):
@ -76,7 +76,7 @@ def version():
@post('/login') @post('/login')
@view('template/login.tpl') @view('login.tpl')
def login_post(): def login_post():
# check login # check login
try: try:
@ -95,14 +95,14 @@ def city_page(city, info=None):
citydict = db.user_facing_properties(city) citydict = db.user_facing_properties(city)
if citydict is not None: if citydict is not None:
citydict['info'] = info citydict['info'] = info
return bottle.template('template/city.tpl', **citydict) return bottle.template('city.tpl', **citydict)
return bottle.template('template/propaganda.tpl', return bottle.template('propaganda.tpl',
**dict(info='There is no Ticketfrei bot in your city' **dict(info='There is no Ticketfrei bot in your city'
' yet. Create one yourself!')) ' yet. Create one yourself!'))
@get('/city/mail/<city>') @get('/city/mail/<city>')
@view('template/mail.tpl') @view('mail.tpl')
def display_mail_page(city): def display_mail_page(city):
user = db.by_city(city) user = db.by_city(city)
return user.state() return user.state()
@ -139,34 +139,34 @@ def unsubscribe(token):
@get('/settings') @get('/settings')
@view('template/settings.tpl') @view('settings.tpl')
def settings(user): def settings(user):
return user.state() return user.state()
@post('/settings/markdown') @post('/settings/markdown')
@view('template/settings.tpl') @view('settings.tpl')
def update_markdown(user): def update_markdown(user):
user.set_markdown(request.forms['markdown']) user.set_markdown(request.forms['markdown'])
return user.state() return user.state()
@post('/settings/mail_md') @post('/settings/mail_md')
@view('template/settings.tpl') @view('settings.tpl')
def update_mail_md(user): def update_mail_md(user):
user.set_mail_md(request.forms['mail_md']) user.set_mail_md(request.forms['mail_md'])
return user.state() return user.state()
@post('/settings/goodlist') @post('/settings/goodlist')
@view('template/settings.tpl') @view('settings.tpl')
def update_trigger_patterns(user): def update_trigger_patterns(user):
user.set_trigger_words(request.forms['goodlist']) user.set_trigger_words(request.forms['goodlist'])
return user.state() return user.state()
@post('/settings/blocklist') @post('/settings/blocklist')
@view('template/settings.tpl') @view('settings.tpl')
def update_badwords(user): def update_badwords(user):
user.set_badwords(request.forms['blocklist']) user.set_badwords(request.forms['blocklist'])
return user.state() return user.state()
@ -187,12 +187,12 @@ def register_telegram(user):
@get('/static/<filename:path>') @get('/static/<filename:path>')
def static(filename): def static(filename):
return bottle.static_file(filename, root='static') return bottle.static_file(filename, root=STATIC_DIR)
# IS THIS USED?
@get('/guides/<filename:path>') #@get('/guides/<filename:path>')
def guides(filename): #def guides(filename):
return bottle.static_file(filename, root='guides') # return bottle.static_file(filename, root='guides')
@get('/logout/') @get('/logout/')
@ -265,14 +265,16 @@ def login_mastodon(user):
logger = logging.getLogger() logger = logging.getLogger()
fh = logging.FileHandler('/var/log/ticketfrei/error.log') fh = logging.FileHandler(config['log']['log_frontend'])
fh.setLevel(logging.DEBUG) fh.setLevel(logging.DEBUG)
logger.addHandler(fh) logger.addHandler(fh)
# TODO change TEMPLATE_PATH to BOTS_DIR after refactoring
bottle.TEMPLATE_PATH.insert(0, TEMPLATE_DIR)
application = bottle.default_app() application = bottle.default_app()
bottle.install(SessionPlugin('/')) bottle.install(SessionPlugin('/'))
if __name__ == '__main__': if __name__ == '__main__':
bottle.run(host="0.0.0.0", port=config["web"]["port"]) bottle.run(host=config["web"]["host"], port=config["web"]["port"])
else: else:
application.catchall = False application.catchall = False

View file

Before

Width:  |  Height:  |  Size: 628 KiB

After

Width:  |  Height:  |  Size: 628 KiB

View file

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

View file

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 6.8 KiB

View file

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

View file

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

View file

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 6.8 KiB

View file

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

View file

Before

Width:  |  Height:  |  Size: 6.2 KiB

After

Width:  |  Height:  |  Size: 6.2 KiB

View file

@ -1,4 +1,4 @@
% rebase('template/wrapper.tpl') % rebase('wrapper.tpl')
<% <%
import markdown as md import markdown as md

View file

@ -0,0 +1,2 @@
% rebase('wrapper.tpl', title='Login')
% include('login-plain.tpl')

View file

@ -1,4 +1,4 @@
% rebase('template/wrapper.tpl') % rebase('wrapper.tpl')
<% <%
import markdown as md import markdown as md

View file

@ -1,4 +1,4 @@
% rebase('template/wrapper.tpl') % rebase('wrapper.tpl')
% if defined('info'): % if defined('info'):
<div class="ui-widget"> <div class="ui-widget">
<div class="ui-state-highlight ui-corner-all" style="padding: 0.7em;"> <div class="ui-state-highlight ui-corner-all" style="padding: 0.7em;">
@ -7,7 +7,7 @@
</div> </div>
<br> <br>
% end % end
% include('template/login-plain.tpl') % include('login-plain.tpl')
<h1>Features</h1> <h1>Features</h1>
<p> <p>
Don't pay for public transport. Instead, warn each other Don't pay for public transport. Instead, warn each other
@ -45,7 +45,7 @@
share it with us, so others can use it, too!</li> share it with us, so others can use it, too!</li>
</ul></li> </ul></li>
</ul> </ul>
% include('template/register-plain.tpl') % include('register-plain.tpl')
<h2>Our Mission</h2> <h2>Our Mission</h2>
<p> <p>
Public transportation is meant to provide an easy and Public transportation is meant to provide an easy and

View file

@ -1,4 +1,4 @@
% rebase('template/wrapper.tpl', title='Register') % rebase('wrapper.tpl', title='Register')
% if defined('info'): % if defined('info'):
<div class="ui-widget"> <div class="ui-widget">
<div class="ui-state-highlight ui-corner-all" style="padding: 0.7em;"> <div class="ui-state-highlight ui-corner-all" style="padding: 0.7em;">
@ -6,5 +6,5 @@
</div> </div>
</div> </div>
% else: % else:
% include('template/register-plain.tpl') % include('register-plain.tpl')
% end % end

View file

@ -1,4 +1,4 @@
% rebase('template/wrapper.tpl') % rebase('wrapper.tpl')
<a href="/logout/"><button>Logout</button></a> <a href="/logout/"><button>Logout</button></a>
% if enabled: % if enabled:
@ -9,11 +9,12 @@
<% <%
# import all the settings templates from bots/*/settings.tpl # import all the settings templates from bots/*/settings.tpl
from config import BOT_DIR
import os import os
bots = os.listdir('bots') bots = os.listdir(BOT_DIR)
for bot in bots: for bot in bots:
include('bots/' + bot + '/settings.tpl', csrf=csrf, city=city) include(os.path.join(BOT_DIR, bot, 'settings.tpl'), csrf=csrf, city=city)
end end
%> %>

View file

@ -0,0 +1,20 @@
[twitter]
# You get those keys when you follow these steps:
# https://developer.twitter.com/en/docs/basics/authentication/guides/access-tokens
consumer_key = "your_consumer_key"
consumer_secret = "your_consumer_secret"
[web]
host = "0.0.0.0" # will be used by bottle as a host.
port = 80
contact = "b3yond@riseup.net"
[mail]
mbox_user = "root"
[database]
db_path = "/var/ticketfrei/db.sqlite"
[log]
log_frontend = "/var/ticketfrei/frontend.log"
log_backend = "/var/log/ticketfrei/backend.log"

View file

@ -0,0 +1,23 @@
from webtest import TestApp
import unittest
import frontend
app = TestApp(frontend.application)
class TestLogin(unittest.TestCase):
def setUp(self):
pass
def tearDown(self):
pass
def test_login_not_registered(self):
request = app.post('/login', {'email': '', 'pass': ''}, expect_errors=True)
self.assertEqual(401, request.status_code)
def test_login_registered(self):
request = app.post('/register', {'email': 'foo@abc.de', 'pass': 'bar', 'pass-repeat': 'bar', 'city': 'testcity'}, expect_errors=True)
request = app.post('/login', {'email': 'foor@abc.de', 'pass': 'bar'}, expect_errors=False)
self.assertEqual(200, request.status_code)

View file

@ -0,0 +1,16 @@
from webtest import TestApp
import unittest
import frontend
app = TestApp(frontend.application)
class TestRegister(unittest.TestCase):
def test_register(self):
request = app.post('/register', {'email': 'foo@abc.de', 'pass': 'bar', 'pass-repeat': 'bar', 'city': 'testcity'}, expect_errors=True)
self.assertEqual(200, request.status_code)
def test_getRoot(self):
request = app.get('/')
self.assertEqual(200, request.status_code)