a lot
This commit is contained in:
parent
ab33e0db9c
commit
749b1f3619
3
Makefile
3
Makefile
|
@ -18,11 +18,12 @@ setupvenv:
|
|||
$(PYTHON_SYS) -m venv $(VENV)
|
||||
$(PYTHON) -m pip install -U pip
|
||||
$(PYTHON) -m pip install wheel, setuptools
|
||||
# $(PYTHON) -m pip install -r requirements.txt
|
||||
$(PYTHON) -m pip install -r requirements.txt
|
||||
|
||||
setupizzylib:
|
||||
$(PYTHON) -m pip uninstall -y izzylib-base izzylib-templates izzylib-sql izzylib-requests-client izzylib-dbus
|
||||
$(PYTHON) -m pip install "git+https://git.barkshark.xyz/izaliamae/izzylib.git@rework#egg=izzylib-base&subdirectory=base"
|
||||
$(PYTHON) -m pip install "git+https://git.barkshark.xyz/izaliamae/izzylib.git@rework#egg=izzylib-password-hasher&subdirectory=hasher"
|
||||
$(PYTHON) -m pip install "git+https://git.barkshark.xyz/izaliamae/izzylib.git@rework#egg=izzylib-http-server&subdirectory=http_server"
|
||||
$(PYTHON) -m pip install "git+https://git.barkshark.xyz/izaliamae/izzylib.git@rework#egg=izzylib-templates&subdirectory=template"
|
||||
$(PYTHON) -m pip install "git+https://git.barkshark.xyz/izaliamae/izzylib.git@rework#egg=izzylib-sql&subdirectory=sql"
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
import getpass, sys
|
||||
|
||||
from envbash import load_envbash
|
||||
from izzylib import DotDict, Path, boolean, logging
|
||||
from os import getcwd, environ as env
|
||||
|
||||
from izzylib import DotDict, Path, logging
|
||||
|
||||
|
||||
scriptpath = Path(__file__).resolve.parent
|
||||
|
||||
|
@ -36,7 +35,7 @@ SYNC_DB_HOST=/var/run/postgresql
|
|||
SYNC_DB_PORT=5432
|
||||
SYNC_DB_NAME=pywebsync
|
||||
SYNC_DB_USER={getpass.getuser()}
|
||||
SYNC_DB_PASS=changeme
|
||||
#SYNC_DB_PASS=changeme
|
||||
SYNC_DB_SSL=False
|
||||
""")
|
||||
|
||||
|
@ -51,5 +50,21 @@ config = DotDict(
|
|||
alt_hosts = env.get('SYNC_ALT_HOSTS', '').split(',')
|
||||
)
|
||||
|
||||
|
||||
if env.get('SYNC_DB_TYPE', 'sqlite').lower() == 'sqlite':
|
||||
dbconfig = DotDict(
|
||||
name = path.data.join('database.db')
|
||||
)
|
||||
|
||||
else:
|
||||
dbconfig = DotDict({
|
||||
'host': env.get('SYNC_DB_HOST', '/var/run/postgresql'),
|
||||
'port': int(env.get('SYNC_DB_PORT', 5432)),
|
||||
'name': env.get('SYNC_DB_NAME', 'pywebsync'),
|
||||
'user': env.get('SYNC_DB_USER', getpass.getuser()),
|
||||
'pass': env.get('SYNC_DB_PASS'),
|
||||
'ssl': boolean(env.get('SYNC_DB_SSL'))
|
||||
})
|
||||
|
||||
config.host = env.get('SYNC_HOST', f'{config.listen}:{config.port}')
|
||||
logging.set_config('level', env.get('SYNC_LOG_LEVEL', 'info'))
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
-extends 'base.haml'
|
||||
-set page = 'Account'
|
||||
-block content
|
||||
#title -> =page
|
|
@ -1,4 +0,0 @@
|
|||
-extends 'base.haml'
|
||||
-set page = 'Admin'
|
||||
-block content
|
||||
#title -> =page
|
|
@ -2,14 +2,14 @@
|
|||
-set page = 'Login'
|
||||
|
||||
-block head
|
||||
%link rel='stylesheet' type='text/css' href='{{cfg.proto}}://{{cfg.web_host}}/style/logreg_form.css'
|
||||
%link rel='stylesheet' type='text/css' href='/style/logreg_form.css'
|
||||
|
||||
-block content
|
||||
#title -> =page
|
||||
%form(action='{{cfg.proto}}://{{cfg.web_host}}/login', method='post')
|
||||
%form(action='/login', method='post')
|
||||
%center
|
||||
%input(type='text', name='username', placeholder='username')
|
||||
%input(type='text', name='handle', placeholder='username', value='{{handle if handle else ""}}')
|
||||
%br
|
||||
%input(type='password', name='password', placeholder='password'})
|
||||
%br
|
||||
%input(type='submit', value='submit', class='button')
|
||||
%input(type='submit', value='Login', class='button')
|
||||
|
|
|
@ -2,17 +2,18 @@
|
|||
-set page = "Register"
|
||||
|
||||
-block head
|
||||
%link rel='stylesheet' type='text/css' href='{{cfg.proto}}://{{cfg.web_host}}/style/logreg_form.css'
|
||||
%link rel='stylesheet' type='text/css' href='/style/logreg_form.css'
|
||||
|
||||
-block content
|
||||
#title -> =page
|
||||
|
||||
%form(action="https://{{cfg.web_host}}/register", method="post", autocomplete="new-password")
|
||||
%form(action="/register", method="post", autocomplete="new-password")
|
||||
%center
|
||||
%input(type="text", name="username", placeholder="username")
|
||||
%br
|
||||
%input(type="text", name="password", placeholder="password")
|
||||
%br
|
||||
%input(type="text", name="password2", placeholder="password again")
|
||||
%br
|
||||
%input(type="text", name="display", placeholder="display name", value="{{form.display or ''}}")
|
||||
%input(type="text", name="handle", placeholder="username", value="{{form.handle or ''}}")
|
||||
%input(type="password", name="password", placeholder="password", value="{{form.password or ''}}")
|
||||
%input(type="password", name="password2", placeholder="password again", value="{{form.password2 or ''}}")
|
||||
%img(src="data:image/webp;base64,{{captcha_base64}}")
|
||||
%input(type="text", name="captcha_text", placeholder="image text (case insensitive)")
|
||||
%input(type="hidden", name="captcha_base64", value="{{captcha_base64}}")
|
||||
%input(type="submit", value="submit", class="button")
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
input {
|
||||
padding: 3px;
|
||||
padding: 5px;
|
||||
text-align: center;
|
||||
border-radius: 5px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
input[type='submit'] {
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
from izzylib.http_server import MiddlewareBase, Response
|
||||
|
||||
from .database import db
|
||||
|
||||
|
||||
auth_paths = [
|
||||
'/login',
|
||||
'/register'
|
||||
]
|
||||
|
||||
reg_paths = [
|
||||
'/account',
|
||||
'/admin',
|
||||
'/logout'
|
||||
]
|
||||
|
||||
|
||||
class LoginDetect(MiddlewareBase):
|
||||
async def handler(self, request):
|
||||
response = Response(self.app, request)
|
||||
code = request.cookies.get('token')
|
||||
token = db.get('token', code)
|
||||
|
||||
if token and token.user:
|
||||
request.ctx.token = token
|
||||
request.ctx.user = token.user
|
||||
request.user_level = token.user.level
|
||||
|
||||
else:
|
||||
request.ctx.token = None
|
||||
request.ctx.user = None
|
||||
|
||||
if any(map(request.path.startswith, reg_paths)) and not token:
|
||||
return response.redir('/login')
|
||||
|
||||
if (any(map(request.path.startswith, auth_paths))) and token and token.user:
|
||||
return response.redir('/account')
|
|
@ -1,8 +1,28 @@
|
|||
import sys
|
||||
|
||||
from izzylib import logging
|
||||
from izzylib.http_server import Application, View
|
||||
from os import environ as env
|
||||
|
||||
from . import views, __software__, __version__
|
||||
from . import middleware, views, __software__, __version__
|
||||
from .config import config, path
|
||||
from .database import OperationalError, db, version
|
||||
|
||||
|
||||
try:
|
||||
dbversion = db.get('config', 'version')
|
||||
|
||||
if not dbversion:
|
||||
logging.error('Database has not been setup. Please run `make setup` first.')
|
||||
sys.exit()
|
||||
|
||||
if dbversion < version:
|
||||
logging.error('Database needs to be migrated. Please run `make migrate`.')
|
||||
sys.exit()
|
||||
|
||||
except OperationalError as e:
|
||||
logging.error('Database has not been setup. Please run `make setup` first.')
|
||||
sys.exit()
|
||||
|
||||
|
||||
app = Application(
|
||||
|
@ -10,9 +30,12 @@ app = Application(
|
|||
version = __version__,
|
||||
git_repo = 'https://git.barkshark.xyz/izaliamae/pyweb-sync',
|
||||
tpl_search = [path.frontend],
|
||||
access_log = True,
|
||||
**config
|
||||
)
|
||||
|
||||
app.add_middleware(middleware.LoginDetect)
|
||||
|
||||
|
||||
## Frontend
|
||||
for cls in dir(views):
|
||||
|
|
|
@ -1,9 +1,19 @@
|
|||
from izzylib import DotDict, logging
|
||||
from izzylib.cache import TtlCache
|
||||
from izzylib.http_server import UserLevel, View
|
||||
|
||||
from ..config import config
|
||||
from ..database import db
|
||||
from ..functions import Captcha
|
||||
|
||||
|
||||
cache = DotDict(
|
||||
captcha = TtlCache(ttl='30m')
|
||||
)
|
||||
|
||||
|
||||
class Home(View):
|
||||
paths = ['/']
|
||||
menu = ('Home', paths[0])
|
||||
|
||||
async def get(self, request, response):
|
||||
return response.template('page/home.haml')
|
||||
|
@ -11,42 +21,119 @@ class Home(View):
|
|||
|
||||
class Register(View):
|
||||
paths = ['/register']
|
||||
menu = ('Register', paths[0])
|
||||
|
||||
async def get(self, request, response):
|
||||
return response.template('page/register.haml')
|
||||
async def get(self, request, response, data={}, msg=None, error=None):
|
||||
capt = Captcha()
|
||||
|
||||
if logging.get_config('level') == logging.Levels.DEBUG:
|
||||
capt.linecolor = (0,0,0,0)
|
||||
|
||||
captcha_base64 = capt.get_captcha().decode()
|
||||
cache.captcha.store(captcha_base64, capt)
|
||||
|
||||
data = {
|
||||
'captcha_base64': captcha_base64,
|
||||
'form': data
|
||||
}
|
||||
|
||||
if msg:
|
||||
data['message'] = msg
|
||||
|
||||
if error:
|
||||
data['error'] = error
|
||||
|
||||
return response.template('page/register.haml', data)
|
||||
|
||||
|
||||
async def post(self, request, response):
|
||||
pass
|
||||
capt_img = request.data.form.pop('captcha_base64', None)
|
||||
capt_text = request.data.form.pop('captcha_text', '')
|
||||
capt = cache.captcha.pop(capt_img, None)
|
||||
|
||||
handle = request.data.form.get('handle')
|
||||
password = request.data.form.get('password')
|
||||
password2 = request.data.form.get('password2')
|
||||
display = request.data.form.get('display', handle)
|
||||
|
||||
if not handle:
|
||||
return await self.get(request, response, data=request.data.form, error='Missing handle')
|
||||
|
||||
if not password or not password2:
|
||||
return await self.get(request, response, data=request.data.form, error='Missing one or both passwords')
|
||||
|
||||
if password != password2:
|
||||
return await self.get(request, response, data=request.data.form, error='Passwords do not match')
|
||||
|
||||
if not capt_img:
|
||||
return await self.get(request, response, data=request.data.form, error='Missing captcha')
|
||||
|
||||
if not capt:
|
||||
return await self.get(request, response, data=request.data.form, error='Invalid captcha')
|
||||
|
||||
if capt_text.lower() != capt.text.lower():
|
||||
return await self.get(request, response, data=request.data.form, error='Captcha text does not match')
|
||||
|
||||
user = db.put('user', handle, password, display)
|
||||
token = db.put('token', user.id)
|
||||
token.set_cookie(response)
|
||||
|
||||
return response.redir('/account/home')
|
||||
|
||||
|
||||
class Login(View):
|
||||
paths = ['/login']
|
||||
menu = ('Login', paths[0])
|
||||
|
||||
async def get(self, request, response):
|
||||
return response.template('page/login.haml')
|
||||
async def get(self, request, response, msg=None, error=None, handle=None):
|
||||
data = {'handle': handle}
|
||||
|
||||
if msg:
|
||||
data['message'] = msg
|
||||
|
||||
if error:
|
||||
data['error'] = error
|
||||
|
||||
return response.template('page/login.haml', context=data)
|
||||
|
||||
|
||||
async def post(self, request, response):
|
||||
pass
|
||||
username = request.data.form.get('handle')
|
||||
password = request.data.form.get('password')
|
||||
|
||||
if not username:
|
||||
return await self.get(request, response, error='Missing username')
|
||||
|
||||
if not password:
|
||||
return await self.get(request, response, error='Missing password', handle=username)
|
||||
|
||||
try:
|
||||
if db.get('password', username, password):
|
||||
return response.redir('/account/home')
|
||||
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
return await self.get(request, response, error='Invalid username or password', handle=username)
|
||||
|
||||
|
||||
class Logout(View):
|
||||
paths = ['/logout']
|
||||
menu = ('Logout', paths[0], UserLevel.USER)
|
||||
|
||||
async def get(self, request, response):
|
||||
return response.template('page/logout.haml')
|
||||
print(request.ctx.token)
|
||||
response = await Login.get(self, request, response, msg='Logged out')
|
||||
print(request.ctx.token)
|
||||
request.ctx.token.del_cookie(response)
|
||||
return response
|
||||
|
||||
|
||||
class Account(View):
|
||||
paths = ['/account']
|
||||
menu = ('Account', paths[0], UserLevel.USER)
|
||||
paths = ['/account', '/account/<page>']
|
||||
|
||||
async def get(self, request, response):
|
||||
return response.template('page/account.haml')
|
||||
async def get(self, request, response, page=None):
|
||||
if not page:
|
||||
return response.redir('/account/home')
|
||||
|
||||
return response.template(f'page/account/{page}.haml')
|
||||
|
||||
|
||||
async def post(self, request, response):
|
||||
|
@ -54,11 +141,13 @@ class Account(View):
|
|||
|
||||
|
||||
class Admin(View):
|
||||
paths = ['/admin']
|
||||
menu = ('Admin', paths[0], UserLevel.ADMIN)
|
||||
paths = ['/admin', '/admin/<page>']
|
||||
|
||||
async def get(self, request, response):
|
||||
return response.template('page/admin.haml')
|
||||
if not page:
|
||||
return response.redir('/admin/home')
|
||||
|
||||
return response.template(f'page/admin/{page}.haml')
|
||||
|
||||
|
||||
async def post(self, request, response):
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"bin": "python3",
|
||||
"bin": "~/.local/share/venv/pyweb-sync/bin/python3",
|
||||
"args": ["-m", "pyweb_sync"],
|
||||
"env": {"LOG_LEVEL": "DEBUG"},
|
||||
"path": "/home/zoey/data/src/mine/pyweb-sync",
|
||||
|
|
Loading…
Reference in a new issue