create auto-restart script and auto-compile js

This commit is contained in:
Izalia Mae 2019-10-06 13:35:32 -04:00
parent 9a595f1576
commit d4c1d320b0
18 changed files with 333 additions and 87 deletions

15
.gitignore vendored
View file

@ -1,8 +1,13 @@
*.env
*.patch
# Python cache
__pycache__
/build
*.pyc
# Extra dirs
/build
/data
/bin
/bin
/misc
# Misc files
nohup.out
webapp/js/__target__

View file

@ -91,6 +91,10 @@ def first_setup():
def settings():
setresults = db.query('SELECT * FROM settings').dictresult()
if setresults == []:
logging.warning('Can\'t find settings in the database')
return
set_dict = {}
for line in setresults:
@ -100,5 +104,13 @@ def settings():
return set_dict
def update_timestamps():
for token in cache.token.items:
db.update('auth_tokens', {'id': cache.token.items[token]['id']}, access=cache.token.items[token]['timestamp'])
for token in cache.cookie.items:
db.update('login_cookies', {'id': cache.cookie.items[token]['id']}, access=cache.cookie.items[token]['timestamp'])
newtrans(first_setup())
SETTINGS = settings()

View file

@ -22,10 +22,10 @@ pyenv = env.get('PYENV', 'default').lower()
try:
if pyenv in ['prod', 'default']:
if pyenv == 'prod':
load_envbash(stor_path+'/prod.env')
elif pyenv == 'dev':
elif pyenv in ['dev', 'default']:
load_envbash(stor_path+'/dev.env')
else:
@ -108,7 +108,7 @@ logging.addHandler(console)
if pyenv in ['prod', 'default']:
if pyenv == 'default':
logging.warning('No environment specified. Assuming production')
logging.warning('No environment specified. Assuming development')
logging.warning('Set "PYENV" to "prod" or "dev" to disable this warning')
logging.debug('Starting in production mode')

11
dist/database.sql vendored
View file

@ -24,20 +24,23 @@ CREATE TABLE IF NOT EXISTS statuses (
timestamp FLOAT NOT NULL
);
CREATE TABLE IF NOT EXISTS tokens (
CREATE TABLE IF NOT EXISTS auth_tokens (
id SERIAL PRIMARY KEY,
userid INT NOT NULL,
appid INT NOT NULL,
token TEXT NOT NULL
token TEXT NOT NULL,
timestamp INT NOT NULL,
access INT
);
CREATE TABLE IF NOT EXISTS login_cookies (
id SERIAL PRIMARY KEY,
userid INT NOT NULL,
cookie TEXT UNIQUE NOT NULL,
timestamp INT NOT NULL,
address TEXT,
agent TEXT
agent TEXT,
timestamp INT NOT NULL,
access INT
);
CREATE TABLE IF NOT EXISTS guests (

View file

@ -1,7 +1,13 @@
from jinja2 import BaseLoader, TemplateNotFound
from os.path import join, exists, getmtime, isfile
from os import environ as env
from config import stor_path, script_path, logging
from simplecache import LRUCache
pyenv = env.get('PYENV', 'dev').lower()
class CustomLoader(BaseLoader):
@ -18,15 +24,16 @@ class CustomLoader(BaseLoader):
logging.debug('Can\'t find custom template file: '+custom_path+template)
path = join(self.path, template)
try:
mtime = getmtime(path)
except FileNotFoundError:
if isfile(path) == False:
logging.error('Can\'t find template file: '+path)
path = join(self.path, 'missing.html')
mtime = getmtime(path)
mtime = getmtime(path)
with open(path) as f:
logging.debug(f'Loading template: {path}')
source = f.read()
return source, path, lambda: mtime == getmtime(path)
get_time = lambda: mtime == getmtime(path)
return source, path, get_time

View file

@ -5,17 +5,6 @@
{% set user = None %}
{% endif %}
{% set colors %}
<div class="submenu">
<details>
<summary class="item"><a>Colors</a></summary>
{% for theme in themes() %}
<div class="item"><a href="javascript:void(0);" onclick="javascript:theme('{{theme}}');" class="theme_link">{{theme}}</a></div>
{% endfor %}
</details>
</div>
{% endset %}
<!DOCTYPE html>
<html>
<head>
@ -44,47 +33,7 @@
</script>
</head>
<body>
<div id="user_panel" class="menu menu-right">
{% if user != None %}
<details>
<summary id="menu_title"><a class="text">{{user.name}}</a></summary>
<div class="item"><a href="https://{{domain}}/">Home</a></div>
<div class="item"><a href="https://{{domain}}/welcome">User Panel</a></div>
<div class="item"><a href="https://{{domain}}/@{{user.handle}}">Profile</a></div>
<div class="submenu">
<details>
<summary class="item"><a class="text">Settings</a></summary>
<div class="item"><a href="https://{{domain}}/settings#profile">Profile</a></div>
<div class="item"><a href="https://{{domain}}/settings#account">Account</a></div>
<div class="item"><a href="https://{{domain}}/settings#options">Options</a></div>
{% if user.permissions < 2 %}<div class="item"><a href="https://{{domain}}/admin">Admin</a></div>{% endif %}
</details>
</div>
{{colors}}
<div class="submenu" id="toot_panel">
<details>
<summary class="item"><a class="text">Toot!</a></summary>
<form action="/poast" method="post">
<input type="text" name="warning" placeholder="Content Warning"><br>
<textarea id="toot_box" name="text" placeholder="*notices your post* OwO what's this?" maxlength="{{settings.char_limit}}"></textarea><br>
<input type="submit" value="YEET!">
</form>
</details>
</div>
<div class="item"><a href="https://{{domain}}/logout">Logout</a></div>
</details>
{% else %}
<details>
<summary id="menu_title">Guest</summary>
<div class="item"><a href="https://{{domain}}/">Home</a></div>
<div class="item"><a href="https://{{domain}}/login">Login</a></div>
<div class="item"><a href="https://{{domain}}/register">Register</a></div>
{{colors}}
</details>
{% endif %}
</div>
{% include "components/menu.html" %}
<div id="header">
<h1 class="title"><a href="https://{{domain}}">{{name}}</a></h1>
</div>

View file

@ -0,0 +1,8 @@
<div class="submenu">
<details>
<summary class="item"><a>Colors</a></summary>
{% for theme in themes() %}
<div class="item"><a href="javascript:void(0);" onclick="javascript:theme('{{theme}}');" class="theme_link">{{theme}}</a></div>
{% endfor %}
</details>
</div>

View file

@ -0,0 +1,40 @@
<div id="user_panel" class="menu menu-right">
{% if user != None %}
<details>
<summary id="menu_title"><a class="text">{{user.name}}</a></summary>
<div class="item"><a href="https://{{domain}}/">Home</a></div>
<div class="item"><a href="https://{{domain}}/welcome">User Panel</a></div>
<div class="item"><a href="https://{{domain}}/@{{user.handle}}">Profile</a></div>
<div class="submenu">
<details>
<summary class="item"><a class="text">Settings</a></summary>
<div class="item"><a href="https://{{domain}}/settings#profile">Profile</a></div>
<div class="item"><a href="https://{{domain}}/settings#account">Account</a></div>
<div class="item"><a href="https://{{domain}}/settings#options">Options</a></div>
{% if user.permissions < 2 %}<div class="item"><a href="https://{{domain}}/admin">Admin</a></div>{% endif %}
</details>
</div>
{% include "components/colors.html" %}
<div class="submenu" id="toot_panel">
<details>
<summary class="item"><a class="text">Toot!</a></summary>
<form action="/poast" method="post">
<input type="text" name="warning" placeholder="Content Warning"><br>
<textarea id="toot_box" name="text" placeholder="*notices your post* OwO what's this?" maxlength="{{settings.char_limit}}"></textarea><br>
<input type="submit" value="YEET!">
</form>
</details>
</div>
<div class="item"><a href="https://{{domain}}/logout">Logout</a></div>
</details>
{% else %}
<details>
<summary id="menu_title">Guest</summary>
<div class="item"><a href="https://{{domain}}/">Home</a></div>
<div class="item"><a href="https://{{domain}}/login">Login</a></div>
<div class="item"><a href="https://{{domain}}/register">Register</a></div>
{% include "components/colors.html" %}
</details>
{% endif %}
</div>

View file

@ -4,8 +4,7 @@
{% block content %}
<center>
<font size=2>It's only pre-alpha</font><br><br><br><br><br><br><br><br><br><br><br><br><br><br>
<font size=1>merp</font><br><br><br><br><br><br><br><br><br><br><br><br><br><br>
<font size=1>I'm just gonna end up shitposting with this damn thing</font>
<font size=2>I don't know what I'm doing tbh</font><br><br><br><br><br><br><br>
<font style="font-size: 8px">merp</font><br><br><br><br><br><br><br>
</center>
{% endblock %}

View file

@ -59,6 +59,15 @@
<div id="options" class="section">
<h2>Options</h2>
<form action="/settings" method="post">
<div class="grid-container">
<div class="grid-item">
Colors
<input type="radio" name="color" value="{{color}}" id="color-{{color}}"><label for="color-{{color}}">{{color}}</label>
</div>
</div>
</form>
</div>
</div>
{% endblock %}

74
reload.py Executable file
View file

@ -0,0 +1,74 @@
#!/usr/bin/env python3
import time
import os
import re
import subprocess
import signal
import sys
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
os.environ['PYENV'] = 'dev'
from config import logging
pid = ''
def run(restart=False):
global pid
if restart == True and pid != '':
logging.info('Terminating process...')
os.kill(pid, signal.SIGTERM)
logging.info('Starting process...')
proc = subprocess.Popen(['nohup', './server.py'], preexec_fn=os.setpgrp)
pid = proc.pid
logging.info(f'Process PID: {pid}')
class MyHandler(FileSystemEventHandler):
def on_modified(self, event):
filename, ext = os.path.splitext(os.path.relpath(event.src_path))
if event.event_type == 'modified' and ext == '.py' and re.search(ignore_paths, filename) == None and filename not in ignore_filenames:
logging.info('Restarting server...')
run(restart=True)
def safe_stop():
logging.info('Stopping process watcher')
observer.stop()
observer.join()
if pid != '':
logging.info(f'Killing process: {pid}')
os.kill(pid, signal.SIGTERM)
sys.exit()
if __name__ == "__main__":
run()
path = os.path.dirname(os.path.realpath(__file__))
observer = Observer()
observer.schedule(MyHandler(), path, recursive=True)
ignore_paths = 'webapp/js|bin|dist|misc|data'
ignore_filenames = ['reload']
logging.info('Starting process watcher')
observer.start()
try:
while True:
time.sleep(100)
except KeyboardInterrupt:
pass
safe_stop()

View file

@ -3,13 +3,15 @@ import aiohttp
import jinja2
import aiohttp_jinja2
import json
import signal
import sys
from jinja2 import FileSystemLoader, select_autoescape
from config import config, logging
from backend import mastodon_api, api, middleware, wellknown
from backend.database import SETTINGS, newtrans, get
from backend.database import SETTINGS, newtrans, get, update_timestamps
from frontend import views, resources, user, redirects
from functions import color, todate, themes
from frontend.template_loader import CustomLoader
@ -109,6 +111,7 @@ web.router.add_get('/socks', redirects.socks)
async def start_web():
runner = aiohttp.web.AppRunner(web, access_log_format='%{X-Real-Ip}i "%r" %s %b "%{User-Agent}i"')
await runner.setup()
listen = config['listen']
port = config['port']
@ -119,7 +122,35 @@ async def start_web():
await site.start()
async def save_tokens():
while True:
logging.debug('Saving updated timestamps for login and auth tokens to the database')
update_timestamps()
await asyncio.sleep(600)
def safe_stop(*args):
logging.debug('Saving updated timestamps')
update_timestamps()
logging.info('Bye')
sys.exit()
def main():
loop = asyncio.get_event_loop()
asyncio.ensure_future(start_web())
loop.run_forever()
signal.signal(signal.SIGHUP, safe_stop)
signal.signal(signal.SIGINT, safe_stop)
signal.signal(signal.SIGQUIT, safe_stop)
signal.signal(signal.SIGTERM, safe_stop)
try:
loop = asyncio.get_event_loop()
asyncio.ensure_future(start_web())
asyncio.ensure_future(save_tokens())
loop.run_forever()
except KeyboardInterrupt:
pass
safe_stop()

View file

@ -1,12 +1,33 @@
#!/usr/bin/env python3
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
from os import environ as ENV
import routes
from config import logging
from webapp import precompile
logging.debug('OvO')
if __name__ == '__main__':
try:
import routes
routes.main()
class MyHandler(FileSystemEventHandler):
def on_modified(self, event):
if event.event_type == 'modified' and event.src_path[-3:] == '.py' and event.src_path.startswith('webapp/js/__target__') == False:
logging.info(event.src_path)
precompile()
except KeyboardInterrupt:
logging.info('Bye')
if __name__ == "__main__":
if ENV.get('PYENV', 'default').lower() in ['dev', 'default']:
path = 'webapp/js'
observer = Observer()
observer.schedule(MyHandler(), path, recursive=True)
logging.info('Starting javascript watcher')
observer.start()
routes.main()
if ENV.get('PYENV', 'default').lower() in ['dev', 'default']:
logging.info('Stopping javascript watcher')
observer.stop()
observer.join()

38
server.spec Normal file
View file

@ -0,0 +1,38 @@
# -*- mode: python ; coding: utf-8 -*-
block_cipher = None
a = Analysis(['server.py'],
pathex=[],
binaries=[],
datas=[
('frontend/templates/static', 'static'),
('frontend/templates/templates', 'templates'),
('frontend/templates/themes', 'themes'),
('dist', 'dist')
],
hiddenimports=[],
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False)
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
[],
name='social',
debug=False,
bootloader_ignore_signals=False,
strip=True,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=True )

View file

@ -91,3 +91,10 @@ if __name__ == '__main__':
except KeyboardInterrupt:
print('Bye!')
'''
I'll need these later
user_check = pre_db.query(f"SELECT 1 FROM pg_roles WHERE rolname='{DB_CONFIG['host']}'")
perms_check = pre_db.query(f"SELECT rolcreatedb FROM pg_authid WHERE rolname = '{DB_CONFIG['host']}'")
'''

View file

@ -72,14 +72,18 @@ class TTLCache:
def store(self, key, value):
while len(self.items) >= self.maxsize:
while len(self.items) >= self.maxsize and self.maxsize != 0:
timestamp = int(datetime.timestamp(datetime.now()))
self.items.popitem(last=False)
if (key in self.items) == False:
timestamp = int(datetime.timestamp(datetime.now()))
data = {'data': value, 'timestamp': timestamp + self.ttl}
data = {'data': value, 'timestamp': timestamp}
self.items[key] = data
if self.items[key]['timestamp'] + self.ttl < timestamp:
del self.items[key]
self.items[key]['timestamp'] = timestamp
self.items.move_to_end(key)
@ -88,3 +92,35 @@ class TTLCache:
return self.items[key]['data']
return None
class LRUCache:
def __init__(self, maxsize=1024):
self.items = OrderedDict()
self.maxsize = maxsize
def invalidate(self, key):
if key in self.items:
del self.items[key]
return True
return False
def store(self, key, value):
while len(self.items) >= self.maxsize and self.maxsize != 0:
self.items.popitem(last=False)
if (key in self.items) == False:
self.items[key] = value
self.items.move_to_end(key)
def fetch(self, key):
if key in self.items:
return self.items[key]
return None

6
webapp/__init__.py Normal file
View file

@ -0,0 +1,6 @@
import os
from config import script_path
def precompile():
os.system(f'transcrypt --map {script_path}/webapp/js/__init__.py')

1
webapp/js/__init__.py Normal file
View file

@ -0,0 +1 @@
print('OwO')