Compare commits

..

No commits in common. "1d7ea375c17e17845c0d9b87cd3fa32ca40c2d48" and "44b534b283a64447c888a2b378d89850cf6508d2" have entirely different histories.

12 changed files with 184 additions and 277 deletions

View File

@ -20,8 +20,7 @@ async def credentials_check(request: Request):
@bp.get('/cache/clear') @bp.get('/cache/clear')
async def clear_cache(request, order:Order): async def clear_cache(request, order:Order):
success = await request.app.ctx.om.fill_cache() await request.app.ctx.om.fill_cache()
if not success: raise exceptions.ServerError("An error occurred while loading the cache")
return redirect(f'/manage/admin') return redirect(f'/manage/admin')
@bp.get('/loginas/<code>') @bp.get('/loginas/<code>')
@ -41,8 +40,8 @@ async def login_as(request, code, order:Order):
@bp.get('/room/verify') @bp.get('/room/verify')
async def verify_rooms(request, order:Order): async def verify_rooms(request, order:Order):
already_checked, success = await request.app.ctx.om.update_cache() already_checked = await request.app.ctx.om.update_cache()
if not already_checked and success: if not already_checked:
orders = filter(lambda x: x.status not in ['c', 'e'] and x.room_id == x.code, request.app.ctx.om.cache.values()) orders = filter(lambda x: x.status not in ['c', 'e'] and x.room_id == x.code, request.app.ctx.om.cache.values())
await validate_rooms(request, orders, None) await validate_rooms(request, orders, None)
return redirect(f'/manage/admin') return redirect(f'/manage/admin')

67
app.py
View File

@ -16,8 +16,6 @@ import requests
import sys import sys
from sanic.log import logger, logging, access_logger from sanic.log import logger, logging, access_logger
from metrics import * from metrics import *
import pretixClient
import traceback
app = Sanic(__name__) app = Sanic(__name__)
app.static("/res", "res/") app.static("/res", "res/")
@ -36,28 +34,18 @@ from checkin import bp as checkin_bp
from admin import bp as admin_bp from admin import bp as admin_bp
app.blueprint([room_bp, karaoke_bp, propic_bp, export_bp, stats_bp, api_bp, carpooling_bp, checkin_bp, admin_bp]) app.blueprint([room_bp, karaoke_bp, propic_bp, export_bp, stats_bp, api_bp, carpooling_bp, checkin_bp, admin_bp])
@app.exception(exceptions.SanicException)
async def clear_session(response): async def clear_session(request, exception):
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()
logger.warning(f"{request} -> {exception}") logger.warning(f"{request} -> {exception}")
statusCode = exception.status_code if hasattr(exception, 'status_code') else 500 tpl = app.ctx.tpl.get_template('error.html')
try: r = html(tpl.render(exception=exception))
tpl = app.ctx.tpl.get_template('error.html')
r = html(tpl.render(exception=exception, status_code=statusCode)) if exception.status_code == 403:
except: r.delete_cookie("foxo_code")
traceback.print_exc() r.delete_cookie("foxo_secret")
if statusCode == 403:
clear_session(r)
return r return r
@app.before_server_start @app.before_server_start
async def main_start(*_): async def main_start(*_):
logger.info(f"[{app.name}] >>>>>> main_start <<<<<<") logger.info(f"[{app.name}] >>>>>> main_start <<<<<<")
@ -68,10 +56,7 @@ async def main_start(*_):
app.ctx.om = OrderManager() app.ctx.om = OrderManager()
if FILL_CACHE: if FILL_CACHE:
checked, success = await app.ctx.om.update_cache(check_itemsQuestions=True) await app.ctx.om.update_cache()
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()
app.ctx.nfc_counts = sqlite3.connect('data/nfc_counts.db') app.ctx.nfc_counts = sqlite3.connect('data/nfc_counts.db')
@ -105,16 +90,18 @@ async def redirect_explore(request, code, secret, order: Order, secret2=None):
if order and order.code != code: order = None if order and order.code != code: order = None
if not order: if not order:
res = await pretixClient.get(f"orders/{code}/", expectedStatusCodes=None) async with httpx.AsyncClient() as client:
incPretixRead()
if res.status_code != 200: res = await client.get(join(base_url_event, f"orders/{code}/"), headers=headers)
raise exceptions.NotFound("This order code does not exist. Check that your order wasn't deleted, or the link is correct.")
if res.status_code != 200:
res = res.json() raise exceptions.NotFound("This order code does not exist. Check that your order wasn't deleted, or the link is correct.")
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!") res = res.json()
r.cookies['foxo_code'] = code if secret != res['secret']:
r.cookies['foxo_secret'] = 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
return r return r
@app.route("/manage/privacy") @app.route("/manage/privacy")
@ -168,9 +155,11 @@ async def download_ticket(request, order: Order):
if not order.status != 'confirmed': if not order.status != 'confirmed':
raise exceptions.Forbidden("You are not allowed to download this ticket.") raise exceptions.Forbidden("You are not allowed to download this ticket.")
res = await pretixClient.get(f"orders/{order.code}/download/pdf/", expectedStatusCodes=[200, 404, 409, 403]) async with httpx.AsyncClient() as client:
incPretixRead()
res = await client.get(join(base_url_event, f"orders/{order.code}/download/pdf/"), headers=headers)
if res.status_code == 409 or res.status_code == 404: if res.status_code == 409:
raise exceptions.SanicException("Your ticket is still being generated. Please try again later!", status_code=res.status_code) raise exceptions.SanicException("Your ticket is still being generated. Please try again later!", status_code=res.status_code)
elif res.status_code == 403: 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) raise exceptions.SanicException("You can download your ticket only after the order has been confirmed and paid. Try later!", status_code=400)
@ -219,7 +208,7 @@ if __name__ == "__main__":
# to let it start in the correct order. The following piece of code makes sure that pretix is running and can talk to # 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 # 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) print("Waiting for pretix to be up and running", file=sys.stderr)
while not SKIP_HEALTHCHECK: while True:
print("Trying connecting to pretix...", file=sys.stderr) print("Trying connecting to pretix...", file=sys.stderr)
try: try:
incPretixRead() incPretixRead()
@ -229,7 +218,7 @@ if __name__ == "__main__":
print("Healtchecking...", file=sys.stderr) print("Healtchecking...", file=sys.stderr)
incPretixRead() incPretixRead()
res = requests.get(join(domain, "healthcheck"), headers=headers) res = requests.get(join(domain, "healthcheck"), headers=headers)
if(res.status_code == 200): if(res.status_code == 200 or SKIP_HEALTHCHECK):
break break
except: except:
pass pass

View File

@ -12,7 +12,6 @@ from time import time
from urllib.parse import unquote from urllib.parse import unquote
import json import json
from metrics import * from metrics import *
import pretixClient
bp = Blueprint("checkin", url_prefix="/checkin") bp = Blueprint("checkin", url_prefix="/checkin")
@ -65,7 +64,9 @@ async def do_checkin(request):
await order.send_answers() await order.send_answers()
if not order.checked_in: if not order.checked_in:
await pretixClient.post("", baseUrl=base_url_event.replace(f'events/{EVENT_NAME}/', 'checkinrpc/redeem/'), json={'secret': order.barcode, 'source_type': 'barcode', 'type': 'entry', 'lists': [3,]}) async with httpx.AsyncClient() as client:
incPretixWrite()
res = await client.post(base_url_event.replace(f'events/{EVENT_NAME}/', 'checkinrpc/redeem/'), json={'secret': order.barcode, 'source_type': 'barcode', 'type': 'entry', 'lists': [3,]}, headers=headers)
tpl = request.app.ctx.tpl.get_template('checkin_3.html') tpl = request.app.ctx.tpl.get_template('checkin_3.html')
return html(tpl.render(order=order, room_owner=room_owner, roommates=roommates)) return html(tpl.render(order=order, room_owner=room_owner, roommates=roommates))

View File

@ -21,11 +21,6 @@ PROPIC_MIN_SIZE = (125, 125) # (Width, Height)
TG_BOT_API = '123456789:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' TG_BOT_API = '123456789:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
TG_CHAT_ID = -1234567 TG_CHAT_ID = -1234567
import httpx
# Number of tries for a request to the pretix's backend
PRETIX_REQUESTS_MAX = 3
PRETIX_REQUESTS_TIMEOUT = httpx.Timeout(15.0, read=30.0, connect=45.0, pool=None) # Timeout for httpx requests in seconds
# These order codes have additional functions. # These order codes have additional functions.
ADMINS = ['XXXXX', 'YYYYY'] ADMINS = ['XXXXX', 'YYYYY']
# A list of staff_roles # A list of staff_roles

229
ext.py
View File

@ -10,9 +10,6 @@ from sanic.log import logger
from time import time from time import time
from metrics import * from metrics import *
import asyncio import asyncio
from threading import Lock
import pretixClient
import traceback
@dataclass @dataclass
class Order: class Order:
@ -159,12 +156,14 @@ class Order:
async def edit_answer_fileUpload(self, name, fileName, mimeType, data : bytes): async def edit_answer_fileUpload(self, name, fileName, mimeType, data : bytes):
if(mimeType != None and data != None): if(mimeType != None and data != None):
localHeaders = dict(headers) async with httpx.AsyncClient() as client:
localHeaders['Content-Type'] = mimeType localHeaders = dict(headers)
localHeaders['Content-Disposition'] = f'attachment; filename="{fileName}"' localHeaders['Content-Type'] = mimeType
res = await pretixClient.post("upload", baseUrl=base_url, headers=localHeaders, content=data) localHeaders['Content-Disposition'] = f'attachment; filename="{fileName}"'
res = res.json() incPretixWrite()
await self.edit_answer(name, res['id']) res = await client.post(join(base_url, 'upload'), headers=localHeaders, content=data)
res = res.json()
await self.edit_answer(name, res['id'])
else: else:
await self.edit_answer(name, None) await self.edit_answer(name, None)
self.loadAns() self.loadAns()
@ -185,8 +184,11 @@ class Order:
break break
if (not found) and (new_answer is not None): if (not found) and (new_answer is not None):
res = await pretixClient.get("questions/")
res = res.json() async with httpx.AsyncClient() as client:
incPretixRead()
res = await client.get(join(base_url_event, 'questions/'), headers=headers)
res = res.json()
for r in res['results']: for r in res['results']:
if r['identifier'] != name: continue if r['identifier'] != name: continue
@ -199,30 +201,32 @@ class Order:
self.loadAns() self.loadAns()
async def send_answers(self): async def send_answers(self):
if DEV_MODE and EXTRA_PRINTS: logger.debug("[ANSWER POST] POSITION ID IS %s", self.position_id) async with httpx.AsyncClient() as client:
if DEV_MODE and EXTRA_PRINTS: logger.debug("[ANSWER POST] POSITION ID IS %s", self.position_id)
for i, ans in enumerate(self.answers):
if TYPE_OF_QUESTIONS[ans['question']] == QUESTION_TYPES["multiple_choice_from_list"]: # if multiple choice for i, ans in enumerate(self.answers):
identifier = ans['question_identifier'] if TYPE_OF_QUESTIONS[ans['question']] == QUESTION_TYPES["multiple_choice_from_list"]: # if multiple choice
if self.ans(identifier) == "": #if empty answer identifier = ans['question_identifier']
await self.edit_answer(identifier, None) if self.ans(identifier) == "": #if empty answer
# Fix for karaoke fields await self.edit_answer(identifier, None)
#if ans['question'] == 40: # Fix for karaoke fields
# del self.answers[i]['options'] #if ans['question'] == 40:
# del self.answers[i]['option_identifiers'] # del self.answers[i]['options']
# del self.answers[i]['option_identifiers']
res = await pretixClient.patch(f'orderpositions/{self.position_id}/', json={'answers': self.answers}, expectedStatusCodes=None)
incPretixWrite()
if res.status_code != 200: res = await client.patch(join(base_url_event, f'orderpositions/{self.position_id}/'), headers=headers, json={'answers': self.answers})
for ans, err in zip(self.answers, res.json()['answers']):
if err: if res.status_code != 200:
logger.error ('[ANSWERS SENDING] ERROR ON %s %s', ans, err) for ans, err in zip(self.answers, res.json()['answers']):
if err:
logger.error ('[ANSWERS SENDING] ERROR ON %s %s', ans, err)
raise exceptions.ServerError('There has been an error while updating this answers.') raise exceptions.ServerError('There has been an error while updating this answers.')
for i, ans in enumerate(self.answers): for i, ans in enumerate(self.answers):
if(TYPE_OF_QUESTIONS[self.answers[i]['question']] == QUESTION_TYPES['file_upload']): if(TYPE_OF_QUESTIONS[self.answers[i]['question']] == QUESTION_TYPES['file_upload']):
self.answers[i]['answer'] = "file:keep" self.answers[i]['answer'] = "file:keep"
self.pending_update = False self.pending_update = False
self.time = -1 self.time = -1
@ -243,10 +247,12 @@ class Quotas:
return 0 return 0
async def get_quotas(request: Request=None): async def get_quotas(request: Request=None):
res = await pretixClient.get('quotas/?order=id&with_availability=true') async with httpx.AsyncClient() as client:
res = res.json() incPretixRead()
res = await client.get(join(base_url_event, 'quotas/?order=id&with_availability=true'), headers=headers)
return Quotas(res) res = res.json()
return Quotas(res)
async def get_order(request: Request=None): async def get_order(request: Request=None):
await request.receive_body() await request.receive_body()
@ -255,7 +261,7 @@ async def get_order(request: Request=None):
class OrderManager: class OrderManager:
def __init__(self): def __init__(self):
self.lastCacheUpdate = 0 self.lastCacheUpdate = 0
self.updating : Lock = Lock() self.updating = False
self.empty() self.empty()
def empty(self): def empty(self):
@ -263,89 +269,64 @@ class OrderManager:
self.order_list = [] self.order_list = []
# Will fill cache once the last cache update is greater than cache expire time # Will fill cache once the last cache update is greater than cache expire time
async def update_cache(self, check_itemsQuestions=False): async def update_cache(self):
t = time() t = time()
to_return = False to_return = False
success = True if(t - self.lastCacheUpdate > CACHE_EXPIRE_TIME and not self.updating):
if(t - self.lastCacheUpdate > CACHE_EXPIRE_TIME and not self.updating.locked()):
to_return = True to_return = True
success = await self.fill_cache(check_itemsQuestions=check_itemsQuestions) await self.fill_cache()
return (to_return, success) return to_return
def add_cache(self, order, cache=None, orderList=None): def add_cache(self, order):
# Extra params for dry runs self.cache[order.code] = order
if(cache is None): if not order.code in self.order_list:
cache = self.cache self.order_list.append(order.code)
if(orderList is None):
orderList = self.order_list
cache[order.code] = order def remove_cache(self, code):
if not order.code in orderList: if code in self.cache:
orderList.append(order.code) del self.cache[code]
self.order_list.remove(code)
def remove_cache(self, code, cache=None, orderList=None):
# Extra params for dry runs
if(cache is None):
cache = self.cache
if(orderList is None):
orderList = self.order_list
if code in cache:
del cache[code]
orderList.remove(code)
async def fill_cache(self, check_itemsQuestions=False) -> bool: async def fill_cache(self):
# Check cache lock # Check cache lock
self.updating.acquire() if self.updating == True: return
# Set cache lock
self.updating = True
start_time = time() start_time = time()
logger.info("[CACHE] Filling cache...") logger.info("[CACHE] Filling cache...")
# Index item's ids # Index item's ids
r = await load_items() await load_items()
if(not r and check_itemsQuestions):
logger.error("[CACHE] Items were not loading correctly. Aborting filling cache...")
return False
# Index questions' types # Index questions' types
r = await load_questions() await load_questions()
if(not r and check_itemsQuestions):
logger.error("[CACHE] Questions were not loading correctly. Aborting filling cache...")
return False
cache = {} # Clear cache data completely
orderList = [] self.empty()
success = True
p = 0 p = 0
try: try:
while 1: async with httpx.AsyncClient() as client:
p += 1 while 1:
res = await pretixClient.get(f"orders/?page={p}", expectedStatusCodes=[200, 404]) p += 1
if res.status_code == 404: break incPretixRead()
# Parse order data res = await client.get(join(base_url_event, f"orders/?page={p}"), headers=headers)
data = res.json() if res.status_code == 404: break
for o in data['results']: # Parse order data
o = Order(o) data = res.json()
if o.status in ['canceled', 'expired']: for o in data['results']:
self.remove_cache(o.code, cache=cache, orderList=orderList) o = Order(o)
else: if o.status in ['canceled', 'expired']:
self.add_cache(Order(o), cache=cache, orderList=orderList) self.remove_cache(o.code)
self.lastCacheUpdate = time() else:
logger.info(f"[CACHE] Cache filled in {self.lastCacheUpdate - start_time}s.") self.add_cache(Order(o))
except Exception: self.lastCacheUpdate = time()
logger.error(f"[CACHE] Error while refreshing cache.\n{traceback.format_exc()}") logger.info(f"[CACHE] Cache filled in {self.lastCacheUpdate - start_time}s.")
success = False except Exception as ex:
logger.error("[CACHE] Error while refreshing cache.", ex)
finally: finally:
self.updating.release() self.updating = False
# Apply new cache if there were no errors
if(success):
self.cache = cache
self.order_list = orderList
# Validating rooms # Validating rooms
rooms = list(filter(lambda o: (o.code == o.room_id), self.cache.values())) rooms = list(filter(lambda o: (o.code == o.room_id), self.cache.values()))
asyncio.create_task(validate_rooms(None, rooms, self)) asyncio.create_task(validate_rooms(None, rooms, self))
return success
async def get_order(self, request=None, code=None, secret=None, nfc_id=None, cached=False): async def get_order(self, request=None, code=None, secret=None, nfc_id=None, cached=False):
@ -368,24 +349,26 @@ class OrderManager:
if re.match('^[A-Z0-9]{5}$', code or '') and (secret is None or re.match('^[a-z0-9]{16,}$', secret)): if re.match('^[A-Z0-9]{5}$', code or '') and (secret is None or re.match('^[a-z0-9]{16,}$', secret)):
if DEV_MODE and EXTRA_PRINTS: logger.debug(f'Fetching {code} with secret {secret}') if DEV_MODE and EXTRA_PRINTS: logger.debug(f'Fetching {code} with secret {secret}')
res = await pretixClient.get(f"orders/{code}/", expectedStatusCodes=None) async with httpx.AsyncClient() as client:
if res.status_code != 200: incPretixRead()
if request: res = await client.get(join(base_url_event, f"orders/{code}/"), headers=headers)
raise exceptions.Forbidden("Your session has expired due to order deletion or change! Please check your E-Mail for more info.") if res.status_code != 200:
else: if request:
self.remove_cache(code) raise exceptions.Forbidden("Your session has expired due to order deletion or change! Please check your E-Mail for more info.")
return None else:
self.remove_cache(code)
return None
res = res.json() res = res.json()
order = Order(res) order = Order(res)
if order.status in ['canceled', 'expired']: if order.status in ['canceled', 'expired']:
self.remove_cache(order.code) self.remove_cache(order.code)
if request: if request:
raise exceptions.Forbidden(f"Your order has been deleted. Contact support with your order identifier ({res['code']}) for further info.") raise exceptions.Forbidden(f"Your order has been deleted. Contact support with your order identifier ({res['code']}) for further info.")
else: else:
self.add_cache(order) self.add_cache(order)
if request and secret != res['secret']: if request and secret != res['secret']:
raise exceptions.Forbidden("Your session has expired due to a token change. Please check your E-Mail for an updated link!") raise exceptions.Forbidden("Your session has expired due to a token change. Please check your E-Mail for an updated link!")
return order return order

View File

@ -3,10 +3,8 @@ from logging import LogRecord
from config import * from config import *
METRICS_REQ_NO = 0 METRICS_REQ_NO = 0
METRICS_ERR_NO = 0 # Errors served to the clients
METRICS_PRETIX_READ = 0 METRICS_PRETIX_READ = 0
METRICS_PRETIX_WRITE = 0 METRICS_PRETIX_WRITE = 0
METRICS_PRETIX_ERRORS = 0 # Errors requesting pretix's backend
def incPretixRead(): def incPretixRead():
global METRICS_PRETIX_READ global METRICS_PRETIX_READ
@ -16,32 +14,19 @@ def incPretixWrite():
global METRICS_PRETIX_WRITE global METRICS_PRETIX_WRITE
METRICS_PRETIX_WRITE += 1 METRICS_PRETIX_WRITE += 1
def incPretixErrors():
global METRICS_PRETIX_ERRORS
METRICS_PRETIX_ERRORS += 1
def incReqNo(): def incReqNo():
global METRICS_REQ_NO global METRICS_REQ_NO
METRICS_REQ_NO += 1 METRICS_REQ_NO += 1
def incErrorNo(): # Errors served to the clients
global METRICS_ERR_NO
METRICS_ERR_NO += 1
def getMetricsText(): def getMetricsText():
global METRICS_REQ_NO global METRICS_REQ_NO
global METRICS_ERR_NO
global METRICS_PRETIX_READ global METRICS_PRETIX_READ
global METRICS_PRETIX_WRITE global METRICS_PRETIX_WRITE
global METRICS_PRETIX_ERRORS
out = [] out = []
out.append(f'sanic_request_count{{}} {METRICS_REQ_NO}') out.append(f'sanic_request_count{{}} {METRICS_REQ_NO}')
out.append(f'sanic_error_count{{}} {METRICS_ERR_NO}')
out.append(f'webint_pretix_read_count{{}} {METRICS_PRETIX_READ}') out.append(f'webint_pretix_read_count{{}} {METRICS_PRETIX_READ}')
out.append(f'webint_pretix_write_count{{}} {METRICS_PRETIX_WRITE}') out.append(f'webint_pretix_write_count{{}} {METRICS_PRETIX_WRITE}')
out.append(f'webint_pretix_error_count{{}} {METRICS_PRETIX_ERRORS}')
return "\n".join(out) return "\n".join(out)

View File

@ -1,49 +0,0 @@
import httpx
from utils import *
from config import *
from sanic.log import logger
from metrics import *
import traceback
import asyncio
async def get(url, baseUrl=base_url_event, headers=headers, expectedStatusCodes=[200]) -> httpx.Response:
async def func(client : httpx.AsyncClient) -> httpx.Request:
return await client.get(join(baseUrl, url), headers=headers)
return await doReq(url, func, incPretixRead, expectedStatusCodes, "GETing")
async def post(url, content=None, json=None, baseUrl=base_url_event, headers=headers, expectedStatusCodes=[200]) -> httpx.Response:
async def func(client : httpx.AsyncClient) -> httpx.Request:
return await client.post(join(baseUrl, url), headers=headers, content=content, json=json)
return await doReq(url, func, incPretixWrite, expectedStatusCodes, "POSTing")
async def patch(url, json, baseUrl=base_url_event, headers=headers, expectedStatusCodes=[200]) -> httpx.Response:
async def func(client : httpx.AsyncClient) -> httpx.Request:
return await client.patch(join(baseUrl, url), headers=headers, json=json)
return await doReq(url, func, incPretixWrite, expectedStatusCodes, "PATCHing")
async def doReq(url, httpxFunc, metricsFunc, expectedStatusCodes, opLogString) -> httpx.Response:
res = None
async with httpx.AsyncClient(timeout=PRETIX_REQUESTS_TIMEOUT) as client:
requests = 0
for requests in range(PRETIX_REQUESTS_MAX):
try:
metricsFunc()
res = await httpxFunc(client)
if expectedStatusCodes is not None and res.status_code not in expectedStatusCodes:
incPretixErrors()
logger.warning(f"[PRETIX] Got an unexpected status code ({res.status_code}) while {opLogString} '{url}'. Allowed status codes: {', '.join(expectedStatusCodes)}")
continue
break
except Exception as e:
incPretixErrors()
logger.warning(f"[PRETIX] An error ({requests}) occurred while {opLogString} '{url}':\n{traceback.format_exc()}")
requests += 1
else:
logger.error(f"[PRETIX] Reached PRETIX_REQUESTS_MAX ({PRETIX_REQUESTS_MAX}) while {opLogString} '{url}'. Aborting")
raise httpx.TimeoutException(f"PRETIX_REQUESTS_MAX reached while {opLogString} to pretix.")
return res

View File

@ -12,21 +12,6 @@ summary:has(span.status) {
background-color: #ffaf0377; background-color: #ffaf0377;
} }
.rainbow-text {
background-image: repeating-linear-gradient(90deg, #ff0000, #ffff00, #00ff00, #00ffff, #0000ff, #ff00ff, #ff0000, #ffff00, #00ff00, #00ffff, #0000ff, #ff00ff);
background-size: 2000% 2000%;
color: transparent;
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
animation: rainbow 4s linear infinite;
}
@keyframes rainbow {
0% { background-position:0% 0%; }
100% { background-position:57.75% 0%; }
}
/* Dark theme */ /* Dark theme */
@media only screen and (prefers-color-scheme: dark) { @media only screen and (prefers-color-scheme: dark) {
.icon {filter: invert(1);} .icon {filter: invert(1);}

View File

@ -14,7 +14,6 @@ nav#topbar {
top: 0rem; top: 0rem;
transition: top 300ms; transition: top 300ms;
line-height: 2em; line-height: 2em;
max-width:98vw;
} }
nav#topbar a { nav#topbar a {
@ -43,6 +42,21 @@ nav img {
box-sizing: border-box; box-sizing: border-box;
} }
a.rainbow-text {
background-image: repeating-linear-gradient(90deg, #ff0000, #ffff00, #00ff00, #00ffff, #0000ff, #ff00ff, #ff0000, #ffff00, #00ff00, #00ffff, #0000ff, #ff00ff);
background-size: 2000% 2000%;
color: transparent;
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
animation: rainbow 4s linear infinite;
}
@keyframes rainbow {
0% { background-position:0% 0%; }
100% { background-position:57.75% 0%; }
}
nav a#mobileMenu { nav a#mobileMenu {
display: none; display: none;
} }

15
room.py
View File

@ -335,6 +335,21 @@ async def confirm_room(request, order: Order, quotas: Quotas):
await rm.edit_answer('pending_roommates', None) await rm.edit_answer('pending_roommates', None)
await rm.edit_answer('pending_room', None) await rm.edit_answer('pending_room', None)
# This should now be useless because in the ticket there already is the ticket/room type
# thing = {
# 'order': order.code,
# 'addon_to': order.position_positionid,
# 'item': ITEM_IDS['room'],
# 'variation': ROOM_MAP[len(room_members)]
# }
#
# async with httpx.AsyncClient() as client:
# incPretixRead()
# res = await client.post(join(base_url_event, "orderpositions/"), headers=headers, json=thing)
#
# if res.status_code != 201:
# raise exceptions.BadRequest("Something has gone wrong! Please contact support immediately")
for rm in room_members: for rm in room_members:
await rm.send_answers() await rm.send_answers()

View File

@ -1,10 +1,10 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block title %}Error {{status_code}}{% endblock %} {% block title %}Error {{exception.status_code}}{% endblock %}
{% block main %} {% block main %}
<main class="container"> <main class="container">
<h1>{{status_code}}</h1> <h1>{{exception.status_code}}</h1>
<p>{{exception}}</p> <p>{{exception}}</p>
{% if status_code == 409 %} {% if exception.status_code == 409 %}
<p>Retrying in 1 second...</p> <p>Retrying in 1 second...</p>
<meta http-equiv="refresh" content="1"> <meta http-equiv="refresh" content="1">
{% endif %} {% endif %}

View File

@ -7,8 +7,6 @@ from email_util import send_unconfirm_message
from sanic.response import text, html, redirect, raw from sanic.response import text, html, redirect, raw
from sanic.log import logger from sanic.log import logger
from metrics import * from metrics import *
import pretixClient
import traceback
METADATA_TAG = "meta_data" METADATA_TAG = "meta_data"
VARIATIONS_TAG = "variations" VARIATIONS_TAG = "variations"
@ -30,38 +28,34 @@ QUESTION_TYPES = { #https://docs.pretix.eu/en/latest/api/resources/questions.htm
TYPE_OF_QUESTIONS = {} # maps questionId -> type TYPE_OF_QUESTIONS = {} # maps questionId -> type
async def load_questions() -> bool: async def load_questions():
global TYPE_OF_QUESTIONS global TYPE_OF_QUESTIONS
# TYPE_OF_QUESTIONS.clear() It should not be needed TYPE_OF_QUESTIONS.clear()
logger.info("[QUESTIONS] Loading questions...") async with httpx.AsyncClient() as client:
success = True
try:
p = 0 p = 0
while 1: while 1:
p += 1 p += 1
res = await pretixClient.get(f"questions/?page={p}", expectedStatusCodes=[200, 404]) incPretixRead()
res = await client.get(join(base_url_event, f"questions/?page={p}"), headers=headers)
if res.status_code == 404: break if res.status_code == 404: break
data = res.json() data = res.json()
for q in data['results']: for q in data['results']:
TYPE_OF_QUESTIONS[q['id']] = q['type'] TYPE_OF_QUESTIONS[q['id']] = q['type']
except Exception:
logger.warning(f"[QUESTIONS] Error while loading questions.\n{traceback.format_exc()}")
success = False
return success
async def load_items() -> bool: async def load_items():
global ITEMS_ID_MAP global ITEMS_ID_MAP
global ITEM_VARIATIONS_MAP global ITEM_VARIATIONS_MAP
global CATEGORIES_LIST_MAP global CATEGORIES_LIST_MAP
global ROOM_TYPE_NAMES global ROOM_TYPE_NAMES
logger.info("[ITEMS] Loading items...") async with httpx.AsyncClient() as client:
success = True
try:
p = 0 p = 0
while 1: while 1:
p += 1 p += 1
res = await pretixClient.get(f"items/?page={p}", expectedStatusCodes=[200, 404]) incPretixRead()
res = await client.get(join(base_url_event, f"items/?page={p}"), headers=headers)
if res.status_code == 404: break if res.status_code == 404: break
data = res.json() data = res.json()
@ -91,10 +85,6 @@ async def load_items() -> bool:
logger.debug(f'Mapped Variations: %s', ITEM_VARIATIONS_MAP) logger.debug(f'Mapped Variations: %s', ITEM_VARIATIONS_MAP)
logger.debug(f'Mapped categories: %s', CATEGORIES_LIST_MAP) logger.debug(f'Mapped categories: %s', CATEGORIES_LIST_MAP)
logger.debug(f'Mapped Rooms: %s', ROOM_TYPE_NAMES) logger.debug(f'Mapped Rooms: %s', ROOM_TYPE_NAMES)
except Exception:
logger.warning(f"[ITEMS] Error while loading items.\n{traceback.format_exc()}")
success = False
return success
# Tries to get an item name from metadata. Prints a warning if an item has no metadata # Tries to get an item name from metadata. Prints a warning if an item has no metadata
def check_and_get_name(type, q): def check_and_get_name(type, q):