2022-12-18 16:40:39 +00:00
|
|
|
from sanic import Sanic, response, exceptions
|
|
|
|
from sanic.response import text, html, redirect, raw
|
|
|
|
from jinja2 import Environment, FileSystemLoader
|
2024-02-13 12:29:03 +00:00
|
|
|
from time import time, sleep
|
2022-12-18 16:40:39 +00:00
|
|
|
import httpx
|
2022-12-18 16:48:29 +00:00
|
|
|
from os.path import join
|
2022-12-18 16:40:39 +00:00
|
|
|
from ext import *
|
|
|
|
from config import *
|
2023-01-08 10:48:01 +00:00
|
|
|
from aztec_code_generator import AztecCode
|
2023-12-30 10:27:42 +00:00
|
|
|
from propic import resetDefaultPropic
|
2023-01-08 10:48:01 +00:00
|
|
|
from io import BytesIO
|
2023-07-04 21:07:39 +00:00
|
|
|
from asyncio import Queue
|
2024-01-29 23:35:56 +00:00
|
|
|
from messages import LOCALES
|
2023-07-04 21:07:39 +00:00
|
|
|
import sqlite3
|
2024-02-13 12:29:03 +00:00
|
|
|
import requests
|
|
|
|
import sys
|
2024-02-14 19:22:56 +00:00
|
|
|
from sanic.log import logger, logging, access_logger
|
|
|
|
from metrics import *
|
2024-05-20 10:07:59 +00:00
|
|
|
from utils import isSessionAdmin
|
2024-02-29 10:42:05 +00:00
|
|
|
from email_util import killSmptClient
|
2024-02-22 18:15:33 +00:00
|
|
|
import pretixClient
|
|
|
|
import traceback
|
2024-02-14 15:10:27 +00:00
|
|
|
|
2024-05-20 10:07:59 +00:00
|
|
|
|
2022-12-18 16:40:39 +00:00
|
|
|
app = Sanic(__name__)
|
|
|
|
app.static("/res", "res/")
|
|
|
|
|
|
|
|
app.ext.add_dependency(Order, get_order)
|
|
|
|
app.ext.add_dependency(Quotas, get_quotas)
|
|
|
|
|
|
|
|
from room import bp as room_bp
|
2022-12-19 21:08:59 +00:00
|
|
|
from propic import bp as propic_bp
|
2023-05-23 19:02:44 +00:00
|
|
|
from karaoke import bp as karaoke_bp
|
2023-01-19 16:02:57 +00:00
|
|
|
from stats import bp as stats_bp
|
2023-05-08 21:04:15 +00:00
|
|
|
from api import bp as api_bp
|
|
|
|
from carpooling import bp as carpooling_bp
|
2023-07-04 21:07:39 +00:00
|
|
|
from checkin import bp as checkin_bp
|
2024-01-08 21:02:27 +00:00
|
|
|
from admin import bp as admin_bp
|
2022-12-18 16:40:39 +00:00
|
|
|
|
2024-05-13 11:30:21 +00:00
|
|
|
app.blueprint([room_bp, karaoke_bp, propic_bp, stats_bp, api_bp, carpooling_bp, checkin_bp, admin_bp])
|
2024-02-22 18:15:33 +00:00
|
|
|
|
|
|
|
|
|
|
|
async def clear_session(response):
|
|
|
|
response.delete_cookie("foxo_code")
|
|
|
|
response.delete_cookie("foxo_secret")
|
|
|
|
|
|
|
|
@app.exception(exceptions.SanicException if DEV_MODE else Exception)
|
|
|
|
async def handleException(request, exception):
|
|
|
|
incErrorNo()
|
2024-02-14 19:22:56 +00:00
|
|
|
logger.warning(f"{request} -> {exception}")
|
2024-02-22 18:15:33 +00:00
|
|
|
statusCode = exception.status_code if hasattr(exception, 'status_code') else 500
|
|
|
|
try:
|
|
|
|
tpl = app.ctx.tpl.get_template('error.html')
|
2024-05-19 19:07:31 +00:00
|
|
|
r = html(tpl.render(exception=exception, status_code=statusCode), status=statusCode)
|
2024-02-22 18:15:33 +00:00
|
|
|
except:
|
|
|
|
traceback.print_exc()
|
|
|
|
|
|
|
|
if statusCode == 403:
|
2024-05-13 08:25:45 +00:00
|
|
|
await clear_session(r)
|
2023-05-11 22:18:49 +00:00
|
|
|
return r
|
2022-12-18 16:40:39 +00:00
|
|
|
|
2024-02-22 18:15:33 +00:00
|
|
|
|
2022-12-18 16:40:39 +00:00
|
|
|
@app.before_server_start
|
|
|
|
async def main_start(*_):
|
2024-01-21 23:35:44 +00:00
|
|
|
logger.info(f"[{app.name}] >>>>>> main_start <<<<<<")
|
2024-02-11 22:57:52 +00:00
|
|
|
logger.setLevel(LOG_LEVEL)
|
2024-02-14 19:22:56 +00:00
|
|
|
access_logger.addFilter(MetricsFilter())
|
2024-01-04 21:15:02 +00:00
|
|
|
|
2024-01-13 15:59:13 +00:00
|
|
|
app.config.REQUEST_MAX_SIZE = PROPIC_MAX_FILE_SIZE * 3
|
2023-01-17 21:25:35 +00:00
|
|
|
|
|
|
|
app.ctx.om = OrderManager()
|
2023-07-04 21:07:39 +00:00
|
|
|
if FILL_CACHE:
|
2024-02-22 18:15:33 +00:00
|
|
|
checked, success = await app.ctx.om.update_cache(check_itemsQuestions=True)
|
|
|
|
if checked and not success:
|
|
|
|
logger.error(f"[{app.name}] Failure in app startup: An error occurred while loading items or questions or cache.")
|
|
|
|
app.stop()
|
2023-07-04 21:07:39 +00:00
|
|
|
|
|
|
|
app.ctx.nfc_counts = sqlite3.connect('data/nfc_counts.db')
|
|
|
|
|
2023-05-11 22:18:49 +00:00
|
|
|
app.ctx.login_codes = {}
|
2023-07-04 21:07:39 +00:00
|
|
|
|
2022-12-18 16:40:39 +00:00
|
|
|
app.ctx.tpl = Environment(loader=FileSystemLoader("tpl"), autoescape=True)
|
|
|
|
app.ctx.tpl.globals.update(time=time)
|
2023-05-08 21:04:15 +00:00
|
|
|
app.ctx.tpl.globals.update(PROPIC_DEADLINE=PROPIC_DEADLINE)
|
2024-05-20 10:29:45 +00:00
|
|
|
app.ctx.tpl.globals.update(ROOM_DEADLINE=ROOM_DEADLINE)
|
2024-01-29 23:35:56 +00:00
|
|
|
app.ctx.tpl.globals.update(LOCALES=LOCALES)
|
2024-01-08 21:02:27 +00:00
|
|
|
app.ctx.tpl.globals.update(ITEMS_ID_MAP=ITEMS_ID_MAP)
|
|
|
|
app.ctx.tpl.globals.update(ITEM_VARIATIONS_MAP=ITEM_VARIATIONS_MAP)
|
2023-12-30 10:27:42 +00:00
|
|
|
app.ctx.tpl.globals.update(ROOM_TYPE_NAMES=ROOM_TYPE_NAMES)
|
2024-01-08 21:02:27 +00:00
|
|
|
app.ctx.tpl.globals.update(PROPIC_MIN_SIZE=PROPIC_MIN_SIZE)
|
|
|
|
app.ctx.tpl.globals.update(PROPIC_MAX_SIZE=PROPIC_MAX_SIZE)
|
|
|
|
app.ctx.tpl.globals.update(PROPIC_MAX_FILE_SIZE=sizeof_fmt(PROPIC_MAX_FILE_SIZE))
|
2022-12-18 16:40:39 +00:00
|
|
|
app.ctx.tpl.globals.update(int=int)
|
|
|
|
app.ctx.tpl.globals.update(len=len)
|
2023-01-08 10:48:01 +00:00
|
|
|
|
|
|
|
@app.route("/manage/barcode/<code>")
|
|
|
|
async def gen_barcode(request, code):
|
|
|
|
aa = AztecCode(code).image(module_size=8, border=2)
|
|
|
|
img = BytesIO()
|
|
|
|
aa.save(img, format='PNG')
|
|
|
|
|
|
|
|
return raw(img.getvalue(), content_type="image/png")
|
2023-01-17 21:25:35 +00:00
|
|
|
|
2024-05-19 19:07:31 +00:00
|
|
|
@app.route("/manage/lol")
|
|
|
|
async def lol(request: Request):
|
|
|
|
await get_quotas(request)
|
|
|
|
return text('hi')
|
|
|
|
|
2023-07-29 14:05:46 +00:00
|
|
|
@app.route(f"/{ORGANIZER}/{EVENT_NAME}/order/<code>/<secret>/open/<secret2>")
|
2022-12-18 16:40:39 +00:00
|
|
|
async def redirect_explore(request, code, secret, order: Order, secret2=None):
|
|
|
|
|
2023-05-11 22:18:49 +00:00
|
|
|
r = redirect(app.url_for("welcome"))
|
2022-12-18 16:40:39 +00:00
|
|
|
if order and order.code != code: order = None
|
|
|
|
|
|
|
|
if not order:
|
2024-02-22 18:15:33 +00:00
|
|
|
res = await pretixClient.get(f"orders/{code}/", expectedStatusCodes=None)
|
|
|
|
|
|
|
|
if res.status_code != 200:
|
|
|
|
raise exceptions.NotFound("This order code does not exist. Check that your order wasn't deleted, or the link is correct.")
|
|
|
|
|
|
|
|
res = res.json()
|
|
|
|
if secret != res['secret']:
|
|
|
|
raise exceptions.Forbidden("The secret part of the url is not correct. Check your E-Mail for the correct link, or contact support!")
|
|
|
|
r.cookies['foxo_code'] = code
|
|
|
|
r.cookies['foxo_secret'] = secret
|
2023-05-11 22:18:49 +00:00
|
|
|
return r
|
2022-12-18 16:40:39 +00:00
|
|
|
|
2023-02-02 21:47:33 +00:00
|
|
|
@app.route("/manage/privacy")
|
|
|
|
async def privacy(request):
|
|
|
|
tpl = app.ctx.tpl.get_template('privacy.html')
|
|
|
|
return html(tpl.render())
|
|
|
|
|
2022-12-18 16:40:39 +00:00
|
|
|
@app.route("/manage/welcome")
|
|
|
|
async def welcome(request, order: Order, quota: Quotas):
|
|
|
|
|
|
|
|
if not order:
|
|
|
|
raise exceptions.Forbidden("You have been logged out. Please access the link in your E-Mail to login again!")
|
2023-12-30 10:27:42 +00:00
|
|
|
|
|
|
|
if order.ans("propic_file") is None:
|
|
|
|
await resetDefaultPropic(request, order, False)
|
|
|
|
if order.ans("propic_fursuiter_file") is None:
|
|
|
|
await resetDefaultPropic(request, order, True)
|
2022-12-18 16:40:39 +00:00
|
|
|
|
|
|
|
pending_roommates = []
|
|
|
|
if order.pending_roommates:
|
|
|
|
for pr in order.pending_roommates:
|
|
|
|
if not pr: continue
|
2023-01-17 21:25:35 +00:00
|
|
|
pending_roommates.append(await app.ctx.om.get_order(code=pr, cached=True))
|
2022-12-18 16:40:39 +00:00
|
|
|
|
|
|
|
room_members = []
|
|
|
|
if order.room_id:
|
|
|
|
if order.room_id != order.code:
|
2023-01-17 21:25:35 +00:00
|
|
|
room_owner = await app.ctx.om.get_order(code=order.room_id, cached=True)
|
2022-12-18 16:40:39 +00:00
|
|
|
else:
|
|
|
|
room_owner = order
|
|
|
|
|
|
|
|
room_members.append(room_owner)
|
|
|
|
|
|
|
|
for member_id in room_owner.ans('room_members').split(','):
|
|
|
|
if member_id == room_owner.code: continue
|
|
|
|
if member_id == order.code:
|
|
|
|
room_members.append(order)
|
|
|
|
else:
|
2024-01-21 23:35:44 +00:00
|
|
|
room_members.append(await app.ctx.om.get_order(code=member_id, cached=True))
|
2024-01-19 17:03:54 +00:00
|
|
|
|
2022-12-18 16:40:39 +00:00
|
|
|
tpl = app.ctx.tpl.get_template('welcome.html')
|
2024-05-20 10:07:59 +00:00
|
|
|
return html(tpl.render(order=order, quota=quota, room_members=room_members, pending_roommates=pending_roommates, ROOM_ERROR_MESSAGES=ROOM_ERROR_TYPES, isSessionAdmin=await isSessionAdmin(request, order)))
|
2022-12-18 16:40:39 +00:00
|
|
|
|
|
|
|
|
|
|
|
@app.route("/manage/download_ticket")
|
2022-12-19 17:58:16 +00:00
|
|
|
async def download_ticket(request, order: Order):
|
2022-12-18 16:40:39 +00:00
|
|
|
|
|
|
|
if not order:
|
|
|
|
raise exceptions.Forbidden("You have been logged out. Please access the link in your E-Mail to login again!")
|
|
|
|
|
|
|
|
if not order.status != 'confirmed':
|
|
|
|
raise exceptions.Forbidden("You are not allowed to download this ticket.")
|
|
|
|
|
2024-02-22 18:15:33 +00:00
|
|
|
res = await pretixClient.get(f"orders/{order.code}/download/pdf/", expectedStatusCodes=[200, 404, 409, 403])
|
2022-12-19 17:58:16 +00:00
|
|
|
|
2024-02-22 18:15:33 +00:00
|
|
|
if res.status_code == 409 or res.status_code == 404:
|
2022-12-19 17:58:16 +00:00
|
|
|
raise exceptions.SanicException("Your ticket is still being generated. Please try again later!", status_code=res.status_code)
|
2023-01-08 10:48:01 +00:00
|
|
|
elif res.status_code == 403:
|
|
|
|
raise exceptions.SanicException("You can download your ticket only after the order has been confirmed and paid. Try later!", status_code=400)
|
2022-12-19 17:58:16 +00:00
|
|
|
|
|
|
|
return raw(res.content, content_type='application/pdf')
|
2024-01-08 21:02:27 +00:00
|
|
|
|
|
|
|
@app.route("/manage/admin")
|
|
|
|
async def admin(request, order: Order):
|
2024-01-21 23:35:44 +00:00
|
|
|
await request.app.ctx.om.update_cache()
|
2024-01-08 21:02:27 +00:00
|
|
|
if not order:
|
|
|
|
raise exceptions.Forbidden("You have been logged out. Please access the link in your E-Mail to login again!")
|
2024-01-13 15:59:13 +00:00
|
|
|
if EXTRA_PRINTS:
|
2024-01-21 23:35:44 +00:00
|
|
|
logger.info(f"Checking admin credentials of {order.code} with secret {order.secret}")
|
2024-01-08 21:02:27 +00:00
|
|
|
if not order.isAdmin(): raise exceptions.Forbidden("Birichino :)")
|
|
|
|
tpl = app.ctx.tpl.get_template('admin.html')
|
|
|
|
return html(tpl.render(order=order))
|
2023-01-08 10:48:01 +00:00
|
|
|
|
|
|
|
@app.route("/manage/logout")
|
2024-01-21 23:35:44 +00:00
|
|
|
async def logout(request):
|
2024-01-13 15:59:13 +00:00
|
|
|
orgCode = request.cookies.get("foxo_code_ORG")
|
|
|
|
orgSecret = request.cookies.get("foxo_secret_ORG")
|
|
|
|
if orgCode != None and orgSecret != None:
|
|
|
|
r = redirect(f'/manage/welcome')
|
|
|
|
r.cookies['foxo_code'] = orgCode
|
|
|
|
r.cookies['foxo_secret'] = orgSecret
|
|
|
|
r.delete_cookie("foxo_code_ORG")
|
|
|
|
r.delete_cookie("foxo_secret_ORG")
|
|
|
|
return r
|
|
|
|
|
2023-12-30 10:27:42 +00:00
|
|
|
raise exceptions.Forbidden("You have been logged out.")
|
2022-12-18 16:40:39 +00:00
|
|
|
|
2024-02-29 10:42:05 +00:00
|
|
|
@app.signal("server.shutdown.before")
|
|
|
|
async def sigintHandler(app, loop):
|
|
|
|
killSmptClient()
|
|
|
|
|
2024-02-14 19:22:56 +00:00
|
|
|
@app.get(METRICS_PATH)
|
|
|
|
async def metrics(request):
|
|
|
|
return text(getMetricsText() + "\n" + getRoomCountersText(request))
|
|
|
|
|
|
|
|
@app.on_request
|
|
|
|
async def countReqs(request : Request):
|
|
|
|
global METRICS_REQ_NO
|
|
|
|
if(request.path != METRICS_PATH):
|
|
|
|
incReqNo()
|
|
|
|
|
2022-12-18 16:40:39 +00:00
|
|
|
if __name__ == "__main__":
|
2024-02-13 12:29:03 +00:00
|
|
|
# Wait for pretix in server reboot
|
|
|
|
# Using a docker configuration, pretix may be unable to talk with postgres if postgres' service started before it.
|
|
|
|
# To fix this issue I added a After=pretix.service to the [Unit] section of /lib/systemd/system/postgresql@.service
|
|
|
|
# to let it start in the correct order. The following piece of code makes sure that pretix is running and can talk to
|
|
|
|
# postgres before actually starting the reserved area, since this operation requires a cache-fill in startup
|
|
|
|
print("Waiting for pretix to be up and running", file=sys.stderr)
|
2024-02-22 18:15:33 +00:00
|
|
|
while not SKIP_HEALTHCHECK:
|
2024-02-13 12:29:03 +00:00
|
|
|
print("Trying connecting to pretix...", file=sys.stderr)
|
|
|
|
try:
|
2024-02-14 19:22:56 +00:00
|
|
|
incPretixRead()
|
2024-02-13 12:29:03 +00:00
|
|
|
res = requests.get(base_url_event, headers=headers)
|
|
|
|
res = res.json()
|
|
|
|
if(res['slug'] == EVENT_NAME):
|
2024-02-13 13:17:21 +00:00
|
|
|
print("Healtchecking...", file=sys.stderr)
|
2024-02-14 19:22:56 +00:00
|
|
|
incPretixRead()
|
2024-02-13 13:17:21 +00:00
|
|
|
res = requests.get(join(domain, "healthcheck"), headers=headers)
|
2024-02-22 18:15:33 +00:00
|
|
|
if(res.status_code == 200):
|
2024-02-13 13:17:21 +00:00
|
|
|
break
|
2024-02-13 12:29:03 +00:00
|
|
|
except:
|
|
|
|
pass
|
|
|
|
sleep(5)
|
|
|
|
print("Connected to pretix!", file=sys.stderr)
|
2024-02-14 15:10:27 +00:00
|
|
|
|
2024-02-13 12:29:03 +00:00
|
|
|
app.run(host="127.0.0.1", port=8188, dev=DEV_MODE, access_log=ACCESS_LOG)
|