stranck-dev #31

Open
drew wants to merge 13 commits from stranck-dev into drew-dev
15 changed files with 198 additions and 74 deletions

View File

@ -62,13 +62,14 @@ async def unconfirm_room(request, code, order:Order):
return redirect(f'/manage/nosecount') return redirect(f'/manage/nosecount')
@bp.get('/room/autoconfirm') @bp.get('/room/autoconfirm')
async def autoconfirm_room(request, code, order:Order): async def autoconfirm_room(request, order:Order):
await clear_cache(request, order) await clear_cache(request, order)
orders = request.app.ctx.om.cache.values() orders = request.app.ctx.om.cache.values()
for order in orders: for order in orders:
if(order.code == order.room_id and not order.room_confirmed and len(order.room_members) == order.room_person_no): if(order.code == order.room_id and not order.room_confirmed and len(order.room_members) == order.room_person_no):
logger.info(f"Auto-Confirming room {order.room_id}") logger.info(f"Auto-Confirming room {order.room_id}")
await confirm_room_by_order(order, request) await confirm_room_by_order(order, request)
await clear_cache(request, order)
return redirect(f'/manage/admin') return redirect(f'/manage/admin')
@bp.get('/room/delete/<code>') @bp.get('/room/delete/<code>')
@ -111,12 +112,12 @@ async def room_wizard(request, order:Order):
await clear_cache(request, order) await clear_cache(request, order)
#Separate orders which have incomplete rooms and which have no rooms #Separate orders which have incomplete rooms and which have no rooms
all_orders = {key:value for key,value in sorted(request.app.ctx.om.cache.items(), key=lambda x: len(x[1].room_members), reverse=True) if value.status not in ['c', 'e'] and not value.daily} all_orders = {key:value for key,value in sorted(request.app.ctx.om.cache.items(), key=lambda x: (x[1].room_person_no, len(x[1].room_members)), reverse=True) if (value.status not in ['canceled', 'expired'] and not value.daily and value.bed_in_room != ITEM_VARIATIONS_MAP["bed_in_room"]["bed_in_room_no_room"])}
orders = {key:value for key,value in sorted(all_orders.items(), key=lambda x: x[1].ans('fursona_name')) if not value.room_confirmed} orders = {key:value for key,value in sorted(all_orders.items(), key=lambda x: x[1].ans('fursona_name')) if not value.room_confirmed}
# Orders with incomplete rooms # Orders with incomplete rooms
incomplete_orders = {key:value for key,value in orders.items() if value.code == value.room_id and (value.room_person_no - len(value.room_members)) > 0} incomplete_orders = {key:value for key,value in orders.items() if value.code == value.room_id and (value.room_person_no - len(value.room_members)) > 0}
# Roomless furs # Roomless furs
roomless_orders = {key:value for key,value in orders.items() if not value.room_id and not value.daily} roomless_orders = {key:value for key,value in orders.items() if(not value.room_id and not value.daily and value.bed_in_room != ITEM_VARIATIONS_MAP["bed_in_room"]["bed_in_room_no_room"])}
# Result map # Result map
result_map = {} result_map = {}
@ -124,10 +125,13 @@ async def room_wizard(request, order:Order):
# Check overflows # Check overflows
room_quota_overflow = {} room_quota_overflow = {}
for key, value in ITEM_VARIATIONS_MAP['bed_in_room'].items(): for key, value in ITEM_VARIATIONS_MAP['bed_in_room'].items():
if key != "bed_in_room_no_room":
room_quota = get_quota(ITEMS_ID_MAP['bed_in_room'], value) room_quota = get_quota(ITEMS_ID_MAP['bed_in_room'], value)
capacity = ROOM_CAPACITY_MAP[key] if key in ROOM_CAPACITY_MAP else 1 capacity = ROOM_CAPACITY_MAP[key] if key in ROOM_CAPACITY_MAP else 1
current_quota = len(list(filter(lambda y: y.bed_in_room == value and y.room_owner == True, orders.values()))) current_quota = len(list(filter(lambda y: y.bed_in_room == value and y.room_owner == True, all_orders.values())))
room_quota_overflow[value] = current_quota - int(room_quota.size / capacity) if room_quota else 0 room_quota_overflow[value] = current_quota - int(room_quota.size / capacity) if room_quota else 0
if DEV_MODE and EXTRA_PRINTS:
print(f"There are {current_quota} of room type {key} out of a total of ({room_quota.size} / {capacity})")
# Init rooms to remove # Init rooms to remove
result_map["void"] = [] result_map["void"] = []
@ -135,6 +139,7 @@ async def room_wizard(request, order:Order):
# Remove rooms that are over quota # Remove rooms that are over quota
for room_type, overflow_qty in {key:value for key,value in room_quota_overflow.items() if value > 0}.items(): for room_type, overflow_qty in {key:value for key,value in room_quota_overflow.items() if value > 0}.items():
sorted_rooms = sorted(incomplete_orders.values(), key=lambda r: len(r.room_members)) sorted_rooms = sorted(incomplete_orders.values(), key=lambda r: len(r.room_members))
sorted_rooms = [r for r in sorted_rooms if r.bed_in_room == room_type]
for room_to_remove in sorted_rooms[:overflow_qty]: for room_to_remove in sorted_rooms[:overflow_qty]:
# Room codes to remove # Room codes to remove
result_map["void"].append(room_to_remove.code) result_map["void"].append(room_to_remove.code)
@ -147,8 +152,10 @@ async def room_wizard(request, order:Order):
for room_order in incomplete_orders.items(): for room_order in incomplete_orders.items():
room = room_order[1] room = room_order[1]
to_add = [] to_add = []
missing_slots = room.room_person_no - len(room.room_members) count = room.room_person_no
for i in range(missing_slots): alreadyPresent = len(room.room_members)
missing_slots = count - alreadyPresent
for _ in range(missing_slots):
compatible_roomates = {key:value for key,value in roomless_orders.items() if value.bed_in_room == room.bed_in_room} compatible_roomates = {key:value for key,value in roomless_orders.items() if value.bed_in_room == room.bed_in_room}
if len(compatible_roomates.items()) == 0: break if len(compatible_roomates.items()) == 0: break
# Try picking a roomate that's from the same country and room type # Try picking a roomate that's from the same country and room type
@ -165,7 +172,9 @@ async def room_wizard(request, order:Order):
del roomless_orders[code_to_add] del roomless_orders[code_to_add]
result_map[room.code] = { result_map[room.code] = {
'type': 'add_existing', 'type': 'add_existing',
'to_add': to_add 'to_add': to_add,
'count': count,
'previouslyPresent': alreadyPresent
} }
generated_counter = 0 generated_counter = 0
@ -173,8 +182,10 @@ async def room_wizard(request, order:Order):
while len(roomless_orders.items()) > 0: while len(roomless_orders.items()) > 0:
room = list(roomless_orders.items())[0][1] room = list(roomless_orders.items())[0][1]
to_add = [] to_add = []
missing_slots = room.room_person_no - len(room.room_members) count = room.room_person_no
for i in range(missing_slots): alreadyPresent = len(room.room_members)
missing_slots = count - alreadyPresent
for _ in range(missing_slots):
compatible_roomates = {key:value for key,value in roomless_orders.items() if value.bed_in_room == room.bed_in_room} compatible_roomates = {key:value for key,value in roomless_orders.items() if value.bed_in_room == room.bed_in_room}
if len(compatible_roomates.items()) == 0: break if len(compatible_roomates.items()) == 0: break
# Try picking a roomate that's from the same country and room type # Try picking a roomate that's from the same country and room type
@ -194,17 +205,20 @@ async def room_wizard(request, order:Order):
'type': 'new', 'type': 'new',
'room_name': f'Generated Room {generated_counter}', 'room_name': f'Generated Room {generated_counter}',
'room_type': room.bed_in_room, 'room_type': room.bed_in_room,
'to_add': to_add 'to_add': to_add,
'count': count,
'previouslyPresent': alreadyPresent
} }
result_map["infinite"] = { 'to_add': [] } result_map["infinite"] = { 'to_add': [] }
result_map = {k: v for k, v in sorted(result_map.items(), key=lambda x: ((x[1]["count"], x[1]["previouslyPresent"]) if("count" in x[1] and "previouslyPresent" in x[1]) else (4316, 0) ))}
tpl = request.app.ctx.tpl.get_template('wizard.html') tpl = request.app.ctx.tpl.get_template('wizard.html')
return html(tpl.render(order=order, all_orders=all_orders, unconfirmed_orders=orders, data=result_map, jsondata=json.dumps(result_map, skipkeys=True, ensure_ascii=False))) return html(tpl.render(order=order, all_orders=all_orders, unconfirmed_orders=orders, data=result_map, jsondata=json.dumps(result_map, skipkeys=True, ensure_ascii=False)))
@bp.post('/room/wizard/submit') @bp.post('/room/wizard/submit')
async def submit_from_room_wizard(request:Request, order:Order): async def submit_from_room_wizard(request:Request, order:Order):
'''Will apply changes to the rooms''' '''Will apply changes to the rooms'''
await request.app.ctx.om.fill_cache() await clear_cache(request, order)
data = json.loads(request.body) data = json.loads(request.body)
@ -253,7 +267,7 @@ async def submit_from_room_wizard(request:Request, order:Order):
pending_member = await request.app.ctx.om.get_order(code=new_member_code) pending_member = await request.app.ctx.om.get_order(code=new_member_code)
# Preconditions # Preconditions
if pending_member.daily == True: raise exceptions.BadRequest(f"Order {pending_member.code} is daily.") if pending_member.daily == True: raise exceptions.BadRequest(f"Order {pending_member.code} is daily.")
if pending_member.status != 'paid': raise exceptions.BadRequest(f"Order {new_member_code} hasn't paid.") #if pending_member.status != 'paid': raise exceptions.BadRequest(f"Order {new_member_code} hasn't paid.") # Since we don't confirm rooms anymore, we don't need to check if they're paid or not
if pending_member.bed_in_room != room_order.bed_in_room: raise exceptions.BadRequest(f"Order {new_member_code} has a different room type than {room_code}.") if pending_member.bed_in_room != room_order.bed_in_room: raise exceptions.BadRequest(f"Order {new_member_code} has a different room type than {room_code}.")
if pending_member.room_owner: exceptions.BadRequest(f"Order {new_member_code} is already a room owner.") if pending_member.room_owner: exceptions.BadRequest(f"Order {new_member_code} is already a room owner.")
if pending_member.room_id and pending_member.room_id not in data['void']: exceptions.BadRequest(f"Order {new_member_code} is in another room.") if pending_member.room_id and pending_member.room_id not in data['void']: exceptions.BadRequest(f"Order {new_member_code} is in another room.")
@ -270,10 +284,10 @@ async def submit_from_room_wizard(request:Request, order:Order):
else: raise exceptions.BadRequest(f"Unexpected type ({value['type']})") else: raise exceptions.BadRequest(f"Unexpected type ({value['type']})")
await room_order.edit_answer('pending_room', None) await room_order.edit_answer('pending_room', None)
await room_order.edit_answer('pending_roommates', None) await room_order.edit_answer('pending_roommates', None)
await room_order.edit_answer('room_confirmed', "True") # await room_order.edit_answer('room_confirmed', "True") Use the autoconfirm button in the admin panel
await room_order.edit_answer('room_members', ','.join(list(set([*room_order.room_members, room_order.code, *value['to_add']])))) await room_order.edit_answer('room_members', ','.join(list(set([*room_order.room_members, room_order.code, *value['to_add']]))))
await room_order.send_answers() await room_order.send_answers()
await request.app.ctx.om.fill_cache() await clear_cache(request, order)
return text('done', status=200) return text('done', status=200)

15
api.py
View File

@ -34,7 +34,8 @@ async def api_members(request):
'propic_fursuiter': o.ans('propic_fursuiter'), 'propic_fursuiter': o.ans('propic_fursuiter'),
'staff_role': o.ans('staff_role'), 'staff_role': o.ans('staff_role'),
'country': o.country, 'country': o.country,
'is_checked_in': False, 'room_id': o.room_id,
'is_checked_in': o.checked_in,
'points': random.randint(0,50) if random.random() > 0.3 else 0 'points': random.randint(0,50) if random.random() > 0.3 else 0
}) })
@ -113,6 +114,10 @@ async def token_test(request):
return response.json({'ok': True, 'message': 'This token is valid :)'}) return response.json({'ok': True, 'message': 'This token is valid :)'})
@bp.get("/ping")
async def ping(request):
return response.text("pong")
@bp.get("/welcome") @bp.get("/welcome")
async def welcome_app(request): async def welcome_app(request):
@ -139,15 +144,18 @@ async def welcome_app(request):
'propic_fursuiter': o.ans('propic_fursuiter'), 'propic_fursuiter': o.ans('propic_fursuiter'),
'staff_role': o.ans('staff_role'), 'staff_role': o.ans('staff_role'),
'country': o.country, 'country': o.country,
'is_checked_in': False, 'is_checked_in': o.checked_in,
'points': random.randint(0,50) if random.random() > 0.3 else 0, 'points': random.randint(0,50) if random.random() > 0.3 else 0,
'can_scan_nfc': o.can_scan_nfc, 'can_scan_nfc': o.can_scan_nfc,
'room_id': o.room_id,
#'mail': o.email,
'actual_room_id': o.actual_room, 'actual_room_id': o.actual_room,
**ret **ret
}) })
@bp.get("/scan/<nfc_id>") @bp.get("/scan/<nfc_id>")
async def nfc_scan(request, nfc_id): async def nfc_scan(request, nfc_id):
return response.text("Nope")
if not request.token: if not request.token:
return response.json({'ok': False, 'error': 'You need to provide a token.'}, status=401) return response.json({'ok': False, 'error': 'You need to provide a token.'}, status=401)
@ -174,11 +182,12 @@ async def nfc_scan(request, nfc_id):
'propic_fursuiter': o.ans('propic_fursuiter'), 'propic_fursuiter': o.ans('propic_fursuiter'),
'staff_role': o.ans('staff_role'), 'staff_role': o.ans('staff_role'),
'country': o.country, 'country': o.country,
'is_checked_in': False, 'is_checked_in': o.checked_in,
'points': random.randint(0,50) if random.random() > 0.3 else 0, 'points': random.randint(0,50) if random.random() > 0.3 else 0,
'comment': o.comment, 'comment': o.comment,
'actual_room_id': o.actual_room, 'actual_room_id': o.actual_room,
'phone': o.phone, 'phone': o.phone,
'room_id': o.room_id,
'telegram_username': o.telegram_username, 'telegram_username': o.telegram_username,
'roommates': {x: (await request.app.ctx.om.get_order(code=x, cached=True)).name for x in room_owner.room_members if x != o.code} 'roommates': {x: (await request.app.ctx.om.get_order(code=x, cached=True)).name for x in room_owner.room_members if x != o.code}
}) })

10
app.py
View File

@ -16,10 +16,12 @@ 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 *
from utils import isSessionAdmin
from email_util import killSmptClient from email_util import killSmptClient
import pretixClient import pretixClient
import traceback import traceback
app = Sanic(__name__) app = Sanic(__name__)
app.static("/res", "res/") app.static("/res", "res/")
@ -80,6 +82,7 @@ async def main_start(*_):
app.ctx.tpl = Environment(loader=FileSystemLoader("tpl"), autoescape=True) app.ctx.tpl = Environment(loader=FileSystemLoader("tpl"), autoescape=True)
app.ctx.tpl.globals.update(time=time) app.ctx.tpl.globals.update(time=time)
app.ctx.tpl.globals.update(PROPIC_DEADLINE=PROPIC_DEADLINE) app.ctx.tpl.globals.update(PROPIC_DEADLINE=PROPIC_DEADLINE)
app.ctx.tpl.globals.update(ROOM_DEADLINE=ROOM_DEADLINE)
app.ctx.tpl.globals.update(LOCALES=LOCALES) app.ctx.tpl.globals.update(LOCALES=LOCALES)
app.ctx.tpl.globals.update(ITEMS_ID_MAP=ITEMS_ID_MAP) app.ctx.tpl.globals.update(ITEMS_ID_MAP=ITEMS_ID_MAP)
app.ctx.tpl.globals.update(ITEM_VARIATIONS_MAP=ITEM_VARIATIONS_MAP) app.ctx.tpl.globals.update(ITEM_VARIATIONS_MAP=ITEM_VARIATIONS_MAP)
@ -98,11 +101,6 @@ async def gen_barcode(request, code):
return raw(img.getvalue(), content_type="image/png") return raw(img.getvalue(), content_type="image/png")
@app.route("/manage/lol")
async def lol(request: Request):
await get_quotas(request)
return text('hi')
@app.route(f"/{ORGANIZER}/{EVENT_NAME}/order/<code>/<secret>/open/<secret2>") @app.route(f"/{ORGANIZER}/{EVENT_NAME}/order/<code>/<secret>/open/<secret2>")
async def redirect_explore(request, code, secret, order: Order, secret2=None): async def redirect_explore(request, code, secret, order: Order, secret2=None):
@ -161,7 +159,7 @@ async def welcome(request, order: Order, quota: Quotas):
room_members.append(await app.ctx.om.get_order(code=member_id, cached=True)) room_members.append(await app.ctx.om.get_order(code=member_id, cached=True))
tpl = app.ctx.tpl.get_template('welcome.html') tpl = app.ctx.tpl.get_template('welcome.html')
return html(tpl.render(order=order, quota=quota, room_members=room_members, pending_roommates=pending_roommates, ROOM_ERROR_MESSAGES=ROOM_ERROR_TYPES)) 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)))
@app.route("/manage/download_ticket") @app.route("/manage/download_ticket")

View File

@ -17,6 +17,8 @@ PROPIC_MAX_FILE_SIZE = 5 * 1024 * 1024 # 5MB
PROPIC_MAX_SIZE = (2048, 2048) # (Width, Height) PROPIC_MAX_SIZE = (2048, 2048) # (Width, Height)
PROPIC_MIN_SIZE = (125, 125) # (Width, Height) PROPIC_MIN_SIZE = (125, 125) # (Width, Height)
ROOM_DEADLINE = 9999999999
# This is used for feedback sending inside of the app. Feedbacks will be sent to the specified chat using the bot api id. # This is used for feedback sending inside of the app. Feedbacks will be sent to the specified chat using the bot api id.
TG_BOT_API = '123456789:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' TG_BOT_API = '123456789:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
TG_CHAT_ID = -1234567 TG_CHAT_ID = -1234567

View File

@ -15,30 +15,44 @@ def killSmptClient():
global sslLock global sslLock
global sslTimer global sslTimer
global smptSender global smptSender
logger.info(f"[SMPT] killSmptClient: Lock status: {sslLock.locked()}")
sslTimer.cancel() sslTimer.cancel()
sslLock.acquire() sslLock.acquire()
exp = None
if(smptSender is not None): if(smptSender is not None):
logger.debug('[SMPT] Closing smpt client') logger.debug('[SMPT] Closing smpt client')
try:
smptSender.quit() # it calls close() inside smptSender.quit() # it calls close() inside
except Exception as e:
exp = e
smptSender = None smptSender = None
sslLock.release() sslLock.release()
if(exp != None):
raise exp
async def openSmptClient(): async def openSmptClient():
global sslLock global sslLock
global sslTimer global sslTimer
global sslContext global sslContext
global smptSender global smptSender
logger.info(f"[SMPT] openSmptClient: Lock status: {sslLock.locked()}")
sslTimer.cancel() sslTimer.cancel()
sslLock.acquire() sslLock.acquire()
exp = None
try:
if(smptSender is None): if(smptSender is None):
logger.debug('[SMPT] Opening smpt client') logger.debug('[SMPT] Opening smpt client')
client : smtplib.SMTP = smtplib.SMTP(SMTP_HOST, SMTP_PORT) client : smtplib.SMTP = smtplib.SMTP(SMTP_HOST, SMTP_PORT)
client.starttls(context=sslContext) client.starttls(context=sslContext)
client.login(SMTP_USER, SMTP_PASSWORD) client.login(SMTP_USER, SMTP_PASSWORD)
smptSender = client smptSender = client
except Exception as e:
exp = e
sslLock.release() sslLock.release()
sslTimer = createTimer() sslTimer = createTimer()
sslTimer.start() sslTimer.start()
if(exp != None):
raise exp
def createTimer(): def createTimer():
return Timer(SMPT_CLIENT_CLOSE_TIMEOUT, killSmptClient) return Timer(SMPT_CLIENT_CLOSE_TIMEOUT, killSmptClient)
@ -51,9 +65,16 @@ async def sendEmail(message : MIMEMultipart):
message['From'] = f'{EMAIL_SENDER_NAME} <{EMAIL_SENDER_MAIL}>' message['From'] = f'{EMAIL_SENDER_NAME} <{EMAIL_SENDER_MAIL}>'
await openSmptClient() await openSmptClient()
logger.debug(f"[SMPT] Sending mail {message['From']} -> {message['to']} '{message['Subject']}'") logger.debug(f"[SMPT] Sending mail {message['From']} -> {message['to']} '{message['Subject']}'")
logger.info(f"[SMPT] sendEmail: Lock status: {sslLock.locked()}")
exp = None
sslLock.acquire() sslLock.acquire()
try:
smptSender.sendmail(message['From'], message['to'], message.as_string()) smptSender.sendmail(message['From'], message['to'], message.as_string())
except Exception as e:
exp = e
sslLock.release() sslLock.release()
if(exp != None):
raise exp
def render_email_template(title = "", body = ""): def render_email_template(title = "", body = ""):
tpl = Environment(loader=FileSystemLoader("tpl"), autoescape=False).get_template('email/comunication.html') tpl = Environment(loader=FileSystemLoader("tpl"), autoescape=False).get_template('email/comunication.html')

24
ext.py
View File

@ -282,9 +282,12 @@ class Quota:
return f'Quota [items={self.items}, variations={self.variations}] [{self.available_number}/{self.size}]' return f'Quota [items={self.items}, variations={self.variations}] [{self.available_number}/{self.size}]'
def get_quota(item: int, variation: int = None) -> Quota: def get_quota(item: int, variation: int = None) -> Quota:
ret : Quota = None
for q in QUOTA_LIST: for q in QUOTA_LIST:
if (q.has_item(item, variation)): return q if (q.has_item(item, variation)):
return None if(ret == None or (q.size != None and q.size < ret.size)):
ret = q
return ret
@dataclass @dataclass
class Quotas: class Quotas:
@ -364,9 +367,24 @@ class OrderManager:
del cache[code] del cache[code]
orderList.remove(code) orderList.remove(code)
async def fill_cache(self, check_itemsQuestions=False) -> bool: async def fill_cache(self, check_itemsQuestions=False) -> bool:
# Check cache lock # Check cache lock
logger.info(f"[CACHE] Lock status: {self.updating.locked()}")
self.updating.acquire() self.updating.acquire()
ret = False
exp = None
try:
ret = await self.fill_cache_INTERNAL(check_itemsQuestions=check_itemsQuestions)
except Exception as e:
exp = e
self.updating.release()
logger.info(f"[CACHE] Ret status: {ret}. Exp: {exp}")
if(exp != None):
raise exp
return ret
async def fill_cache_INTERNAL(self, check_itemsQuestions=False) -> bool:
start_time = time() start_time = time()
logger.info("[CACHE] Filling cache...") logger.info("[CACHE] Filling cache...")
# Index item's ids # Index item's ids
@ -409,8 +427,6 @@ class OrderManager:
except Exception: except Exception:
logger.error(f"[CACHE] Error while refreshing cache.\n{traceback.format_exc()}") logger.error(f"[CACHE] Error while refreshing cache.\n{traceback.format_exc()}")
success = False success = False
finally:
self.updating.release()
# Apply new cache if there were no errors # Apply new cache if there were no errors
if(success): if(success):

View File

@ -3,6 +3,7 @@ from sanic import Blueprint, exceptions, response
from ext import * from ext import *
from urllib.parse import unquote from urllib.parse import unquote
from config import ADMINS from config import ADMINS
from utils import isSessionAdmin
import json import json
bp = Blueprint("karaoke", url_prefix="/manage/karaoke") bp = Blueprint("karaoke", url_prefix="/manage/karaoke")
@ -10,7 +11,7 @@ bp = Blueprint("karaoke", url_prefix="/manage/karaoke")
@bp.get("/admin") @bp.get("/admin")
async def show_songs(request, order: Order): async def show_songs(request, order: Order):
if not order.isAdmin(): if not await isSessionAdmin(request, order):
raise exceptions.Forbidden("Birichino") raise exceptions.Forbidden("Birichino")
orders = [x for x in request.app.ctx.om.cache.values() if x.karaoke_songs] orders = [x for x in request.app.ctx.om.cache.values() if x.karaoke_songs]
@ -28,7 +29,7 @@ async def show_songs(request, order: Order):
@bp.post("/approve") @bp.post("/approve")
async def approve_songs(request, order: Order): async def approve_songs(request, order: Order):
if not order.isAdmin(): if not await isSessionAdmin(request, order):
raise exceptions.Forbidden("Birichino") raise exceptions.Forbidden("Birichino")
for song in request.form: for song in request.form:
@ -44,7 +45,7 @@ async def sing_song(request, order: Order, songname):
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: raise exceptions.Forbidden("You have been logged out. Please access the link in your E-Mail to login again!")
if not order.isAdmin(): if not await isSessionAdmin(request, order):
raise exceptions.Forbidden("Birichino") raise exceptions.Forbidden("Birichino")
songname = unquote(songname) songname = unquote(songname)

View File

@ -6,6 +6,7 @@ from PIL import Image
from io import BytesIO from io import BytesIO
from hashlib import sha224 from hashlib import sha224
from time import time from time import time
from utils import isSessionAdmin
import os import os
bp = Blueprint("propic", url_prefix="/manage/propic") bp = Blueprint("propic", url_prefix="/manage/propic")
@ -38,7 +39,7 @@ async def upload_propic(request, order: Order):
if order.propic_locked: if order.propic_locked:
raise exceptions.BadRequest("You have been limited from further editing the propic.") raise exceptions.BadRequest("You have been limited from further editing the propic.")
if request.form.get('submit') != 'Upload' and time() > PROPIC_DEADLINE: if request.form.get('submit') != 'Upload' and (time() > PROPIC_DEADLINE and not await isSessionAdmin(request, order)):
raise exceptions.BadRequest("The deadline has passed. You cannot modify the badges at this moment.") raise exceptions.BadRequest("The deadline has passed. You cannot modify the badges at this moment.")
if request.form.get('submit') == 'Delete main image': if request.form.get('submit') == 'Delete main image':

11
room.py
View File

@ -6,9 +6,18 @@ from config import headers
import os import os
from image_util import generate_room_preview, get_room from image_util import generate_room_preview, get_room
from utils import confirm_room_by_order from utils import confirm_room_by_order
from time import time
bp = Blueprint("room", url_prefix="/manage/room") bp = Blueprint("room", url_prefix="/manage/room")
@bp.middleware
async def deadline_check(request: Request):
order = await get_order(request)
if not order:
raise exceptions.Forbidden("You have been logged out. Please access the link in your E-Mail to login again!")
if time() > ROOM_DEADLINE and not await isSessionAdmin(request, order):
raise exceptions.BadRequest("The deadline has passed. You cannot modify the room at this moment.")
@bp.post("/create") @bp.post("/create")
async def room_create_post(request, order: Order): async def room_create_post(request, order: Order):
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: raise exceptions.Forbidden("You have been logged out. Please access the link in your E-Mail to login again!")
@ -304,7 +313,7 @@ async def confirm_room(request, order: Order, quotas: Quotas):
#if quotas.get_left(len(order.room_members)) == 0: #if quotas.get_left(len(order.room_members)) == 0:
# raise exceptions.BadRequest("There are no more rooms of this size to reserve.") # raise exceptions.BadRequest("There are no more rooms of this size to reserve.")
confirm_room_by_order(order, request) await confirm_room_by_order(order, request)
return redirect('/manage/welcome') return redirect('/manage/welcome')

View File

@ -1,21 +1,28 @@
#!/usr/bin/python #!/usr/bin/python
from os import listdir, remove from os import listdir, remove, getenv
from os.path import isfile, join from os.path import isfile, join
import datetime import datetime
import subprocess import subprocess
PRETIX_BACKUP = False PRETIX_BACKUP = False
WEBINT_BACKUP = False WEBINT_BACKUP = False
WP_BACKUP = False
STUFF_BACKUP = False
BACKUP_DIR_PRETIX = "/home/pretix/backups/" BACKUP_DIR_PRETIX = "/home/pretix/backups/"
BACKUP_DIR_WEBINT = "/home/webint/backups/" BACKUP_DIR_WEBINT = "/home/webint/backups/"
BACKUP_DIR_WP = "/home/worcopio/backups/"
BACKUP_DIR_STUFF = "/root/backups/"
MAX_FILE_NO = 14 MAX_FILE_NO = 7
COMMAND_PRETIX_POSTGRES = "pg_dump -F p pretix | gzip > %s" # Restore with psql -f %s COMMAND_PRETIX_POSTGRES = "pg_dump -F p pretix | gzip > %s" # Restore with psql -f %s
COMMAND_PRETIX_DATA = "tar -cf %s /var/pretix-data" # Restore with tar -xvf %s. To make .secret readable I used setfacl -m u:pretix:r /var/pretix-data/.secret COMMAND_PRETIX_DATA = "tar -czf %s /var/pretix-data" # Restore with tar -xvf %s. To make .secret readable I used setfacl -m u:pretix:r /var/pretix-data/.secret
COMMAND_WEBINT = "tar -cf %s /home/webint/furizon_webint" # Restore with tar -xvf %s COMMAND_WEBINT = "tar -czf %s /home/webint/furizon_webint" # Restore with tar -xvf %s
COMMAND_WP_MYSQL = "mysqldump -h 127.0.0.1 -P 5688 -u root --password=__PASSWORD__ --all-databases | gzip > %s" # Restore with zcat %s | mysql -h 127.0.0.1 -P 5688 -u root
COMMAND_WP_DATA = "tar -czf %s /var/lib/docker/volumes/worcopio-docker_wordpress/_data" # Restore with tar -xvf %s
COMMAND_STUFF = "tar -czf %s /etc/ /var/backups/ /var/log/ /var/mail/ /var/pretix-data/ /var/prometheus-data/ /var/spool/ /var/www/ /var/lib/grafana/ /var/lib/redis/" # Restore with tar -xvf %s
def deleteOlder(path : str, prefix : str, postfix : str): def deleteOlder(path : str, prefix : str, postfix : str):
@ -42,3 +49,18 @@ if(PRETIX_BACKUP):
if(WEBINT_BACKUP): if(WEBINT_BACKUP):
runBackup("webint_full", "backup.tar.gz", BACKUP_DIR_WEBINT, COMMAND_WEBINT) runBackup("webint_full", "backup.tar.gz", BACKUP_DIR_WEBINT, COMMAND_WEBINT)
if(WP_BACKUP):
mysqlCmd = COMMAND_WP_MYSQL
mysqlPwd = getenv("MYSQL_PWD")
if(mysqlPwd != None and mysqlPwd.strip() != ""):
mysqlPwd = mysqlPwd.strip()
print(f"Running with password `{mysqlPwd}`")
mysqlCmd = mysqlCmd.replace("__PASSWORD__", mysqlPwd)
runBackup("wp_mysql", "backup.sql.gz", join(BACKUP_DIR_WP, "mysql"), mysqlCmd)
else:
print("Backup run without the $MYSQL_PWD env var set. Skipping wp_mysql backup")
runBackup("wp_wp", "backup.tar.gz", join(BACKUP_DIR_WP, "wp"), COMMAND_WP_DATA)
if(STUFF_BACKUP):
runBackup("stuff", "backup.tar.gz", BACKUP_DIR_STUFF, COMMAND_STUFF)

11
stuff/testAsyncio.py Normal file
View File

@ -0,0 +1,11 @@
# python merda
import asyncio
async def a():
print("a")
def b():
loop = asyncio.get_event_loop()
print(loop)
b()

View File

@ -32,7 +32,7 @@
{% endif %} {% endif %}
</div> </div>
{% if time() > PROPIC_DEADLINE %} {% if time() > PROPIC_DEADLINE and not isSessionAdmin %}
<p class="notice">⚠️ The deadline to upload pictures for the badge has expired. For last-minute changes, please contact the support over at <a href="mailto:info@furizon.net">info@furizon.net</a>. If your badge has been printed already, changing it will incur in a 2€ fee. You can also get extra badges at the reception for the same price. If you upload a propic now, it might not be printed on time.</p> <p class="notice">⚠️ The deadline to upload pictures for the badge has expired. For last-minute changes, please contact the support over at <a href="mailto:info@furizon.net">info@furizon.net</a>. If your badge has been printed already, changing it will incur in a 2€ fee. You can also get extra badges at the reception for the same price. If you upload a propic now, it might not be printed on time.</p>
{% else %} {% else %}
<p><em> <p><em>
@ -43,9 +43,9 @@
{% endif %} {% endif %}
<div class="grid grid_2x2"> <div class="grid grid_2x2">
<input style="grid-area: 1 / 1 / 2 / 3;" type="submit" name="submit" value="Upload" {{'disabled' if (order.ans('propic') and order.ans('propic_fursuiter')) else ''}} /> <input style="grid-area: 1 / 1 / 2 / 3;" type="submit" name="submit" value="Upload" {{'disabled' if ((order.ans('propic') and order.ans('propic_fursuiter'))) or (time() > PROPIC_DEADLINE and not isSessionAdmin) else ''}} />
<input style="grid-area: 2 / 1 / 3 / 2;" type="submit" name="submit" value="Delete main image" {{'disabled' if (time() > PROPIC_DEADLINE or not order.ans('propic')) else ''}} /> <input style="grid-area: 2 / 1 / 3 / 2;" type="submit" name="submit" value="Delete main image" {{'disabled' if ((time() > PROPIC_DEADLINE and not isSessionAdmin) or not order.ans('propic')) else ''}} />
<input style="grid-area: 2 / 2 / 3 / 3;" type="submit" name="submit" value="Delete fursuit image" {{'disabled' if (time() > PROPIC_DEADLINE or not order.ans('propic_fursuiter')) else ''}} /> <input style="grid-area: 2 / 2 / 3 / 3;" type="submit" name="submit" value="Delete fursuit image" {{'disabled' if ((time() > PROPIC_DEADLINE and not isSessionAdmin) or not order.ans('propic_fursuiter')) else ''}} />
</div> </div>
</form> </form>
</details> </details>

View File

@ -8,6 +8,11 @@
<p class="notice" style="background:#0881c0"><b><a href="/manage/nosecount?filter=capacity">Check here</a> for any fur who share your room type.</p> <p class="notice" style="background:#0881c0"><b><a href="/manage/nosecount?filter=capacity">Check here</a> for any fur who share your room type.</p>
{% endif %} {% endif %}
{% if time() > ROOM_DEADLINE %}
<p class="notice">⚠️ The deadline to edit your room has passed. If your room is not full it will be subject to changes by the staff as we optimize for hotel capacity.</p>
{% else %}
{# Show alert if room owner has wrong people inside #} {# Show alert if room owner has wrong people inside #}
{# {% if room_members and quota.get_left(len(room_members)) == 0 and (not order.room_confirmed) %} #} {# {% if room_members and quota.get_left(len(room_members)) == 0 and (not order.room_confirmed) %} #}
@ -19,6 +24,8 @@
<p class="notice">⚠️ Your room hasn't been confirmed yet. Unconfirmed rooms are subject to changes by the staff as we optimize for hotel capacity.</p> <p class="notice">⚠️ Your room hasn't been confirmed yet. Unconfirmed rooms are subject to changes by the staff as we optimize for hotel capacity.</p>
{% endif %} {% endif %}
{% endif %}
{# Show notice if the room is confirmed #} {# Show notice if the room is confirmed #}
{% if order.room_confirmed %} {% if order.room_confirmed %}
{# <p class="notice" style="background:#060">✅ Your <strong>{{[None,'single','double','triple','quadruple','quintuple'][len(room_members)]}}</strong> room has been confirmed</p> #} {# <p class="notice" style="background:#060">✅ Your <strong>{{[None,'single','double','triple','quadruple','quintuple'][len(room_members)]}}</strong> room has been confirmed</p> #}
@ -40,7 +47,7 @@
{% if person.status == 'pending' %} {% if person.status == 'pending' %}
<p><strong style="color:red;">UNPAID</strong></p> <p><strong style="color:red;">UNPAID</strong></p>
{% endif %} {% endif %}
{% if order.room_owner and person.code != order.code and (not order.room_confirmed) %}<a href="/manage/room/kick/{{person.code}}">KICK</a>{% endif %} {% if order.room_owner and person.code != order.code and (not order.room_confirmed) and (time() <= ROOM_DEADLINE or isSessionAdmin) %}<a href="/manage/room/kick/{{person.code}}">KICK</a>{% endif %}
</div> </div>
{% if person.status != 'paid' %} {% if person.status != 'paid' %}
@ -51,7 +58,7 @@
{# {% if order.room_id == order.code and not order.room_confirmed and len(room_members) < 5%} #} {# {% if order.room_id == order.code and not order.room_confirmed and len(room_members) < 5%} #}
{% if order.room_id == order.code and not order.room_confirmed and len(room_members) < order.room_person_no %} {% if order.room_id == order.code and not order.room_confirmed and len(room_members) < order.room_person_no %}
<div> <div>
<a href="javascript:document.getElementById('modal-roominvite').setAttribute('open', 'true');"> <a {% if time() <= ROOM_DEADLINE or isSessionAdmin %} href="javascript:document.getElementById('modal-roominvite').setAttribute('open', 'true');" {% else %} disabled {% endif %}>
<div class="propic-container"> <div class="propic-container">
<img class="propic" src="/res/new.png" /> <img class="propic" src="/res/new.png" />
<h3>Invite</h3> <h3>Invite</h3>
@ -63,13 +70,13 @@
</div> </div>
{% elif order.pending_room %} {% elif order.pending_room %}
<p>You have have asked to join the room of another member. Wait for them to confirm or reject your request.</p> <p>You have have asked to join the room of another member. Wait for them to confirm or reject your request.</p>
<a role="button" href="/manage/room/cancel_request">Cancel pending join request</a> <a role="button" href="/manage/room/cancel_request" {{'disabled' if time() > ROOM_DEADLINE and not isSessionAdmin else ''}}>Cancel pending join request</a>
{% else %} {% else %}
<p class="notice">🎲 If you don't join a room or create your one within the room deadline, we will randomly put you into a room with free spots.</p> <p class="notice">🎲 If you don't join a room or create your one within the room deadline, we will randomly put you into a room with free spots.</p>
<p>To join a room, ask somebody to send you their room code.</p> <p>To join a room, ask somebody to send you their room code.</p>
<p class="grid"> <p class="grid">
<a role="button" href="/manage/room/create">Create a room</a> <a role="button" href="/manage/room/create" {{'disabled' if time() > ROOM_DEADLINE and not isSessionAdmin else ''}}>Create a room</a>
<a role="button" href="javascript:document.getElementById('modal-joinroom').setAttribute('open', 'true');">Join a room</a> <a role="button" href="javascript:document.getElementById('modal-joinroom').setAttribute('open', 'true');" {{'disabled' if time() > ROOM_DEADLINE and not isSessionAdmin else ''}}>Join a room</a>
</p> </p>
{% endif %} {% endif %}
@ -83,17 +90,17 @@
{% if order.room_owner %} {% if order.room_owner %}
{% if not order.room_confirmed %} {% if not order.room_confirmed %}
{# <a role="button" {% if not room.forbidden and quota.get_left(len(room_members)) > 0 %}href="javascript:document.getElementById('modal-roomconfirm').setAttribute('open', 'true');"{% endif %}>Confirm <strong>{{[None,'single','double','triple','quadruple','quintuple'][len(room_members)]}}</strong> room</a> #} {# <a role="button" {{'disabled' if time() > ROOM_DEADLINE and not isSessionAdmin else ''}} {% if not room.forbidden and quota.get_left(len(room_members)) > 0 %}href="javascript:document.getElementById('modal-roomconfirm').setAttribute('open', 'true');"{% endif %}>Confirm <strong>{{[None,'single','double','triple','quadruple','quintuple'][len(room_members)]}}</strong> room</a> #}
<a style="grid-area: 1 / 1 / 2 / 2;" role="button" href="javascript:document.getElementById('modal-roomrename').setAttribute('open', 'true');">Rename room</a> <a style="grid-area: 1 / 1 / 2 / 2;" role="button" href="javascript:document.getElementById('modal-roomrename').setAttribute('open', 'true');" {{'disabled' if time() > ROOM_DEADLINE and not isSessionAdmin else ''}}>Rename room</a>
<a style="grid-area: 1 / 2 / 2 / 3;" href="/manage/room/delete" role="button" {{'disabled' if (len(room_members) > 1) else ''}} >Delete room</a> <a style="grid-area: 1 / 2 / 2 / 3;" href="/manage/room/delete" role="button" {{'disabled' if (len(room_members) > 1) or (time() > ROOM_DEADLINE and not isSessionAdmin) else ''}} >Delete room</a>
<a style="grid-area: 2 / 1 / 3 / 3; display:block;" role="button" {% if not room.forbidden and len(room_members) == order.room_person_no %}href="javascript:document.getElementById('modal-roomconfirm').setAttribute('open', 'true');"{% endif %}>Confirm <strong>{{[None,'single','double','triple','quadruple','quintuple'][order.room_person_no]}}</strong> room</a> <a style="grid-area: 2 / 1 / 3 / 3; display:block;" role="button" {{'disabled' if time() > ROOM_DEADLINE and not isSessionAdmin else ''}} {% if not room.forbidden and len(room_members) == order.room_person_no %}href="javascript:document.getElementById('modal-roomconfirm').setAttribute('open', 'true');"{% endif %}>Confirm <strong>{{[None,'single','double','triple','quadruple','quintuple'][order.room_person_no]}}</strong> room</a>
{% else %} {% else %}
{# <a style="grid-area: 1 / 1 / 2 / 2;" role="button" href="javascript:navigator.share({title: 'Furizon room', text:'Viewing room {{order.room_name}}', url: `${window.location.protocol}//${window.location.host}/manage/room/view/{{order.code}}}`});">Share</a> #} {# <a style="grid-area: 1 / 1 / 2 / 2;" role="button" {{'disabled' if time() > ROOM_DEADLINE and not isSessionAdmin else ''}} href="javascript:navigator.share({title: 'Furizon room', text:'Viewing room {{order.room_name}}', url: `${window.location.protocol}//${window.location.host}/manage/room/view/{{order.code}}}`});">Share</a> #}
{% endif %} {% endif %}
{% else %} {% else %}
{% if order.room_id and not order.room_confirmed %} {% if order.room_id and not order.room_confirmed %}
<a href="/manage/room/leave" role="button">Leave room</a> <a href="/manage/room/leave" role="button" {{'disabled' if time() > ROOM_DEADLINE and not isSessionAdmin else ''}}>Leave room</a>
{% endif %} {% endif %}
{% endif %} {% endif %}
</p> </p>
@ -111,8 +118,8 @@
<td><strong style="color:red;">UNPAID</strong></td> <td><strong style="color:red;">UNPAID</strong></td>
{% endif %} {% endif %}
{% if order.room_owner %} {% if order.room_owner %}
<td style="width:1%;white-space: nowrap;"><a role="button" href="/manage/room/approve/{{person.code}}">Approve</a></td> <td style="width:1%;white-space: nowrap;"><a role="button" href="/manage/room/approve/{{person.code}}" {{'disabled' if time() > ROOM_DEADLINE and not isSessionAdmin else ''}}>Approve</a></td>
<td style="width:1%;white-space: nowrap;"><a role="button" href="/manage/room/reject/{{person.code}}">Reject</a></td> <td style="width:1%;white-space: nowrap;"><a role="button" href="/manage/room/reject/{{person.code}}" {{'disabled' if time() > ROOM_DEADLINE and not isSessionAdmin else ''}}>Reject</a></td>
{% endif %} {% endif %}
</tr> </tr>
</div> </div>

View File

@ -24,7 +24,7 @@
{%with room_order = unconfirmed_orders[room[0]] %} {%with room_order = unconfirmed_orders[room[0]] %}
<div class="room" id="room-{{room_order.code}}" room-type="{{room_order.bed_in_room}}" room-size="{{room_order.room_person_no - len(room_order.room_members)}}" current-size="{{len(room[1]['to_add'])}}"> <div class="room" id="room-{{room_order.code}}" room-type="{{room_order.bed_in_room}}" room-size="{{room_order.room_person_no - len(room_order.room_members)}}" current-size="{{len(room[1]['to_add'])}}">
<h4 style="margin-top:1em;"> <h4 style="margin-top:1em;">
<span>{{room_order.room_name if room_order.room_name else room[1]['room_name'] if room[1] and room[1]['room_name'] else ''}}</span> <span>{{room_order.room_name if room_order.room_name else room[1]['room_name'] if room[1] and room[1]['room_name'] else ''}} - {{room_order.room_person_no}} People max</span>
</h4> </h4>
<div class="grid people" style="padding-bottom:1em;"> <div class="grid people" style="padding-bottom:1em;">
{% for m in room_order.room_members %} {% for m in room_order.room_members %}

View File

@ -287,6 +287,19 @@ async def validate_rooms(request, rooms, om):
logger.info(f"[ROOM VALIDATION] Sent {sent_count} emails") logger.info(f"[ROOM VALIDATION] Sent {sent_count} emails")
# Returns true if the logged used is an admin OR if it's an admin logged as another user
async def isSessionAdmin(request, order):
if(order.isAdmin()): return True
orgCode = request.cookies.get("foxo_code_ORG")
orgSecret = request.cookies.get("foxo_secret_ORG")
if orgCode != None and orgSecret != None:
user = await request.app.ctx.om.get_order(code=orgCode)
if(user == None): return False
if(user.secret != orgSecret): raise exceptions.Forbidden("Birichino :)")
return user.isAdmin()
async def check_room(request, order, om=None): async def check_room(request, order, om=None):
room_errors = [] room_errors = []
room_members = [] room_members = []