forked from Hay1tsme/artemis
port frontend changes from different project
This commit is contained in:
parent
e27ac4b81f
commit
9dab26b122
@ -76,10 +76,12 @@ if not cfg.billing.standalone:
|
|||||||
Route("/request/", billing.handle_billing_request, methods=["POST"]),
|
Route("/request/", billing.handle_billing_request, methods=["POST"]),
|
||||||
]
|
]
|
||||||
|
|
||||||
if not cfg.frontend.standalone:
|
if not cfg.frontend.standalone and cfg.frontend.secret:
|
||||||
frontend = FrontendServlet(cfg, cfg_dir)
|
frontend = FrontendServlet(cfg, cfg_dir)
|
||||||
route_lst += frontend.get_routes()
|
route_lst += frontend.get_routes()
|
||||||
else:
|
else:
|
||||||
|
if not cfg.frontend.secret:
|
||||||
|
logger.error("Frontend secret not specified, cannot start frontend!")
|
||||||
route_lst.append(Route("/", dummy_rt))
|
route_lst.append(Route("/", dummy_rt))
|
||||||
route_lst.append(Route("/robots.txt", FrontendServlet.robots))
|
route_lst.append(Route("/robots.txt", FrontendServlet.robots))
|
||||||
|
|
||||||
|
@ -212,7 +212,7 @@ class FrontendConfig:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def secret(self) -> str:
|
def secret(self) -> str:
|
||||||
CoreConfig.get_config_field(
|
return CoreConfig.get_config_field(
|
||||||
self.__config, "core", "frontend", "secret", default=""
|
self.__config, "core", "frontend", "secret", default=""
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -58,7 +58,7 @@ class CardData(BaseData):
|
|||||||
"""
|
"""
|
||||||
Given a 20 digit access code as a string, get the user id associated with that card
|
Given a 20 digit access code as a string, get the user id associated with that card
|
||||||
"""
|
"""
|
||||||
card = self.get_card_by_access_code(access_code)
|
card = await self.get_card_by_access_code(access_code)
|
||||||
if card is None:
|
if card is None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@ -68,7 +68,7 @@ class CardData(BaseData):
|
|||||||
"""
|
"""
|
||||||
Given a 20 digit access code as a string, check if the card is banned
|
Given a 20 digit access code as a string, check if the card is banned
|
||||||
"""
|
"""
|
||||||
card = self.get_card_by_access_code(access_code)
|
card = await self.get_card_by_access_code(access_code)
|
||||||
if card is None:
|
if card is None:
|
||||||
return None
|
return None
|
||||||
if card["is_banned"]:
|
if card["is_banned"]:
|
||||||
@ -78,7 +78,7 @@ class CardData(BaseData):
|
|||||||
"""
|
"""
|
||||||
Given a 20 digit access code as a string, check if the card is locked
|
Given a 20 digit access code as a string, check if the card is locked
|
||||||
"""
|
"""
|
||||||
card = self.get_card_by_access_code(access_code)
|
card = await self.get_card_by_access_code(access_code)
|
||||||
if card is None:
|
if card is None:
|
||||||
return None
|
return None
|
||||||
if card["is_locked"]:
|
if card["is_locked"]:
|
||||||
|
@ -64,8 +64,8 @@ class UserData(BaseData):
|
|||||||
return False
|
return False
|
||||||
return result.fetchone()
|
return result.fetchone()
|
||||||
|
|
||||||
def check_password(self, user_id: int, passwd: bytes = None) -> bool:
|
async def check_password(self, user_id: int, passwd: bytes = None) -> bool:
|
||||||
usr = self.get_user(user_id)
|
usr = await self.get_user(user_id)
|
||||||
if usr is None:
|
if usr is None:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
776
core/frontend.py
776
core/frontend.py
@ -1,30 +1,21 @@
|
|||||||
import logging, coloredlogs
|
import logging, coloredlogs
|
||||||
from typing import Any, Dict, List
|
from typing import Any, Dict, List, Union, Optional
|
||||||
from twisted.web import resource
|
|
||||||
from twisted.web.util import redirectTo
|
|
||||||
from starlette.requests import Request
|
from starlette.requests import Request
|
||||||
from starlette.routing import Route
|
from starlette.routing import Route, Mount
|
||||||
from starlette.responses import Response, PlainTextResponse
|
from starlette.responses import Response, PlainTextResponse, RedirectResponse
|
||||||
from logging.handlers import TimedRotatingFileHandler
|
from logging.handlers import TimedRotatingFileHandler
|
||||||
from twisted.web.server import Session
|
|
||||||
from zope.interface import Interface, Attribute, implementer
|
|
||||||
from twisted.python.components import registerAdapter
|
|
||||||
import jinja2
|
import jinja2
|
||||||
import bcrypt
|
import bcrypt
|
||||||
import re
|
import re
|
||||||
|
import jwt
|
||||||
|
from base64 import b64decode
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from urllib import parse
|
from urllib import parse
|
||||||
|
from datetime import datetime, timezone
|
||||||
|
|
||||||
from core import CoreConfig, Utils
|
from core import CoreConfig, Utils
|
||||||
from core.data import Data
|
from core.data import Data
|
||||||
|
|
||||||
|
|
||||||
class IUserSession(Interface):
|
|
||||||
userId = Attribute("User's ID")
|
|
||||||
current_ip = Attribute("User's current ip address")
|
|
||||||
permissions = Attribute("User's permission level")
|
|
||||||
ongeki_version = Attribute("User's selected Ongeki Version")
|
|
||||||
|
|
||||||
class PermissionOffset(Enum):
|
class PermissionOffset(Enum):
|
||||||
USER = 0 # Regular user
|
USER = 0 # Regular user
|
||||||
USERMOD = 1 # Can moderate other users
|
USERMOD = 1 # Can moderate other users
|
||||||
@ -33,31 +24,38 @@ class PermissionOffset(Enum):
|
|||||||
# 4 - 6 reserved for future use
|
# 4 - 6 reserved for future use
|
||||||
OWNER = 7 # Can do anything
|
OWNER = 7 # Can do anything
|
||||||
|
|
||||||
@implementer(IUserSession)
|
class ShopPermissionOffset(Enum):
|
||||||
class UserSession(object):
|
VIEW = 0 # View info and cabs
|
||||||
def __init__(self, session):
|
BOOKKEEP = 1 # View bookeeping info
|
||||||
self.userId = 0
|
EDITOR = 2 # Can edit name, settings
|
||||||
self.current_ip = "0.0.0.0"
|
REGISTRAR = 3 # Can add cabs
|
||||||
self.permissions = 0
|
# 4 - 6 reserved for future use
|
||||||
self.ongeki_version = 7
|
OWNER = 7 # Can do anything
|
||||||
|
|
||||||
|
class ShopOwner():
|
||||||
|
def __init__(self, usr_id: int = 0, usr_name: str = "", perms: int = 0) -> None:
|
||||||
|
self.user_id = usr_id
|
||||||
|
self.username = usr_name
|
||||||
|
self.permissions = perms
|
||||||
|
|
||||||
class FrontendServlet(resource.Resource):
|
class UserSession():
|
||||||
def getChild(self, name: bytes, request: Request):
|
def __init__(self, usr_id: int = 0, ip: str = "", perms: int = 0, ongeki_ver: int = 7):
|
||||||
self.logger.debug(f"{Utils.get_ip_addr(request)} -> {name.decode()}")
|
self.user_id = usr_id
|
||||||
if name == b"":
|
self.current_ip = ip
|
||||||
return self
|
self.permissions = perms
|
||||||
return resource.Resource.getChild(self, name, request)
|
self.ongeki_version = ongeki_ver
|
||||||
|
|
||||||
|
class FrontendServlet():
|
||||||
def __init__(self, cfg: CoreConfig, config_dir: str) -> None:
|
def __init__(self, cfg: CoreConfig, config_dir: str) -> None:
|
||||||
self.config = cfg
|
self.config = cfg
|
||||||
log_fmt_str = "[%(asctime)s] Frontend | %(levelname)s | %(message)s"
|
log_fmt_str = "[%(asctime)s] Frontend | %(levelname)s | %(message)s"
|
||||||
log_fmt = logging.Formatter(log_fmt_str)
|
log_fmt = logging.Formatter(log_fmt_str)
|
||||||
self.logger = logging.getLogger("frontend")
|
|
||||||
self.environment = jinja2.Environment(loader=jinja2.FileSystemLoader("."))
|
self.environment = jinja2.Environment(loader=jinja2.FileSystemLoader("."))
|
||||||
self.game_list: List[Dict[str, str]] = []
|
self.game_list: Dict[str, Dict[str, Any]] = {}
|
||||||
self.children: Dict[str, Any] = {}
|
self.sn_cvt: Dict[str, str] = {}
|
||||||
|
|
||||||
|
self.logger = logging.getLogger("frontend")
|
||||||
|
if not hasattr(self.logger, "inited"):
|
||||||
fileHandler = TimedRotatingFileHandler(
|
fileHandler = TimedRotatingFileHandler(
|
||||||
"{0}/{1}.log".format(self.config.server.log_dir, "frontend"),
|
"{0}/{1}.log".format(self.config.server.log_dir, "frontend"),
|
||||||
when="d",
|
when="d",
|
||||||
@ -75,144 +73,276 @@ class FrontendServlet(resource.Resource):
|
|||||||
coloredlogs.install(
|
coloredlogs.install(
|
||||||
level=cfg.frontend.loglevel, logger=self.logger, fmt=log_fmt_str
|
level=cfg.frontend.loglevel, logger=self.logger, fmt=log_fmt_str
|
||||||
)
|
)
|
||||||
registerAdapter(UserSession, Session, IUserSession)
|
|
||||||
|
|
||||||
fe_game = FE_Game(cfg, self.environment)
|
self.logger.inited = True
|
||||||
|
|
||||||
games = Utils.get_all_titles()
|
games = Utils.get_all_titles()
|
||||||
for game_dir, game_mod in games.items():
|
for game_dir, game_mod in games.items():
|
||||||
if hasattr(game_mod, "frontend"):
|
if hasattr(game_mod, "frontend") and hasattr(game_mod, "index") and hasattr(game_mod, "game_codes"):
|
||||||
try:
|
try:
|
||||||
|
if game_mod.index.is_game_enabled(game_mod.game_codes[0], self.config, config_dir):
|
||||||
game_fe = game_mod.frontend(cfg, self.environment, config_dir)
|
game_fe = game_mod.frontend(cfg, self.environment, config_dir)
|
||||||
self.game_list.append({"url": game_dir, "name": game_fe.nav_name})
|
self.game_list[game_fe.nav_name] = {"url": f"/{game_dir}", "class": game_fe }
|
||||||
fe_game.putChild(game_dir.encode(), game_fe)
|
|
||||||
|
if hasattr(game_fe, "SN_PREFIX") and hasattr(game_fe, "NETID_PREFIX"):
|
||||||
|
if len(game_fe.SN_PREFIX) == len(game_fe.NETID_PREFIX):
|
||||||
|
for x in range(len(game_fe.SN_PREFIX)):
|
||||||
|
self.sn_cvt[game_fe.SN_PREFIX[x]] = game_fe.NETID_PREFIX[x]
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.error(
|
self.logger.error(
|
||||||
f"Failed to import frontend from {game_dir} because {e}"
|
f"Failed to import frontend from {game_dir} because {e}"
|
||||||
)
|
)
|
||||||
|
|
||||||
self.environment.globals["game_list"] = self.game_list
|
|
||||||
self.putChild(b"gate", FE_Gate(cfg, self.environment))
|
|
||||||
self.putChild(b"user", FE_User(cfg, self.environment))
|
|
||||||
self.putChild(b"sys", FE_System(cfg, self.environment))
|
|
||||||
self.putChild(b"arcade", FE_Arcade(cfg, self.environment))
|
|
||||||
self.putChild(b"cab", FE_Machine(cfg, self.environment))
|
|
||||||
self.putChild(b"game", fe_game)
|
|
||||||
|
|
||||||
self.logger.info(
|
self.environment.globals["game_list"] = self.game_list
|
||||||
f"Ready on port {self.config.server.port} serving {len(fe_game.children)} games"
|
self.environment.globals["sn_cvt"] = self.sn_cvt
|
||||||
)
|
self.base = FE_Base(cfg, self.environment)
|
||||||
|
self.gate = FE_Gate(cfg, self.environment)
|
||||||
|
self.user = FE_User(cfg, self.environment)
|
||||||
|
self.system = FE_System(cfg, self.environment)
|
||||||
|
self.arcade = FE_Arcade(cfg, self.environment)
|
||||||
|
self.machine = FE_Machine(cfg, self.environment)
|
||||||
|
|
||||||
def get_routes(self) -> List[Route]:
|
def get_routes(self) -> List[Route]:
|
||||||
return []
|
g_routes = []
|
||||||
|
for nav_name, g_data in self.environment.globals["game_list"].items():
|
||||||
|
g_routes.append(Mount(g_data['url'], routes=g_data['class'].get_routes()))
|
||||||
|
return [
|
||||||
|
Route("/", self.base.render_GET, methods=['GET']),
|
||||||
|
Mount("/user", routes=[
|
||||||
|
Route("/", self.user.render_GET, methods=['GET']),
|
||||||
|
Route("/{user_id:int}", self.user.render_GET, methods=['GET']),
|
||||||
|
Route("/update.pw", self.user.render_POST, methods=['POST']),
|
||||||
|
Route("/update.name", self.user.update_username, methods=['POST']),
|
||||||
|
Route("/edit.card", self.user.edit_card, methods=['POST']),
|
||||||
|
Route("/add.card", self.user.add_card, methods=['POST']),
|
||||||
|
Route("/logout", self.user.render_logout, methods=['GET']),
|
||||||
|
]),
|
||||||
|
Mount("/gate", routes=[
|
||||||
|
Route("/", self.gate.render_GET, methods=['GET', 'POST']),
|
||||||
|
Route("/gate.login", self.gate.render_login, methods=['POST']),
|
||||||
|
Route("/gate.create", self.gate.render_create, methods=['POST']),
|
||||||
|
Route("/create", self.gate.render_create_get, methods=['GET']),
|
||||||
|
]),
|
||||||
|
Mount("/sys", routes=[
|
||||||
|
Route("/", self.system.render_GET, methods=['GET']),
|
||||||
|
Route("/lookup.user", self.system.lookup_user, methods=['GET']),
|
||||||
|
Route("/lookup.shop", self.system.lookup_shop, methods=['GET']),
|
||||||
|
]),
|
||||||
|
Mount("/shop", routes=[
|
||||||
|
Route("/", self.arcade.render_GET, methods=['GET']),
|
||||||
|
Route("/{shop_id:int}", self.arcade.render_GET, methods=['GET']),
|
||||||
|
]),
|
||||||
|
Mount("/cab", routes=[
|
||||||
|
Route("/", self.machine.render_GET, methods=['GET']),
|
||||||
|
Route("/{machine_id:int}", self.machine.render_GET, methods=['GET']),
|
||||||
|
]),
|
||||||
|
Mount("/game", routes=g_routes),
|
||||||
|
Route("/robots.txt", self.robots)
|
||||||
|
]
|
||||||
|
|
||||||
|
def startup(self) -> None:
|
||||||
|
self.config.update({
|
||||||
|
"frontend": {
|
||||||
|
"standalone": True,
|
||||||
|
"loglevel": CoreConfig.loglevel_to_str(self.config.frontend.loglevel),
|
||||||
|
"secret": self.config.frontend.secret
|
||||||
|
}
|
||||||
|
})
|
||||||
|
self.logger.info(f"Serving {len(self.game_list)} games")
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def robots(cls, request: Request) -> PlainTextResponse:
|
async def robots(cls, request: Request) -> PlainTextResponse:
|
||||||
return PlainTextResponse("User-agent: *\nDisallow: /\n\nUser-agent: AdsBot-Google\nDisallow: /")
|
return PlainTextResponse("User-agent: *\nDisallow: /\n\nUser-agent: AdsBot-Google\nDisallow: /")
|
||||||
|
|
||||||
async def render_GET(self, request):
|
class FE_Base():
|
||||||
self.logger.debug(f"{Utils.get_ip_addr(request)} -> {request.uri.decode()}")
|
|
||||||
template = self.environment.get_template("core/frontend/index.jinja")
|
|
||||||
return template.render(
|
|
||||||
server_name=self.config.server.name,
|
|
||||||
title=self.config.server.name,
|
|
||||||
game_list=self.game_list,
|
|
||||||
sesh=vars(IUserSession(request.getSession())),
|
|
||||||
).encode("utf-16")
|
|
||||||
|
|
||||||
|
|
||||||
class FE_Base(resource.Resource):
|
|
||||||
"""
|
"""
|
||||||
A Generic skeleton class that all frontend handlers should inherit from
|
A Generic skeleton class that all frontend handlers should inherit from
|
||||||
Initializes the environment, data, logger, config, and sets isLeaf to true
|
Initializes the environment, data, logger, config, and sets isLeaf to true
|
||||||
It is expected that game implementations of this class overwrite many of these
|
It is expected that game implementations of this class overwrite many of these
|
||||||
"""
|
"""
|
||||||
|
|
||||||
isLeaf = True
|
|
||||||
|
|
||||||
def __init__(self, cfg: CoreConfig, environment: jinja2.Environment) -> None:
|
def __init__(self, cfg: CoreConfig, environment: jinja2.Environment) -> None:
|
||||||
self.core_config = cfg
|
self.core_config = cfg
|
||||||
self.data = Data(cfg)
|
self.data = Data(cfg)
|
||||||
self.logger = logging.getLogger("frontend")
|
self.logger = logging.getLogger("frontend")
|
||||||
self.environment = environment
|
self.environment = environment
|
||||||
self.nav_name = "nav_name"
|
self.nav_name = "index"
|
||||||
|
|
||||||
|
async def render_GET(self, request: Request):
|
||||||
|
self.logger.debug(f"{Utils.get_ip_addr(request)} -> {request.url}")
|
||||||
|
template = self.environment.get_template("core/templates/index.jinja")
|
||||||
|
sesh = self.validate_session(request)
|
||||||
|
resp = Response(template.render(
|
||||||
|
server_name=self.core_config.server.name,
|
||||||
|
title=self.core_config.server.name,
|
||||||
|
game_list=self.environment.globals["game_list"],
|
||||||
|
sesh=vars(sesh) if sesh is not None else vars(UserSession()),
|
||||||
|
))
|
||||||
|
|
||||||
|
if sesh is None:
|
||||||
|
resp.delete_cookie("DIANA_SESH")
|
||||||
|
return resp
|
||||||
|
|
||||||
|
def get_routes(self) -> List[Route]:
|
||||||
|
return []
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def test_perm(cls, permission: int, offset: Union[PermissionOffset, ShopPermissionOffset]) -> bool:
|
||||||
|
logging.getLogger('frontend').debug(f"{permission} vs {1 << offset.value}")
|
||||||
|
return permission & 1 << offset.value == 1 << offset.value
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def test_perm_minimum(cls, permission: int, offset: Union[PermissionOffset, ShopPermissionOffset]) -> bool:
|
||||||
|
return permission >= 1 << offset.value
|
||||||
|
|
||||||
|
def decode_session(self, token: str) -> UserSession:
|
||||||
|
sesh = UserSession()
|
||||||
|
if not token: return sesh
|
||||||
|
try:
|
||||||
|
tk = jwt.decode(token, b64decode(self.core_config.frontend.secret), options={"verify_signature": True}, algorithms=["HS256"])
|
||||||
|
sesh.user_id = tk['user_id']
|
||||||
|
sesh.current_ip = tk['current_ip']
|
||||||
|
sesh.permissions = tk['permissions']
|
||||||
|
|
||||||
|
if sesh.user_id <= 0:
|
||||||
|
self.logger.error("User session failed to validate due to an invalid ID!")
|
||||||
|
return UserSession()
|
||||||
|
return sesh
|
||||||
|
except jwt.ExpiredSignatureError:
|
||||||
|
self.logger.error("User session failed to validate due to an expired signature!")
|
||||||
|
return sesh
|
||||||
|
except jwt.InvalidSignatureError:
|
||||||
|
self.logger.error("User session failed to validate due to an invalid signature!")
|
||||||
|
return sesh
|
||||||
|
except jwt.DecodeError as e:
|
||||||
|
self.logger.error(f"User session failed to decode! {e}")
|
||||||
|
return sesh
|
||||||
|
except jwt.InvalidTokenError as e:
|
||||||
|
self.logger.error(f"User session is invalid! {e}")
|
||||||
|
return sesh
|
||||||
|
except KeyError as e:
|
||||||
|
self.logger.error(f"{e} missing from User session!")
|
||||||
|
return UserSession()
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(f"Unknown exception occoured when decoding User session! {e}")
|
||||||
|
return UserSession()
|
||||||
|
|
||||||
|
def validate_session(self, request: Request) -> Optional[UserSession]:
|
||||||
|
sesh = request.cookies.get('DIANA_SESH', "")
|
||||||
|
if not sesh:
|
||||||
|
return None
|
||||||
|
|
||||||
|
usr_sesh = self.decode_session(sesh)
|
||||||
|
req_ip = Utils.get_ip_addr(request)
|
||||||
|
|
||||||
|
if usr_sesh.current_ip != req_ip:
|
||||||
|
self.logger.error(f"User session failed to validate due to mismatched IPs! {usr_sesh.current_ip} -> {req_ip}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
if usr_sesh.permissions <= 0 or usr_sesh.permissions > 255:
|
||||||
|
self.logger.error(f"User session failed to validate due to an invalid permission value! {usr_sesh.permissions}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
return usr_sesh
|
||||||
|
|
||||||
|
def encode_session(self, sesh: UserSession, exp_seconds: int = 86400) -> str:
|
||||||
|
try:
|
||||||
|
return jwt.encode({ "user_id": sesh.user_id, "current_ip": sesh.current_ip, "permissions": sesh.permissions, "ongeki_version": sesh.ongeki_version, "exp": int(datetime.now(tz=timezone.utc).timestamp()) + exp_seconds }, b64decode(self.core_config.frontend.secret), algorithm="HS256")
|
||||||
|
except jwt.InvalidKeyError:
|
||||||
|
self.logger.error("Failed to encode User session because the secret is invalid!")
|
||||||
|
return ""
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(f"Unknown exception occoured when encoding User session! {e}")
|
||||||
|
return ""
|
||||||
|
|
||||||
class FE_Gate(FE_Base):
|
class FE_Gate(FE_Base):
|
||||||
def render_GET(self, request: Request):
|
async def render_GET(self, request: Request):
|
||||||
self.logger.debug(f"{Utils.get_ip_addr(request)} -> {request.uri.decode()}")
|
self.logger.debug(f"{Utils.get_ip_addr(request)} -> {request.url.path}")
|
||||||
uri: str = request.uri.decode()
|
|
||||||
|
|
||||||
sesh = request.getSession()
|
usr_sesh = self.validate_session(request)
|
||||||
usr_sesh = IUserSession(sesh)
|
if usr_sesh and usr_sesh.user_id > 0:
|
||||||
if usr_sesh.userId > 0:
|
return RedirectResponse("/user/", 303)
|
||||||
return redirectTo(b"/user", request)
|
|
||||||
|
|
||||||
if uri.startswith("/gate/create"):
|
|
||||||
return self.create_user(request)
|
|
||||||
|
|
||||||
if b"e" in request.args:
|
if "e" in request.query_params:
|
||||||
try:
|
try:
|
||||||
err = int(request.args[b"e"][0].decode())
|
err = int(request.query_params.get("e", ["0"])[0])
|
||||||
except Exception:
|
except Exception:
|
||||||
err = 0
|
err = 0
|
||||||
|
|
||||||
else:
|
else:
|
||||||
err = 0
|
err = 0
|
||||||
|
|
||||||
template = self.environment.get_template("core/frontend/gate/gate.jinja")
|
template = self.environment.get_template("core/templates/gate/gate.jinja")
|
||||||
return template.render(
|
resp = Response(template.render(
|
||||||
title=f"{self.core_config.server.name} | Login Gate",
|
title=f"{self.core_config.server.name} | Login Gate",
|
||||||
error=err,
|
error=err,
|
||||||
sesh=vars(usr_sesh),
|
sesh=vars(UserSession()),
|
||||||
).encode("utf-16")
|
))
|
||||||
|
resp.delete_cookie("DIANA_SESH")
|
||||||
|
return resp
|
||||||
|
|
||||||
async def render_POST(self, request: Request):
|
async def render_login(self, request: Request):
|
||||||
uri = request.uri.decode()
|
|
||||||
ip = Utils.get_ip_addr(request)
|
ip = Utils.get_ip_addr(request)
|
||||||
|
frm = await request.form()
|
||||||
|
access_code: str = frm.get("access_code", None)
|
||||||
|
if not access_code:
|
||||||
|
return RedirectResponse("/gate/?e=1", 303)
|
||||||
|
|
||||||
if uri == "/gate/gate.login":
|
passwd: bytes = frm.get("passwd", "").encode()
|
||||||
access_code: str = request.args[b"access_code"][0].decode()
|
|
||||||
passwd: bytes = request.args[b"passwd"][0]
|
|
||||||
if passwd == b"":
|
if passwd == b"":
|
||||||
passwd = None
|
passwd = None
|
||||||
|
|
||||||
uid = await self.data.card.get_user_id_from_card(access_code)
|
uid = await self.data.card.get_user_id_from_card(access_code)
|
||||||
user = await self.data.user.get_user(uid)
|
|
||||||
if uid is None:
|
if uid is None:
|
||||||
return redirectTo(b"/gate?e=1", request)
|
self.logger.debug(f"Failed to find user for card {access_code}")
|
||||||
|
return RedirectResponse("/gate/?e=1", 303)
|
||||||
|
|
||||||
|
user = await self.data.user.get_user(uid)
|
||||||
|
if user is None:
|
||||||
|
self.logger.error(f"Failed to load user {uid}")
|
||||||
|
return RedirectResponse("/gate/?e=1", 303)
|
||||||
|
|
||||||
if passwd is None:
|
if passwd is None:
|
||||||
sesh = await self.data.user.check_password(uid)
|
sesh = await self.data.user.check_password(uid)
|
||||||
|
|
||||||
if sesh is not None:
|
if sesh is not None:
|
||||||
return redirectTo(
|
return RedirectResponse(f"/gate/create?ac={access_code}")
|
||||||
f"/gate/create?ac={access_code}".encode(), request
|
|
||||||
)
|
|
||||||
return redirectTo(b"/gate?e=1", request)
|
|
||||||
|
|
||||||
if not self.data.user.check_password(uid, passwd):
|
return RedirectResponse("/gate/?e=1", 303)
|
||||||
return redirectTo(b"/gate?e=1", request)
|
|
||||||
|
if not await self.data.user.check_password(uid, passwd):
|
||||||
|
self.logger.debug(f"Failed password for access code {access_code}")
|
||||||
|
return RedirectResponse("/gate/?e=1", 303)
|
||||||
|
|
||||||
self.logger.info(f"Successful login of user {uid} at {ip}")
|
self.logger.info(f"Successful login of user {uid} at {ip}")
|
||||||
|
|
||||||
sesh = request.getSession()
|
sesh = UserSession()
|
||||||
usr_sesh = IUserSession(sesh)
|
sesh.user_id = uid
|
||||||
usr_sesh.userId = uid
|
sesh.current_ip = ip
|
||||||
usr_sesh.current_ip = ip
|
sesh.permissions = user['permissions']
|
||||||
usr_sesh.permissions = user['permissions']
|
|
||||||
|
|
||||||
return redirectTo(b"/user", request)
|
usr_sesh = self.encode_session(sesh)
|
||||||
|
self.logger.debug(f"Created session with JWT {usr_sesh}")
|
||||||
|
resp = RedirectResponse("/user/", 303)
|
||||||
|
resp.set_cookie("DIANA_SESH", usr_sesh)
|
||||||
|
|
||||||
elif uri == "/gate/gate.create":
|
return resp
|
||||||
access_code: str = request.args[b"access_code"][0].decode()
|
|
||||||
username: str = request.args[b"username"][0]
|
async def render_create(self, request: Request):
|
||||||
email: str = request.args[b"email"][0].decode()
|
ip = Utils.get_ip_addr(request)
|
||||||
passwd: bytes = request.args[b"passwd"][0]
|
access_code: str = request.query_params.get("access_code", "")
|
||||||
|
username: str = request.query_params.get("username", "")
|
||||||
|
email: str = request.query_params.get("email", "")
|
||||||
|
passwd: bytes = request.query_params.get("passwd", "").encode()
|
||||||
|
|
||||||
|
if not access_code or not username or not email or not passwd:
|
||||||
|
return RedirectResponse("/gate/?e=1", 303)
|
||||||
|
|
||||||
uid = await self.data.card.get_user_id_from_card(access_code)
|
uid = await self.data.card.get_user_id_from_card(access_code)
|
||||||
if uid is None:
|
if uid is None:
|
||||||
return redirectTo(b"/gate?e=1", request)
|
return RedirectResponse("/gate/?e=1", 303)
|
||||||
|
|
||||||
salt = bcrypt.gensalt()
|
salt = bcrypt.gensalt()
|
||||||
hashed = bcrypt.hashpw(passwd, salt)
|
hashed = bcrypt.hashpw(passwd, salt)
|
||||||
@ -221,67 +351,72 @@ class FE_Gate(FE_Base):
|
|||||||
uid, username, email.lower(), hashed.decode(), 1
|
uid, username, email.lower(), hashed.decode(), 1
|
||||||
)
|
)
|
||||||
if result is None:
|
if result is None:
|
||||||
return redirectTo(b"/gate?e=3", request)
|
return RedirectResponse("/gate/?e=3", 303)
|
||||||
|
|
||||||
if not self.data.user.check_password(uid, passwd):
|
if not await self.data.user.check_password(uid, passwd):
|
||||||
return redirectTo(b"/gate", request)
|
return RedirectResponse("/gate/", 303)
|
||||||
|
|
||||||
return redirectTo(b"/user", request)
|
sesh = UserSession()
|
||||||
|
sesh.user_id = uid
|
||||||
|
sesh.current_ip = ip
|
||||||
|
sesh.permissions = 1
|
||||||
|
|
||||||
else:
|
usr_sesh = self.encode_session(sesh)
|
||||||
return b""
|
self.logger.debug(f"Created session with JWT {usr_sesh}")
|
||||||
|
resp = RedirectResponse("/user", 303)
|
||||||
|
resp.set_cookie("DIANA_SESH", usr_sesh)
|
||||||
|
|
||||||
async def create_user(self, request: Request):
|
return resp
|
||||||
if b"ac" not in request.args or len(request.args[b"ac"][0].decode()) != 20:
|
|
||||||
return redirectTo(b"/gate?e=2", request)
|
async def render_create_get(self, request: Request):
|
||||||
|
ac = request.query_params.get(b"ac", [b""])[0].decode()
|
||||||
|
if len(ac) != 20:
|
||||||
|
return RedirectResponse("/gate/?e=2", 303)
|
||||||
|
|
||||||
ac = request.args[b"ac"][0].decode()
|
|
||||||
card = await self.data.card.get_card_by_access_code(ac)
|
card = await self.data.card.get_card_by_access_code(ac)
|
||||||
if card is None:
|
if card is None:
|
||||||
return redirectTo(b"/gate?e=1", request)
|
return RedirectResponse("/gate/?e=1", 303)
|
||||||
|
|
||||||
user = await self.data.user.get_user(card['user'])
|
user = await self.data.user.get_user(card['user'])
|
||||||
if user is None:
|
if user is None:
|
||||||
self.logger.warning(f"Card {ac} exists with no/invalid associated user ID {card['user']}")
|
self.logger.warning(f"Card {ac} exists with no/invalid associated user ID {card['user']}")
|
||||||
return redirectTo(b"/gate?e=0", request)
|
return RedirectResponse("/gate/?e=0", 303)
|
||||||
|
|
||||||
if user['password'] is not None:
|
if user['password'] is not None:
|
||||||
return redirectTo(b"/gate?e=1", request)
|
return RedirectResponse("/gate/?e=1", 303)
|
||||||
|
|
||||||
template = self.environment.get_template("core/frontend/gate/create.jinja")
|
template = self.environment.get_template("core/templates/gate/create.jinja")
|
||||||
return template.render(
|
return Response(template.render(
|
||||||
title=f"{self.core_config.server.name} | Create User",
|
title=f"{self.core_config.server.name} | Create User",
|
||||||
code=ac,
|
code=ac,
|
||||||
sesh={"userId": 0, "permissions": 0},
|
sesh={"user_id": 0, "permissions": 0},
|
||||||
).encode("utf-16")
|
))
|
||||||
|
|
||||||
|
|
||||||
class FE_User(FE_Base):
|
class FE_User(FE_Base):
|
||||||
async def render_GET(self, request: Request):
|
async def render_GET(self, request: Request):
|
||||||
uri = request.uri.decode()
|
uri = request.url.path
|
||||||
template = self.environment.get_template("core/frontend/user/index.jinja")
|
user_id = request.path_params.get('user_id', None)
|
||||||
|
self.logger.debug(f"{Utils.get_ip_addr(request)} -> {uri}")
|
||||||
|
template = self.environment.get_template("core/templates/user/index.jinja")
|
||||||
|
|
||||||
sesh: Session = request.getSession()
|
usr_sesh = self.validate_session(request)
|
||||||
usr_sesh = IUserSession(sesh)
|
if not usr_sesh:
|
||||||
if usr_sesh.userId == 0:
|
return RedirectResponse("/gate/", 303)
|
||||||
return redirectTo(b"/gate", request)
|
|
||||||
|
|
||||||
m = re.match("\/user\/(\d*)", uri)
|
if user_id:
|
||||||
|
if not self.test_perm(usr_sesh.permissions, PermissionOffset.USERMOD) and user_id != usr_sesh.user_id:
|
||||||
if m is not None:
|
self.logger.warn(f"User {usr_sesh.user_id} does not have permission to view user {user_id}")
|
||||||
usrid = m.group(1)
|
return RedirectResponse("/user/", 303)
|
||||||
if usr_sesh.permissions < 1 << PermissionOffset.USERMOD.value or not usrid == usr_sesh.userId:
|
|
||||||
return redirectTo(b"/user", request)
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
usrid = usr_sesh.userId
|
user_id = usr_sesh.user_id
|
||||||
|
|
||||||
user = await self.data.user.get_user(usrid)
|
user = await self.data.user.get_user(user_id)
|
||||||
if user is None:
|
if user is None:
|
||||||
return redirectTo(b"/user", request)
|
self.logger.debug(f"User {user_id} not found")
|
||||||
|
return RedirectResponse("/user/", 303)
|
||||||
|
|
||||||
cards = await self.data.card.get_user_cards(usrid)
|
cards = await self.data.card.get_user_cards(user_id)
|
||||||
arcades = await self.data.arcade.get_arcades_managed_by_user(usrid)
|
|
||||||
|
|
||||||
card_data = []
|
card_data = []
|
||||||
arcade_data = []
|
arcade_data = []
|
||||||
@ -294,176 +429,285 @@ class FE_User(FE_Base):
|
|||||||
else:
|
else:
|
||||||
status = 'Active'
|
status = 'Active'
|
||||||
|
|
||||||
card_data.append({'access_code': c['access_code'], 'status': status})
|
idm = c['idm']
|
||||||
|
ac = c['access_code']
|
||||||
|
|
||||||
for a in arcades:
|
if ac.startswith("5") or idm is not None:
|
||||||
arcade_data.append({'id': a['id'], 'name': a['name']})
|
c_type = "AmusementIC"
|
||||||
|
elif ac.startswith("3"):
|
||||||
|
c_type = "Banapass"
|
||||||
|
elif ac.startswith("010"):
|
||||||
|
c_type = "Unknown Aime"
|
||||||
|
desc, _ = self.data.card.get_aime_ac_key_desc(ac)
|
||||||
|
if desc is not None:
|
||||||
|
c_type = desc
|
||||||
|
else:
|
||||||
|
c_type = "Unknown"
|
||||||
|
|
||||||
return template.render(
|
card_data.append({'access_code': ac, 'status': status, 'chip_id': None if c['chip_id'] is None else f"{c['chip_id']:X}", 'idm': idm, 'type': c_type, "memo": ""})
|
||||||
|
|
||||||
|
if "e" in request.query_params:
|
||||||
|
try:
|
||||||
|
err = int(request.query_params.get("e", 0))
|
||||||
|
except Exception:
|
||||||
|
err = 0
|
||||||
|
|
||||||
|
else:
|
||||||
|
err = 0
|
||||||
|
|
||||||
|
if "s" in request.query_params:
|
||||||
|
try:
|
||||||
|
succ = int(request.query_params.get("s", 0))
|
||||||
|
except Exception:
|
||||||
|
succ = 0
|
||||||
|
|
||||||
|
else:
|
||||||
|
succ = 0
|
||||||
|
|
||||||
|
return Response(template.render(
|
||||||
title=f"{self.core_config.server.name} | Account",
|
title=f"{self.core_config.server.name} | Account",
|
||||||
sesh=vars(usr_sesh),
|
sesh=vars(usr_sesh),
|
||||||
cards=card_data,
|
cards=card_data,
|
||||||
|
error=err,
|
||||||
|
success=succ,
|
||||||
username=user['username'],
|
username=user['username'],
|
||||||
arcades=arcade_data
|
arcades=arcade_data
|
||||||
).encode("utf-16")
|
))
|
||||||
|
|
||||||
|
async def render_logout(self, request: Request):
|
||||||
|
resp = RedirectResponse("/gate/", 303)
|
||||||
|
resp.delete_cookie("DIANA_SESH")
|
||||||
|
return resp
|
||||||
|
|
||||||
|
async def edit_card(self, request: Request) -> RedirectResponse:
|
||||||
|
return RedirectResponse("/user/", 303)
|
||||||
|
|
||||||
|
async def add_card(self, request: Request) -> RedirectResponse:
|
||||||
|
return RedirectResponse("/user/", 303)
|
||||||
|
|
||||||
async def render_POST(self, request: Request):
|
async def render_POST(self, request: Request):
|
||||||
pass
|
frm = await request.form()
|
||||||
|
usr_sesh = self.validate_session(request)
|
||||||
|
if not usr_sesh or not self.test_perm(usr_sesh.permissions, PermissionOffset.USERMOD):
|
||||||
|
return RedirectResponse("/gate/", 303)
|
||||||
|
|
||||||
|
old_pw: str = frm.get('current_pw', None)
|
||||||
|
pw1: str = frm.get('password1', None)
|
||||||
|
pw2: str = frm.get('password2', None)
|
||||||
|
|
||||||
|
if old_pw is None or pw1 is None or pw2 is None:
|
||||||
|
return RedirectResponse("/user/?e=4", 303)
|
||||||
|
|
||||||
|
if pw1 != pw2:
|
||||||
|
return RedirectResponse("/user/?e=6", 303)
|
||||||
|
|
||||||
|
if not await self.data.user.check_password(usr_sesh.user_id, old_pw.encode()):
|
||||||
|
return RedirectResponse("/user/?e=5", 303)
|
||||||
|
|
||||||
|
if len(pw1) < 10 or not any(ele.isupper() for ele in pw1) or not any(ele.islower() for ele in pw1) \
|
||||||
|
or not any(ele.isdigit() for ele in pw1) or not any(not ele.isalnum() for ele in pw1):
|
||||||
|
return RedirectResponse("/user/?e=7", 303)
|
||||||
|
|
||||||
|
salt = bcrypt.gensalt()
|
||||||
|
hashed = bcrypt.hashpw(pw1.encode(), salt)
|
||||||
|
if not await self.data.user.change_password(usr_sesh.user_id, hashed.decode()):
|
||||||
|
return RedirectResponse("/gate/?e=1", 303)
|
||||||
|
|
||||||
|
return RedirectResponse("/user/?s=1", 303)
|
||||||
|
|
||||||
|
async def update_username(self, request: Request):
|
||||||
|
frm = await request.form()
|
||||||
|
new_name: bytes = frm.get('new_name', "")
|
||||||
|
usr_sesh = self.validate_session(request)
|
||||||
|
if not usr_sesh or not self.test_perm(usr_sesh.permissions, PermissionOffset.USERMOD):
|
||||||
|
return RedirectResponse("/gate/", 303)
|
||||||
|
|
||||||
|
if new_name is None or not new_name:
|
||||||
|
return RedirectResponse("/user/?e=4", 303)
|
||||||
|
|
||||||
|
if len(new_name) > 10:
|
||||||
|
return RedirectResponse("/user/?e=8", 303)
|
||||||
|
|
||||||
|
if not await self.data.user.change_username(usr_sesh.user_id, new_name):
|
||||||
|
return RedirectResponse("/user/?e=8", 303)
|
||||||
|
|
||||||
|
return RedirectResponse("/user/?s=2", 303)
|
||||||
|
|
||||||
class FE_System(FE_Base):
|
class FE_System(FE_Base):
|
||||||
async def render_GET(self, request: Request):
|
async def render_GET(self, request: Request):
|
||||||
uri = request.uri.decode()
|
template = self.environment.get_template("core/templates/sys/index.jinja")
|
||||||
template = self.environment.get_template("core/frontend/sys/index.jinja")
|
self.logger.debug(f"{Utils.get_ip_addr(request)} -> {request.url.path}")
|
||||||
|
|
||||||
|
usr_sesh = self.validate_session(request)
|
||||||
|
if not usr_sesh or not self.test_perm_minimum(usr_sesh.permissions, PermissionOffset.USERMOD):
|
||||||
|
return RedirectResponse("/gate/", 303)
|
||||||
|
|
||||||
|
return Response(template.render(
|
||||||
|
title=f"{self.core_config.server.name} | System",
|
||||||
|
sesh=vars(usr_sesh),
|
||||||
|
usrlist=[],
|
||||||
|
))
|
||||||
|
|
||||||
|
async def lookup_user(self, request: Request):
|
||||||
|
template = self.environment.get_template("core/templates/sys/index.jinja")
|
||||||
usrlist: List[Dict] = []
|
usrlist: List[Dict] = []
|
||||||
aclist: List[Dict] = []
|
usr_sesh = self.validate_session(request)
|
||||||
cablist: List[Dict] = []
|
if not usr_sesh or not self.test_perm(usr_sesh.permissions, PermissionOffset.USERMOD):
|
||||||
|
return RedirectResponse("/gate/", 303)
|
||||||
|
|
||||||
sesh: Session = request.getSession()
|
uid_search = request.query_params.get("usrId", None)
|
||||||
usr_sesh = IUserSession(sesh)
|
email_search = request.query_params.get("usrEmail", None)
|
||||||
if usr_sesh.userId == 0 or usr_sesh.permissions < 1 << PermissionOffset.USERMOD.value:
|
uname_search = request.query_params.get("usrName", None)
|
||||||
return redirectTo(b"/gate", request)
|
|
||||||
|
|
||||||
if uri.startswith("/sys/lookup.user?"):
|
if uid_search:
|
||||||
uri_parse = parse.parse_qs(uri.replace("/sys/lookup.user?", "")) # lop off the first bit
|
u = await self.data.user.get_user(uid_search)
|
||||||
uid_search = uri_parse.get("usrId")
|
|
||||||
email_search = uri_parse.get("usrEmail")
|
|
||||||
uname_search = uri_parse.get("usrName")
|
|
||||||
|
|
||||||
if uid_search is not None:
|
|
||||||
u = await self.data.user.get_user(uid_search[0])
|
|
||||||
if u is not None:
|
if u is not None:
|
||||||
usrlist.append(u._asdict())
|
usrlist.append(u._asdict())
|
||||||
|
|
||||||
elif email_search is not None:
|
elif email_search:
|
||||||
u = await self.data.user.find_user_by_email(email_search[0])
|
u = await self.data.user.find_user_by_email(email_search)
|
||||||
if u is not None:
|
if u is not None:
|
||||||
usrlist.append(u._asdict())
|
usrlist.append(u._asdict())
|
||||||
|
|
||||||
elif uname_search is not None:
|
elif uname_search:
|
||||||
ul = await self.data.user.find_user_by_username(uname_search[0])
|
ul = await self.data.user.find_user_by_username(uname_search)
|
||||||
for u in ul:
|
for u in ul:
|
||||||
usrlist.append(u._asdict())
|
usrlist.append(u._asdict())
|
||||||
|
|
||||||
elif uri.startswith("/sys/lookup.arcade?"):
|
return Response(template.render(
|
||||||
uri_parse = parse.parse_qs(uri.replace("/sys/lookup.arcade?", "")) # lop off the first bit
|
|
||||||
ac_id_search = uri_parse.get("arcadeId")
|
|
||||||
ac_name_search = uri_parse.get("arcadeName")
|
|
||||||
ac_user_search = uri_parse.get("arcadeUser")
|
|
||||||
ac_ip_search = uri_parse.get("arcadeIp")
|
|
||||||
|
|
||||||
if ac_id_search is not None:
|
|
||||||
u = await self.data.arcade.get_arcade(ac_id_search[0])
|
|
||||||
if u is not None:
|
|
||||||
aclist.append(u._asdict())
|
|
||||||
|
|
||||||
elif ac_name_search is not None:
|
|
||||||
ul = await self.data.arcade.get_arcade_by_name(ac_name_search[0])
|
|
||||||
if ul is not None:
|
|
||||||
for u in ul:
|
|
||||||
aclist.append(u._asdict())
|
|
||||||
|
|
||||||
elif ac_user_search is not None:
|
|
||||||
ul = await self.data.arcade.get_arcades_managed_by_user(ac_user_search[0])
|
|
||||||
if ul is not None:
|
|
||||||
for u in ul:
|
|
||||||
aclist.append(u._asdict())
|
|
||||||
|
|
||||||
elif ac_ip_search is not None:
|
|
||||||
ul = await self.data.arcade.get_arcades_by_ip(ac_ip_search[0])
|
|
||||||
if ul is not None:
|
|
||||||
for u in ul:
|
|
||||||
aclist.append(u._asdict())
|
|
||||||
|
|
||||||
elif uri.startswith("/sys/lookup.cab?"):
|
|
||||||
uri_parse = parse.parse_qs(uri.replace("/sys/lookup.cab?", "")) # lop off the first bit
|
|
||||||
cab_id_search = uri_parse.get("cabId")
|
|
||||||
cab_serial_search = uri_parse.get("cabSerial")
|
|
||||||
cab_acid_search = uri_parse.get("cabAcId")
|
|
||||||
|
|
||||||
if cab_id_search is not None:
|
|
||||||
u = await self.data.arcade.get_machine(id=cab_id_search[0])
|
|
||||||
if u is not None:
|
|
||||||
cablist.append(u._asdict())
|
|
||||||
|
|
||||||
elif cab_serial_search is not None:
|
|
||||||
u = await self.data.arcade.get_machine(serial=cab_serial_search[0])
|
|
||||||
if u is not None:
|
|
||||||
cablist.append(u._asdict())
|
|
||||||
|
|
||||||
elif cab_acid_search is not None:
|
|
||||||
ul = await self.data.arcade.get_arcade_machines(cab_acid_search[0])
|
|
||||||
for u in ul:
|
|
||||||
cablist.append(u._asdict())
|
|
||||||
|
|
||||||
return template.render(
|
|
||||||
title=f"{self.core_config.server.name} | System",
|
title=f"{self.core_config.server.name} | System",
|
||||||
sesh=vars(usr_sesh),
|
sesh=vars(usr_sesh),
|
||||||
usrlist=usrlist,
|
usrlist=usrlist,
|
||||||
aclist=aclist,
|
shoplist=[],
|
||||||
cablist=cablist,
|
))
|
||||||
).encode("utf-16")
|
|
||||||
|
async def lookup_shop(self, request: Request):
|
||||||
|
shoplist = []
|
||||||
|
template = self.environment.get_template("core/templates/sys/index.jinja")
|
||||||
|
|
||||||
|
usr_sesh = self.validate_session(request)
|
||||||
|
if not usr_sesh or not self.test_perm(usr_sesh.permissions, PermissionOffset.ACMOD):
|
||||||
|
return RedirectResponse("/gate/", 303)
|
||||||
|
|
||||||
|
shopid_search = request.query_params.get("shopId", None)
|
||||||
|
sn_search = request.query_params.get("serialNum", None)
|
||||||
|
|
||||||
|
if shopid_search:
|
||||||
|
if shopid_search.isdigit():
|
||||||
|
shopid_search = int(shopid_search)
|
||||||
|
try:
|
||||||
|
sinfo = await self.data.arcade.get_arcade(shopid_search)
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(f"Failed to fetch shop info for shop {shopid_search} in lookup_shop - {e}")
|
||||||
|
sinfo = None
|
||||||
|
if sinfo:
|
||||||
|
shoplist.append({
|
||||||
|
"name": sinfo.name,
|
||||||
|
"id": sinfo.allnet_id
|
||||||
|
})
|
||||||
|
|
||||||
|
else:
|
||||||
|
return Response(template.render(
|
||||||
|
title=f"{self.core_config.server.name} | System",
|
||||||
|
sesh=vars(usr_sesh),
|
||||||
|
usrlist=[],
|
||||||
|
shoplist=shoplist,
|
||||||
|
error=4
|
||||||
|
))
|
||||||
|
|
||||||
|
if sn_search:
|
||||||
|
sn_search = sn_search.upper().replace("-", "").strip()
|
||||||
|
if sn_search.isdigit() and len(sn_search) == 12:
|
||||||
|
prefix = sn_search[:4]
|
||||||
|
suffix = sn_search[5:]
|
||||||
|
|
||||||
|
netid_prefix = self.environment.globals["sn_cvt"].get(prefix, "")
|
||||||
|
sn_search = netid_prefix + suffix
|
||||||
|
|
||||||
|
if re.match("^AB[D|G|L]N\d{7}$", sn_search):
|
||||||
|
cabinfo = await self.data.arcade.get_machine(sn_search)
|
||||||
|
if cabinfo is None: sinfo = None
|
||||||
|
else:
|
||||||
|
sinfo = await self.data.arcade.get_arcade(cabinfo['arcade'])
|
||||||
|
if sinfo:
|
||||||
|
shoplist.append({
|
||||||
|
"name": sinfo['name'],
|
||||||
|
"id": sinfo['id']
|
||||||
|
})
|
||||||
|
|
||||||
|
else:
|
||||||
|
return Response(template.render(
|
||||||
|
title=f"{self.core_config.server.name} | System",
|
||||||
|
sesh=vars(usr_sesh),
|
||||||
|
usrlist=[],
|
||||||
|
shoplist=shoplist,
|
||||||
|
error=10
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
class FE_Game(FE_Base):
|
return Response(template.render(
|
||||||
isLeaf = False
|
title=f"{self.core_config.server.name} | System",
|
||||||
children: Dict[str, Any] = {}
|
sesh=vars(usr_sesh),
|
||||||
|
usrlist=[],
|
||||||
def getChild(self, name: bytes, request: Request):
|
shoplist=shoplist,
|
||||||
if name == b"":
|
))
|
||||||
return self
|
|
||||||
return resource.Resource.getChild(self, name, request)
|
|
||||||
|
|
||||||
async def render_GET(self, request: Request) -> bytes:
|
|
||||||
return redirectTo(b"/user", request)
|
|
||||||
|
|
||||||
|
|
||||||
class FE_Arcade(FE_Base):
|
class FE_Arcade(FE_Base):
|
||||||
async def render_GET(self, request: Request):
|
async def render_GET(self, request: Request):
|
||||||
uri = request.uri.decode()
|
template = self.environment.get_template("core/templates/arcade/index.jinja")
|
||||||
template = self.environment.get_template("core/frontend/arcade/index.jinja")
|
shop_id = request.path_params.get('shop_id', None)
|
||||||
managed = []
|
|
||||||
|
|
||||||
sesh: Session = request.getSession()
|
usr_sesh = self.validate_session(request)
|
||||||
usr_sesh = IUserSession(sesh)
|
if not usr_sesh or not self.test_perm(usr_sesh.permissions, PermissionOffset.ACMOD):
|
||||||
if usr_sesh.userId == 0:
|
self.logger.warn(f"User {usr_sesh.user_id} does not have permission to view shops!")
|
||||||
return redirectTo(b"/gate", request)
|
return RedirectResponse("/gate/", 303)
|
||||||
|
|
||||||
m = re.match("\/arcade\/(\d*)", uri)
|
if not shop_id:
|
||||||
|
return Response(template.render(
|
||||||
if m is not None:
|
|
||||||
arcadeid = m.group(1)
|
|
||||||
perms = await self.data.arcade.get_manager_permissions(usr_sesh.userId, arcadeid)
|
|
||||||
arcade = await self.data.arcade.get_arcade(arcadeid)
|
|
||||||
|
|
||||||
if perms is None:
|
|
||||||
perms = 0
|
|
||||||
|
|
||||||
else:
|
|
||||||
return redirectTo(b"/user", request)
|
|
||||||
|
|
||||||
return template.render(
|
|
||||||
title=f"{self.core_config.server.name} | Arcade",
|
title=f"{self.core_config.server.name} | Arcade",
|
||||||
sesh=vars(usr_sesh),
|
sesh=vars(usr_sesh),
|
||||||
error=0,
|
))
|
||||||
perms=perms,
|
|
||||||
arcade=arcade._asdict()
|
|
||||||
).encode("utf-16")
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
sinfo = await self.data.arcade.get_arcade(shop_id)
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(f"Failed to fetch shop info for shop {shop_id} in render_GET - {e}")
|
||||||
|
sinfo = None
|
||||||
|
if not sinfo:
|
||||||
|
return Response(template.render(
|
||||||
|
title=f"{self.core_config.server.name} | Arcade",
|
||||||
|
sesh=vars(usr_sesh),
|
||||||
|
))
|
||||||
|
|
||||||
|
return Response(template.render(
|
||||||
|
title=f"{self.core_config.server.name} | Arcade",
|
||||||
|
sesh=vars(usr_sesh),
|
||||||
|
arcade={
|
||||||
|
"name": sinfo['name'],
|
||||||
|
"id": sinfo['id'],
|
||||||
|
"cabs": []
|
||||||
|
}
|
||||||
|
|
||||||
|
))
|
||||||
|
|
||||||
class FE_Machine(FE_Base):
|
class FE_Machine(FE_Base):
|
||||||
async def render_GET(self, request: Request):
|
async def render_GET(self, request: Request):
|
||||||
uri = request.uri.decode()
|
template = self.environment.get_template("core/templates/machine/index.jinja")
|
||||||
template = self.environment.get_template("core/frontend/machine/index.jinja")
|
cab_id = request.path_params.get('cab_id', None)
|
||||||
|
|
||||||
sesh: Session = request.getSession()
|
usr_sesh = self.validate_session(request)
|
||||||
usr_sesh = IUserSession(sesh)
|
if not usr_sesh or not self.test_perm(usr_sesh.permissions, PermissionOffset.ACMOD):
|
||||||
if usr_sesh.userId == 0:
|
self.logger.warn(f"User {usr_sesh.user_id} does not have permission to view shops!")
|
||||||
return redirectTo(b"/gate", request)
|
return RedirectResponse("/gate/", 303)
|
||||||
|
|
||||||
return template.render(
|
if not cab_id:
|
||||||
|
return Response(template.render(
|
||||||
title=f"{self.core_config.server.name} | Machine",
|
title=f"{self.core_config.server.name} | Machine",
|
||||||
sesh=vars(usr_sesh),
|
sesh=vars(usr_sesh),
|
||||||
arcade={},
|
))
|
||||||
error=0,
|
|
||||||
).encode("utf-16")
|
return Response(template.render(
|
||||||
|
title=f"{self.core_config.server.name} | Machine",
|
||||||
|
sesh=vars(usr_sesh),
|
||||||
|
arcade={}
|
||||||
|
))
|
@ -1,4 +0,0 @@
|
|||||||
{% extends "core/frontend/index.jinja" %}
|
|
||||||
{% block content %}
|
|
||||||
<h1>{{ arcade.name }}</h1>
|
|
||||||
{% endblock content %}
|
|
@ -1,5 +0,0 @@
|
|||||||
{% extends "core/frontend/index.jinja" %}
|
|
||||||
{% block content %}
|
|
||||||
{% include "core/frontend/widgets/err_banner.jinja" %}
|
|
||||||
<h1>Machine Management</h1>
|
|
||||||
{% endblock content %}
|
|
@ -1,103 +0,0 @@
|
|||||||
{% extends "core/frontend/index.jinja" %}
|
|
||||||
{% block content %}
|
|
||||||
<h1>System Management</h1>
|
|
||||||
|
|
||||||
<div class="row" id="rowForm">
|
|
||||||
{% if sesh.permissions >= 2 %}
|
|
||||||
<div class="col-sm-6" style="max-width: 25%;">
|
|
||||||
<form id="usrLookup" name="usrLookup" action="/sys/lookup.user" class="form-inline">
|
|
||||||
<h3>User Search</h3>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="usrId">User ID</label>
|
|
||||||
<input type="number" class="form-control" id="usrId" name="usrId">
|
|
||||||
</div>
|
|
||||||
OR
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="usrName">Username</label>
|
|
||||||
<input type="text" class="form-control" id="usrName" name="usrName">
|
|
||||||
</div>
|
|
||||||
OR
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="usrEmail">Email address</label>
|
|
||||||
<input type="email" class="form-control" id="usrEmail" name="usrEmail" aria-describedby="emailHelp">
|
|
||||||
</div>
|
|
||||||
<br />
|
|
||||||
<button type="submit" class="btn btn-primary">Search</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
{% if sesh.permissions >= 4 %}
|
|
||||||
<div class="col-sm-6" style="max-width: 25%;">
|
|
||||||
<form id="arcadeLookup" name="arcadeLookup" action="/sys/lookup.arcade" class="form-inline" >
|
|
||||||
<h3>Arcade Search</h3>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="arcadeId">Arcade ID</label>
|
|
||||||
<input type="number" class="form-control" id="arcadeId" name="arcadeId">
|
|
||||||
</div>
|
|
||||||
OR
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="arcadeName">Arcade Name</label>
|
|
||||||
<input type="text" class="form-control" id="arcadeName" name="arcadeName">
|
|
||||||
</div>
|
|
||||||
OR
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="arcadeUser">Owner User ID</label>
|
|
||||||
<input type="number" class="form-control" id="arcadeUser" name="arcadeUser">
|
|
||||||
</div>
|
|
||||||
OR
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="arcadeIp">Assigned IP Address</label>
|
|
||||||
<input type="text" class="form-control" id="arcadeIp" name="arcadeIp">
|
|
||||||
</div>
|
|
||||||
<br />
|
|
||||||
<button type="submit" class="btn btn-primary">Search</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
<div class="col-sm-6" style="max-width: 25%;">
|
|
||||||
<form id="cabLookup" name="cabLookup" action="/sys/lookup.cab" class="form-inline" >
|
|
||||||
<h3>Machine Search</h3>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="cabId">Machine ID</label>
|
|
||||||
<input type="number" class="form-control" id="cabId" name="cabId">
|
|
||||||
</div>
|
|
||||||
OR
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="cabSerial">Machine Serial</label>
|
|
||||||
<input type="text" class="form-control" id="cabSerial" name="cabSerial">
|
|
||||||
</div>
|
|
||||||
OR
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="cabAcId">Arcade ID</label>
|
|
||||||
<input type="number" class="form-control" id="cabAcId" name="cabAcId">
|
|
||||||
</div>
|
|
||||||
<br />
|
|
||||||
<button type="submit" class="btn btn-primary">Search</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
<div class="row" id="rowResult" style="margin: 10px;">
|
|
||||||
{% if sesh.permissions >= 2 %}
|
|
||||||
<div id="userSearchResult" class="col-sm-6" style="max-width: 25%;">
|
|
||||||
{% for usr in usrlist %}
|
|
||||||
<a href=/user/{{ usr.id }}><pre>{{ usr.id }} | {{ usr.username if usr.username != None else "<i>No Name Set</i>"}}</pre></a>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
{% if sesh.permissions >= 4 %}
|
|
||||||
<div id="arcadeSearchResult" class="col-sm-6" style="max-width: 25%;">
|
|
||||||
{% for ac in aclist %}
|
|
||||||
<pre><a href=/arcade/{{ ac.id }}>{{ ac.id }} | {{ ac.name if ac.name != None else "<i>No Name Set</i>" }} | {{ ac.ip if ac.ip != None else "<i>No IP Assigned</i>"}}</pre></a>
|
|
||||||
{% endfor %}
|
|
||||||
</div
|
|
||||||
><div id="cabSearchResult" class="col-sm-6" style="max-width: 25%;">
|
|
||||||
{% for cab in cablist %}
|
|
||||||
<a href=/cab/{{ cab.id }}><pre>{{ cab.id }} | {{ cab.game if cab.game != None else "<i>ANY </i>" }} | {{ cab.serial }}</pre></a>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
<div class="row" id="rowAdd">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
{% endblock content %}
|
|
@ -1,41 +0,0 @@
|
|||||||
{% extends "core/frontend/index.jinja" %}
|
|
||||||
{% block content %}
|
|
||||||
<h1>Management for {{ username }}</h1>
|
|
||||||
<h2>Cards <button class="btn btn-success" data-bs-toggle="modal" data-bs-target="#card_add">Add</button></h2>
|
|
||||||
<ul style="font-size: 20px;">
|
|
||||||
{% for c in cards %}
|
|
||||||
<li>{{ c.access_code }}: {{ c.status }} {% if c.status == 'Active'%}<button class="btn-warning btn">Lock</button>{% elif c.status == 'Locked' %}<button class="btn-warning btn">Unlock</button>{% endif %} <button class="btn-danger btn">Delete</button></li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
{% if arcades is defined %}
|
|
||||||
<h2>Arcades</h2>
|
|
||||||
<ul style="font-size: 20px;">
|
|
||||||
{% for a in arcades %}
|
|
||||||
<li><a href=/arcade/{{ a.id }}>{{ a.name }}</a></li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<div class="modal fade" id="card_add" tabindex="-1" aria-labelledby="card_add_label" aria-hidden="true">
|
|
||||||
<div class="modal-dialog">
|
|
||||||
<div class="modal-content">
|
|
||||||
<div class="modal-header">
|
|
||||||
<h1 class="modal-title fs-5" id="card_add_label">Add Card</h1>
|
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
||||||
</div>
|
|
||||||
<div class="modal-body">
|
|
||||||
HOW TO:<br>
|
|
||||||
Scan your card on any networked game and press the "View Access Code" button (varies by game) and enter the 20 digit code below.<br>
|
|
||||||
!!FOR AMUSEIC CARDS: DO NOT ENTER THE CODE SHOWN ON THE BACK OF THE CARD ITSELF OR IT WILL NOT WORK!!
|
|
||||||
<p /><label for="card_add_frm_access_code">Access Code: </label><input id="card_add_frm_access_code" maxlength="20" type="text" required>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button type="button" class="btn btn-primary">Add</button>
|
|
||||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% endblock content %}
|
|
@ -1,18 +0,0 @@
|
|||||||
{% if error > 0 %}
|
|
||||||
<div class="err-banner">
|
|
||||||
<h3>Error</h3>
|
|
||||||
{% if error == 1 %}
|
|
||||||
Card not registered, or wrong password
|
|
||||||
{% elif error == 2 %}
|
|
||||||
Missing or malformed access code
|
|
||||||
{% elif error == 3 %}
|
|
||||||
Failed to create user
|
|
||||||
{% elif error == 4 %}
|
|
||||||
Arcade not found
|
|
||||||
{% elif error == 5 %}
|
|
||||||
Machine not found
|
|
||||||
{% else %}
|
|
||||||
An unknown error occoured
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
4
core/templates/arcade/index.jinja
Normal file
4
core/templates/arcade/index.jinja
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{% extends "core/templates/index.jinja" %}
|
||||||
|
{% block content %}
|
||||||
|
<h1>{{ arcade.name }}</h1>
|
||||||
|
{% endblock content %}
|
@ -1,4 +1,4 @@
|
|||||||
{% extends "core/frontend/index.jinja" %}
|
{% extends "core/templates/index.jinja" %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h1>Create User</h1>
|
<h1>Create User</h1>
|
||||||
<form id="create" style="max-width: 240px; min-width: 10%;" action="/gate/gate.create" method="post">
|
<form id="create" style="max-width: 240px; min-width: 10%;" action="/gate/gate.create" method="post">
|
@ -1,7 +1,7 @@
|
|||||||
{% extends "core/frontend/index.jinja" %}
|
{% extends "core/templates/index.jinja" %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h1>Gate</h1>
|
<h1>Gate</h1>
|
||||||
{% include "core/frontend/widgets/err_banner.jinja" %}
|
{% include "core/templates/widgets/err_banner.jinja" %}
|
||||||
<style>
|
<style>
|
||||||
/* Chrome, Safari, Edge, Opera */
|
/* Chrome, Safari, Edge, Opera */
|
||||||
input::-webkit-outer-spin-button,
|
input::-webkit-outer-spin-button,
|
@ -84,7 +84,7 @@
|
|||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
{% include "core/frontend/widgets/topbar.jinja" %}
|
{% include "core/templates/widgets/topbar.jinja" %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h1>{{ server_name }}</h1>
|
<h1>{{ server_name }}</h1>
|
||||||
{% endblock content %}
|
{% endblock content %}
|
5
core/templates/machine/index.jinja
Normal file
5
core/templates/machine/index.jinja
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{% extends "core/templates/index.jinja" %}
|
||||||
|
{% block content %}
|
||||||
|
{% include "core/templates/widgets/err_banner.jinja" %}
|
||||||
|
<h1>Machine Management</h1>
|
||||||
|
{% endblock content %}
|
69
core/templates/sys/index.jinja
Normal file
69
core/templates/sys/index.jinja
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
{% extends "core/templates/index.jinja" %}
|
||||||
|
{% block content %}
|
||||||
|
<h1>System Management</h1>
|
||||||
|
{% if error is defined %}
|
||||||
|
{% include "core/templates/widgets/err_banner.jinja" %}
|
||||||
|
{% endif %}
|
||||||
|
<div class="row" id="rowForm">
|
||||||
|
{% if "{:08b}".format(sesh.permissions)[6] == "1" %}
|
||||||
|
<div class="col-sm-6" style="max-width: 25%;">
|
||||||
|
<form id="usrLookup" name="usrLookup" action="/sys/lookup.user" class="form-inline">
|
||||||
|
<h3>User Search</h3>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="usrId">User ID</label>
|
||||||
|
<input type="number" class="form-control" id="usrId" name="usrId">
|
||||||
|
</div>
|
||||||
|
OR
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="usrName">Username</label>
|
||||||
|
<input type="text" class="form-control" id="usrName" name="usrName">
|
||||||
|
</div>
|
||||||
|
OR
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="usrEmail">Email address</label>
|
||||||
|
<input type="email" class="form-control" id="usrEmail" name="usrEmail" aria-describedby="emailHelp">
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<button type="submit" class="btn btn-primary">Search</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% if "{:08b}".format(sesh.permissions)[5] == "1" %}
|
||||||
|
<div class="col-sm-6" style="max-width: 25%;">
|
||||||
|
<form id="shopLookup" name="shopLookup" action="/sys/lookup.shop" class="form-inline">
|
||||||
|
<h3>Shop search</h3>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="shopId">Shop ID</label>
|
||||||
|
<input type="number" class="form-control" id="shopId" name="shopId">
|
||||||
|
</div>
|
||||||
|
OR
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="serialNum">Serial Number</label>
|
||||||
|
<input type="text" class="form-control" id="serialNum" name="serialNum" maxlength="12">
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<button type="submit" class="btn btn-primary">Search</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<div class="row" id="rowResult" style="margin: 10px;">
|
||||||
|
{% if "{:08b}".format(sesh.permissions)[6] == "1" %}
|
||||||
|
<div id="userSearchResult" class="col-sm-6" style="max-width: 25%;">
|
||||||
|
{% for usr in usrlist %}
|
||||||
|
<a href=/user/{{ usr.id }}><pre>{{ usr.username if usr.username is not none else "<i>No Name Set</i>"}}</pre></a>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% if "{:08b}".format(sesh.permissions)[5] == "1" %}
|
||||||
|
<div id="shopSearchResult" class="col-sm-6" style="max-width: 25%;">
|
||||||
|
{% for shop in shoplist %}
|
||||||
|
<a href="/shop/{{ shop.id }}"><pre>{{ shop.name if shop.name else "<i>No Name Set</i>"}}</pre></a>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<div class="row" id="rowAdd">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
{% endblock content %}
|
175
core/templates/user/index.jinja
Normal file
175
core/templates/user/index.jinja
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
{% extends "core/templates/index.jinja" %}
|
||||||
|
{% block content %}
|
||||||
|
<script type="text/javascript">
|
||||||
|
function toggle_new_name_form() {
|
||||||
|
let frm = document.getElementById("new_name_form");
|
||||||
|
let btn = document.getElementById("btn_toggle_form");
|
||||||
|
|
||||||
|
if (frm.style['display'] != "") {
|
||||||
|
frm.style['display'] = "";
|
||||||
|
frm.style['max-height'] = "";
|
||||||
|
btn.innerText = "Cancel";
|
||||||
|
} else {
|
||||||
|
frm.style['display'] = "none";
|
||||||
|
frm.style['max-height'] = "0px";
|
||||||
|
btn.innerText = "Edit";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function toggle_add_card_form() {
|
||||||
|
let btn = document.getElementById("btn_add_card");
|
||||||
|
let dv = document.getElementById("add_card_container")
|
||||||
|
|
||||||
|
if (dv.style['display'] != "") {
|
||||||
|
btn.innerText = "Cancel";
|
||||||
|
dv.style['display'] = "";
|
||||||
|
} else {
|
||||||
|
btn.innerText = "Add";
|
||||||
|
dv.style['display'] = "none";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function prep_edit_form(access_code, chip_id, idm, card_type, u_memo) {
|
||||||
|
ac = document.getElementById("card_edit_frm_access_code");
|
||||||
|
cid = document.getElementById("card_edit_frm_chip_id");
|
||||||
|
fidm = document.getElementById("card_edit_frm_idm");
|
||||||
|
memo = document.getElementById("card_edit_frm_memo");
|
||||||
|
|
||||||
|
if (chip_id == "None" || chip_id == undefined) {
|
||||||
|
chip_id = ""
|
||||||
|
}
|
||||||
|
if (idm == "None" || idm == undefined) {
|
||||||
|
idm = ""
|
||||||
|
}
|
||||||
|
if (u_memo == "None" || u_memo == undefined) {
|
||||||
|
u_memo = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
ac.value = access_code;
|
||||||
|
cid.value = chip_id;
|
||||||
|
fidm.value = idm;
|
||||||
|
memo.value = u_memo;
|
||||||
|
|
||||||
|
if (card_type == "AmusementIC") {
|
||||||
|
cid.disabled = true;
|
||||||
|
fidm.disabled = false;
|
||||||
|
} else {
|
||||||
|
cid.disabled = false;
|
||||||
|
fidm.disabled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<h1>Management for {{ username }} <button onclick="toggle_new_name_form()" class="btn btn-secondary" id="btn_toggle_form">Edit</button></h1>
|
||||||
|
{% if error is defined %}
|
||||||
|
{% include "core/templates/widgets/err_banner.jinja" %}
|
||||||
|
{% endif %}
|
||||||
|
{% if success is defined and success == 2 %}
|
||||||
|
<div style="background-color: #00AA00; padding: 20px; margin-bottom: 10px; width: 15%;">
|
||||||
|
Update successful
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<form style="max-width: 33%; display: none; max-height: 0px;" action="/user/update.name" method="post" id="new_name_form">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="new_name" class="form-label">New Nickname</label>
|
||||||
|
<input type="text" class="form-control" id="new_name" name="new_name" aria-describedby="new_name_help">
|
||||||
|
<div id="new_name_help" class="form-text">Must be 10 characters or less</div>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary">Submit</button>
|
||||||
|
</form>
|
||||||
|
<p></p>
|
||||||
|
<h2>Cards <button class="btn btn-success" id="btn_add_card" onclick="toggle_add_card_form()">Add</button></h2>
|
||||||
|
{% if success is defined and success == 3 %}
|
||||||
|
<div style="background-color: #00AA00; padding: 20px; margin-bottom: 10px; width: 15%;">
|
||||||
|
Card added successfully
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<div id="add_card_container" style="display: none; max-width: 33%;">
|
||||||
|
<form action="/user/add.card" method="post", id="frm_add_card">
|
||||||
|
<label class="form-label" for="card_add_frm_access_code">Access Code:</label>
|
||||||
|
<input class="form-control" name="add_access_code" id="card_add_frm_access_code" maxlength="20" type="text" required aria-describedby="ac_help">
|
||||||
|
<div id="ac_help" class="form-text">20 digit code on the back of the card.</div>
|
||||||
|
<button type="submit" class="btn btn-primary">Add</button>
|
||||||
|
</form>
|
||||||
|
<br>
|
||||||
|
</div>
|
||||||
|
<ul style="font-size: 20px;">
|
||||||
|
{% for c in cards %}
|
||||||
|
<li>{{ c.access_code }} ({{ c.type}}): {{ c.status }} <button onclick="prep_edit_form('{{ c.access_code }}', '{{ c.chip_id}}', '{{ c.idm }}', '{{ c.type }}', '{{ c.memo }}')" data-bs-toggle="modal" data-bs-target="#card_edit" class="btn btn-secondary" id="btn_edit_card_{{ c.access_code }}">Edit</button> {% if c.status == 'Active'%}<button class="btn-warning btn">Lock</button>{% elif c.status == 'Locked' %}<button class="btn-warning btn">Unlock</button>{% endif %} <button class="btn-danger btn">Delete</button></li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>Reset Password</h2>
|
||||||
|
{% if success is defined and success == 1 %}
|
||||||
|
<div style="background-color: #00AA00; padding: 20px; margin-bottom: 10px; width: 15%;">
|
||||||
|
Update successful
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<form style="max-width: 33%;" action="/user/update.pw" method="post">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="current_pw" class="form-label">Current Password</label>
|
||||||
|
<input type="password" class="form-control" id="current_pw" name="current_pw">
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="password1" class="form-label">New Password</label>
|
||||||
|
<input type="password" class="form-control" id="password1" name="password1" aria-describedby="password_help">
|
||||||
|
<div id="password_help" class="form-text">Password must be at least 10 characters long, contain an upper and lowercase character, number, and special character</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="password2" class="form-label">Retype New Password</label>
|
||||||
|
<input type="password" class="form-control" id="password2" name="password2">
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary">Submit</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
{% if arcades is defined and arcades|length > 0 %}
|
||||||
|
<h2>Arcades</h2>
|
||||||
|
<ul>
|
||||||
|
{% for a in arcades %}
|
||||||
|
<li><h3>{{ a.name }}</h3>
|
||||||
|
{% if a.machines|length > 0 %}
|
||||||
|
<table>
|
||||||
|
<tr><th>Serial</th><th>Game</th><th>Last Seen</th></tr>
|
||||||
|
{% for m in a.machines %}
|
||||||
|
<tr><td>{{ m.serial }}</td><td>{{ m.game }}</td><td>{{ m.last_seen }}</td></tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
{% endif %}
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<div class="modal fade" id="card_edit" tabindex="-1" aria-labelledby="card_edit_label" aria-hidden="true">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h1 class="modal-title fs-5" id="card_edit_label">Edit Card</h1>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form action="/user/edit.card" method="post", id="frm_edit_card">
|
||||||
|
<label class="form-label" for="card_edit_frm_access_code">Access Code:</label>
|
||||||
|
<input class="form-control" readonly name="add_access_code" id="card_edit_frm_access_code" maxlength="20" type="text" required aria-describedby="ac_help">
|
||||||
|
<div id="ac_help" class="form-text">20 digit code on the back of the card. If this is incorrect, contact a sysadmin.</div>
|
||||||
|
|
||||||
|
<label class="form-label" for="card_edit_frm_memo" id="card_edit_frm_memo_lbl">Memo:</label>
|
||||||
|
<input class="form-control" aria-describedby="memo_help" name="add_memo" id="card_edit_frm_memo" maxlength="16" type="text">
|
||||||
|
<div id="memo_help" class="form-text">Must be 16 characters or less.</div>
|
||||||
|
|
||||||
|
<label class="form-label" for="card_edit_frm_idm" id="card_edit_frm_idm_lbl">FeliCa IDm:</label>
|
||||||
|
<input class="form-control" aria-describedby="idm_help" name="add_felica_idm" id="card_edit_frm_idm" maxlength="16" type="text">
|
||||||
|
<div id="idm_help" class="form-text">8 bytes that uniquly idenfites a FeliCa card. Obtained by reading the card with an NFC reader.</div>
|
||||||
|
|
||||||
|
<label class="form-label" for="card_edit_frm_chip_id" id="card_edit_frm_chip_id_lbl">Mifare UID:</label>
|
||||||
|
<input class="form-control" aria-describedby="chip_id_help" name="add_mifare_chip_id" id="card_edit_frm_chip_id" maxlength="8" type="text">
|
||||||
|
<div id="chip_id_help" class="form-text">4 byte integer that uniquly identifies a Mifare card. Obtained by reading the card with an NFC reader.</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="submit" class="btn btn-primary" form="frm_edit_card">Edit</button>
|
||||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock content %}
|
29
core/templates/widgets/err_banner.jinja
Normal file
29
core/templates/widgets/err_banner.jinja
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
{% if error > 0 %}
|
||||||
|
<div class="err-banner">
|
||||||
|
<h3>Error</h3>
|
||||||
|
{% if error == 1 %}
|
||||||
|
Card not registered, or wrong password
|
||||||
|
{% elif error == 2 %}
|
||||||
|
Missing or malformed access code
|
||||||
|
{% elif error == 3 %}
|
||||||
|
Failed to create user
|
||||||
|
{% elif error == 4 %}
|
||||||
|
Required field not filled or invalid
|
||||||
|
{% elif error == 5 %}
|
||||||
|
Incorrect old password
|
||||||
|
{% elif error == 6 %}
|
||||||
|
Passwords don't match
|
||||||
|
{% elif error == 7 %}
|
||||||
|
New password not acceptable
|
||||||
|
{% elif error == 8 %}
|
||||||
|
New Nickname too long
|
||||||
|
{% elif error == 9 %}
|
||||||
|
You must be logged in to preform this action
|
||||||
|
New Nickname too long
|
||||||
|
{% elif error == 10 %}
|
||||||
|
Invalid serial number
|
||||||
|
{% else %}
|
||||||
|
An unknown error occoured
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
@ -3,19 +3,20 @@
|
|||||||
</div>
|
</div>
|
||||||
<div style="background: #333; color: #f9f9f9; width: 80%; height: 50px; line-height: 50px; padding-left: 10px; float: left;">
|
<div style="background: #333; color: #f9f9f9; width: 80%; height: 50px; line-height: 50px; padding-left: 10px; float: left;">
|
||||||
<a href=/><button class="btn btn-primary">Home</button></a>
|
<a href=/><button class="btn btn-primary">Home</button></a>
|
||||||
{% for game in game_list %}
|
{% for game, data in game_list|items %}
|
||||||
<a href=/game/{{ game.url }}><button class="btn btn-success">{{ game.name }}</button></a>
|
<a href=/game{{ data.url }}/><button class="btn btn-success">{{ game }}</button></a>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div style="background: #333; color: #f9f9f9; width: 10%; height: 50px; line-height: 50px; text-align: center; float: left;">
|
<div style="background: #333; color: #f9f9f9; width: 10%; height: 50px; line-height: 50px; text-align: center; float: left;">
|
||||||
{% if sesh is defined and sesh["permissions"] >= 2 %}
|
{% if sesh is defined and sesh["permissions"] >= 2 %}
|
||||||
<a href="/sys"><button class="btn btn-primary">System</button></a>
|
<a href="/sys/"><button class="btn btn-primary">System</button></a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if sesh is defined and sesh["userId"] > 0 %}
|
{% if sesh is defined and sesh["user_id"] > 0 %}
|
||||||
<a href="/user"><button class="btn btn-primary">Account</button></a>
|
<a href="/user/"><button class="btn btn-primary">Account</button></a>
|
||||||
|
<a href="/user/logout"><button class="btn btn-danger">Logout</button></a>
|
||||||
{% else %}
|
{% else %}
|
||||||
<a href="/gate"><button class="btn btn-primary">Gate</button></a>
|
<a href="/gate/"><button class="btn btn-primary">Gate</button></a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
</div>
|
</div>
|
@ -1,12 +1,13 @@
|
|||||||
import json
|
import json
|
||||||
|
from typing import List
|
||||||
|
from starlette.routing import Route
|
||||||
|
from starlette.responses import Response, RedirectResponse
|
||||||
import yaml
|
import yaml
|
||||||
import jinja2
|
import jinja2
|
||||||
from os import path
|
from os import path
|
||||||
from twisted.web.util import redirectTo
|
|
||||||
from starlette.requests import Request
|
from starlette.requests import Request
|
||||||
from twisted.web.server import Session
|
|
||||||
|
|
||||||
from core.frontend import FE_Base, IUserSession
|
from core.frontend import FE_Base, UserSession
|
||||||
from core.config import CoreConfig
|
from core.config import CoreConfig
|
||||||
from titles.idac.database import IDACData
|
from titles.idac.database import IDACData
|
||||||
from titles.idac.schema.profile import *
|
from titles.idac.schema.profile import *
|
||||||
@ -26,7 +27,8 @@ class IDACFrontend(FE_Base):
|
|||||||
self.game_cfg.update(
|
self.game_cfg.update(
|
||||||
yaml.safe_load(open(f"{cfg_dir}/{IDACConstants.CONFIG_NAME}"))
|
yaml.safe_load(open(f"{cfg_dir}/{IDACConstants.CONFIG_NAME}"))
|
||||||
)
|
)
|
||||||
self.nav_name = "頭文字D THE ARCADE"
|
#self.nav_name = "頭文字D THE ARCADE"
|
||||||
|
self.nav_name = "IDAC"
|
||||||
# TODO: Add version list
|
# TODO: Add version list
|
||||||
self.version = IDACConstants.VER_IDAC_SEASON_2
|
self.version = IDACConstants.VER_IDAC_SEASON_2
|
||||||
|
|
||||||
@ -37,6 +39,11 @@ class IDACFrontend(FE_Base):
|
|||||||
34: "full_tune_fragments",
|
34: "full_tune_fragments",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def get_routes(self) -> List[Route]:
|
||||||
|
return [
|
||||||
|
Route("/", self.render_GET)
|
||||||
|
]
|
||||||
|
|
||||||
async def generate_all_tables_json(self, user_id: int):
|
async def generate_all_tables_json(self, user_id: int):
|
||||||
json_export = {}
|
json_export = {}
|
||||||
|
|
||||||
@ -87,35 +94,32 @@ class IDACFrontend(FE_Base):
|
|||||||
return json.dumps(json_export, indent=4, default=str, ensure_ascii=False)
|
return json.dumps(json_export, indent=4, default=str, ensure_ascii=False)
|
||||||
|
|
||||||
async def render_GET(self, request: Request) -> bytes:
|
async def render_GET(self, request: Request) -> bytes:
|
||||||
uri: str = request.uri.decode()
|
uri: str = request.url.path
|
||||||
|
|
||||||
template = self.environment.get_template(
|
template = self.environment.get_template(
|
||||||
"titles/idac/frontend/idac_index.jinja"
|
"titles/idac/templates/idac_index.jinja"
|
||||||
)
|
)
|
||||||
sesh: Session = request.getSession()
|
usr_sesh = self.validate_session(request)
|
||||||
usr_sesh = IUserSession(sesh)
|
if not usr_sesh:
|
||||||
user_id = usr_sesh.userId
|
usr_sesh = UserSession()
|
||||||
|
user_id = usr_sesh.user_id
|
||||||
# user_id = usr_sesh.user_id
|
# user_id = usr_sesh.user_id
|
||||||
|
|
||||||
# profile export
|
# profile export
|
||||||
if uri.startswith("/game/idac/export"):
|
if uri.startswith("/game/idac/export"):
|
||||||
if user_id == 0:
|
if user_id == 0:
|
||||||
return redirectTo(b"/game/idac", request)
|
return RedirectResponse(b"/game/idac", request)
|
||||||
|
|
||||||
# set the file name, content type and size to download the json
|
# set the file name, content type and size to download the json
|
||||||
content = await self.generate_all_tables_json(user_id).encode("utf-8")
|
content = await self.generate_all_tables_json(user_id).encode("utf-8")
|
||||||
request.responseHeaders.addRawHeader(
|
|
||||||
b"content-type", b"application/octet-stream"
|
|
||||||
)
|
|
||||||
request.responseHeaders.addRawHeader(
|
|
||||||
b"content-disposition", b"attachment; filename=idac_profile.json"
|
|
||||||
)
|
|
||||||
request.responseHeaders.addRawHeader(
|
|
||||||
b"content-length", str(len(content)).encode("utf-8")
|
|
||||||
)
|
|
||||||
|
|
||||||
self.logger.info(f"User {user_id} exported their IDAC data")
|
self.logger.info(f"User {user_id} exported their IDAC data")
|
||||||
return content
|
return Response(
|
||||||
|
content,
|
||||||
|
200,
|
||||||
|
{'content-disposition': 'attachment; filename=idac_profile.json'},
|
||||||
|
"application/octet-stream"
|
||||||
|
)
|
||||||
|
|
||||||
profile_data, tickets, rank = None, None, None
|
profile_data, tickets, rank = None, None, None
|
||||||
if user_id > 0:
|
if user_id > 0:
|
||||||
@ -128,7 +132,7 @@ class IDACFrontend(FE_Base):
|
|||||||
for ticket in ticket_data
|
for ticket in ticket_data
|
||||||
}
|
}
|
||||||
|
|
||||||
return template.render(
|
return Response(template.render(
|
||||||
title=f"{self.core_config.server.name} | {self.nav_name}",
|
title=f"{self.core_config.server.name} | {self.nav_name}",
|
||||||
game_list=self.environment.globals["game_list"],
|
game_list=self.environment.globals["game_list"],
|
||||||
profile=profile_data,
|
profile=profile_data,
|
||||||
@ -136,4 +140,4 @@ class IDACFrontend(FE_Base):
|
|||||||
rank=rank,
|
rank=rank,
|
||||||
sesh=vars(usr_sesh),
|
sesh=vars(usr_sesh),
|
||||||
active_page="idac",
|
active_page="idac",
|
||||||
).encode("utf-16")
|
))
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
{% extends "core/frontend/index.jinja" %}
|
{% extends "core/templates/index.jinja" %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h1 class="mb-3">頭文字D THE ARCADE</h1>
|
<h1 class="mb-3">頭文字D THE ARCADE</h1>
|
||||||
|
|
||||||
{% if sesh is defined and sesh["userId"] > 0 %}
|
{% if sesh is defined and sesh["user_id"] > 0 %}
|
||||||
<div class="card mb-3">
|
<div class="card mb-3">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="card-title">
|
<div class="card-title">
|
||||||
@ -128,7 +128,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
{% include "titles/idac/frontend/js/idac_scripts.js" %}
|
{% include "titles/idac/templates/js/idac_scripts.js" %}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{% endblock content %}
|
{% endblock content %}
|
@ -1,11 +1,14 @@
|
|||||||
|
from typing import List
|
||||||
|
from starlette.routing import Route
|
||||||
import yaml
|
import yaml
|
||||||
import jinja2
|
import jinja2
|
||||||
from starlette.requests import Request
|
from starlette.requests import Request
|
||||||
|
from starlette.responses import Response, RedirectResponse
|
||||||
from os import path
|
from os import path
|
||||||
from twisted.web.util import redirectTo
|
from twisted.web.util import redirectTo
|
||||||
from twisted.web.server import Session
|
from twisted.web.server import Session
|
||||||
|
|
||||||
from core.frontend import FE_Base, IUserSession
|
from core.frontend import FE_Base, UserSession
|
||||||
from core.config import CoreConfig
|
from core.config import CoreConfig
|
||||||
|
|
||||||
from titles.ongeki.config import OngekiConfig
|
from titles.ongeki.config import OngekiConfig
|
||||||
@ -28,23 +31,30 @@ class OngekiFrontend(FE_Base):
|
|||||||
self.nav_name = "O.N.G.E.K.I."
|
self.nav_name = "O.N.G.E.K.I."
|
||||||
self.version_list = OngekiConstants.VERSION_NAMES
|
self.version_list = OngekiConstants.VERSION_NAMES
|
||||||
|
|
||||||
|
def get_routes(self) -> List[Route]:
|
||||||
|
return [
|
||||||
|
Route("/", self.render_GET)
|
||||||
|
]
|
||||||
|
|
||||||
async def render_GET(self, request: Request) -> bytes:
|
async def render_GET(self, request: Request) -> bytes:
|
||||||
template = self.environment.get_template(
|
template = self.environment.get_template(
|
||||||
"titles/ongeki/frontend/ongeki_index.jinja"
|
"titles/ongeki/templates/ongeki_index.jinja"
|
||||||
)
|
)
|
||||||
sesh: Session = request.getSession()
|
usr_sesh = self.validate_session(request)
|
||||||
usr_sesh = IUserSession(sesh)
|
if not usr_sesh:
|
||||||
|
usr_sesh = UserSession()
|
||||||
|
|
||||||
self.version = usr_sesh.ongeki_version
|
self.version = usr_sesh.ongeki_version
|
||||||
if getattr(usr_sesh, "userId", 0) != 0:
|
if getattr(usr_sesh, "userId", 0) != 0:
|
||||||
profile_data =self.data.profile.get_profile_data(usr_sesh.userId, self.version)
|
profile_data =self.data.profile.get_profile_data(usr_sesh.user_id, self.version)
|
||||||
rival_list = await self.data.profile.get_rivals(usr_sesh.userId)
|
rival_list = await self.data.profile.get_rivals(usr_sesh.user_id)
|
||||||
rival_data = {
|
rival_data = {
|
||||||
"userRivalList": rival_list,
|
"userRivalList": rival_list,
|
||||||
"userId": usr_sesh.userId
|
"userId": usr_sesh.user_id
|
||||||
}
|
}
|
||||||
rival_info = OngekiBase.handle_get_user_rival_data_api_request(self, rival_data)
|
rival_info = OngekiBase.handle_get_user_rival_data_api_request(self, rival_data)
|
||||||
|
|
||||||
return template.render(
|
return Response(template.render(
|
||||||
data=self.data.profile,
|
data=self.data.profile,
|
||||||
title=f"{self.core_config.server.name} | {self.nav_name}",
|
title=f"{self.core_config.server.name} | {self.nav_name}",
|
||||||
game_list=self.environment.globals["game_list"],
|
game_list=self.environment.globals["game_list"],
|
||||||
@ -54,34 +64,36 @@ class OngekiFrontend(FE_Base):
|
|||||||
version_list=self.version_list,
|
version_list=self.version_list,
|
||||||
version=self.version,
|
version=self.version,
|
||||||
sesh=vars(usr_sesh)
|
sesh=vars(usr_sesh)
|
||||||
).encode("utf-16")
|
))
|
||||||
else:
|
else:
|
||||||
return redirectTo(b"/gate/", request)
|
return RedirectResponse("/gate/", 303)
|
||||||
|
|
||||||
async def render_POST(self, request: Request):
|
async def render_POST(self, request: Request):
|
||||||
uri = request.uri.decode()
|
uri = request.uri.decode()
|
||||||
sesh: Session = request.getSession()
|
usr_sesh = self.validate_session(request)
|
||||||
usr_sesh = IUserSession(sesh)
|
if not usr_sesh:
|
||||||
if hasattr(usr_sesh, "userId"):
|
usr_sesh = UserSession()
|
||||||
|
|
||||||
|
if usr_sesh.user_id > 0:
|
||||||
if uri == "/game/ongeki/rival.add":
|
if uri == "/game/ongeki/rival.add":
|
||||||
rival_id = request.args[b"rivalUserId"][0].decode()
|
rival_id = request.args[b"rivalUserId"][0].decode()
|
||||||
await self.data.profile.put_rival(usr_sesh.userId, rival_id)
|
await self.data.profile.put_rival(usr_sesh.user_id, rival_id)
|
||||||
# self.logger.info(f"{usr_sesh.userId} added a rival")
|
# self.logger.info(f"{usr_sesh.user_id} added a rival")
|
||||||
return redirectTo(b"/game/ongeki/", request)
|
return RedirectResponse(b"/game/ongeki/", 303)
|
||||||
|
|
||||||
elif uri == "/game/ongeki/rival.delete":
|
elif uri == "/game/ongeki/rival.delete":
|
||||||
rival_id = request.args[b"rivalUserId"][0].decode()
|
rival_id = request.args[b"rivalUserId"][0].decode()
|
||||||
await self.data.profile.delete_rival(usr_sesh.userId, rival_id)
|
await self.data.profile.delete_rival(usr_sesh.user_id, rival_id)
|
||||||
# self.logger.info(f"{response}")
|
# self.logger.info(f"{response}")
|
||||||
return redirectTo(b"/game/ongeki/", request)
|
return RedirectResponse(b"/game/ongeki/", 303)
|
||||||
|
|
||||||
elif uri == "/game/ongeki/version.change":
|
elif uri == "/game/ongeki/version.change":
|
||||||
ongeki_version=request.args[b"version"][0].decode()
|
ongeki_version=request.args[b"version"][0].decode()
|
||||||
if(ongeki_version.isdigit()):
|
if(ongeki_version.isdigit()):
|
||||||
usr_sesh.ongeki_version=int(ongeki_version)
|
usr_sesh.ongeki_version=int(ongeki_version)
|
||||||
return redirectTo(b"/game/ongeki/", request)
|
return RedirectResponse("/game/ongeki/", 303)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
return b"Something went wrong"
|
Response("Something went wrong", status_code=500)
|
||||||
else:
|
else:
|
||||||
return b"User is not logged in"
|
return RedirectResponse("/gate/", 303)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{% extends "core/frontend/index.jinja" %}
|
{% extends "core/templates/index.jinja" %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
{% if sesh is defined and sesh["userId"] > 0 %}
|
{% if sesh is defined and sesh["user_id"] > 0 %}
|
||||||
<br>
|
<br>
|
||||||
<br>
|
<br>
|
||||||
<br>
|
<br>
|
||||||
@ -75,7 +75,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
{% include 'titles/ongeki/frontend/js/ongeki_scripts.js' %}
|
{% include 'titles/ongeki/templates/js/ongeki_scripts.js' %}
|
||||||
</script>
|
</script>
|
||||||
{% else %}
|
{% else %}
|
||||||
<h2>Not Currently Logged In</h2>
|
<h2>Not Currently Logged In</h2>
|
@ -1,10 +1,12 @@
|
|||||||
import yaml
|
import yaml
|
||||||
import jinja2
|
import jinja2
|
||||||
|
from typing import List
|
||||||
from starlette.requests import Request
|
from starlette.requests import Request
|
||||||
|
from starlette.responses import Response, RedirectResponse
|
||||||
|
from starlette.routing import Route
|
||||||
from os import path
|
from os import path
|
||||||
from twisted.web.server import Session
|
|
||||||
|
|
||||||
from core.frontend import FE_Base, IUserSession
|
from core.frontend import FE_Base, UserSession
|
||||||
from core.config import CoreConfig
|
from core.config import CoreConfig
|
||||||
from .database import PokkenData
|
from .database import PokkenData
|
||||||
from .config import PokkenConfig
|
from .config import PokkenConfig
|
||||||
@ -12,6 +14,8 @@ from .const import PokkenConstants
|
|||||||
|
|
||||||
|
|
||||||
class PokkenFrontend(FE_Base):
|
class PokkenFrontend(FE_Base):
|
||||||
|
SN_PREFIX = PokkenConstants.SERIAL_IDENT
|
||||||
|
NETID_PREFIX = PokkenConstants.NETID_PREFIX
|
||||||
def __init__(
|
def __init__(
|
||||||
self, cfg: CoreConfig, environment: jinja2.Environment, cfg_dir: str
|
self, cfg: CoreConfig, environment: jinja2.Environment, cfg_dir: str
|
||||||
) -> None:
|
) -> None:
|
||||||
@ -24,16 +28,74 @@ class PokkenFrontend(FE_Base):
|
|||||||
)
|
)
|
||||||
self.nav_name = "Pokken"
|
self.nav_name = "Pokken"
|
||||||
|
|
||||||
def render_GET(self, request: Request) -> bytes:
|
def get_routes(self) -> List[Route]:
|
||||||
|
return [
|
||||||
|
Route("/", self.render_GET, methods=['GET']),
|
||||||
|
Route("/update.name", self.change_name, methods=['POST']),
|
||||||
|
]
|
||||||
|
|
||||||
|
async def render_GET(self, request: Request) -> Response:
|
||||||
template = self.environment.get_template(
|
template = self.environment.get_template(
|
||||||
"titles/pokken/frontend/pokken_index.jinja"
|
"titles/pokken/templates/pokken_index.jinja"
|
||||||
)
|
)
|
||||||
|
pf = None
|
||||||
|
|
||||||
sesh: Session = request.getSession()
|
usr_sesh = self.validate_session(request)
|
||||||
usr_sesh = IUserSession(sesh)
|
if not usr_sesh:
|
||||||
|
usr_sesh = UserSession()
|
||||||
|
|
||||||
return template.render(
|
else:
|
||||||
|
profile = await self.data.profile.get_profile(usr_sesh.user_id)
|
||||||
|
if profile is not None and profile['trainer_name']:
|
||||||
|
pf = profile._asdict()
|
||||||
|
|
||||||
|
if "e" in request.query_params:
|
||||||
|
try:
|
||||||
|
err = int(request.query_params.get("e", 0))
|
||||||
|
except Exception:
|
||||||
|
err = 0
|
||||||
|
|
||||||
|
else:
|
||||||
|
err = 0
|
||||||
|
|
||||||
|
if "s" in request.query_params:
|
||||||
|
try:
|
||||||
|
succ = int(request.query_params.get("s", 0))
|
||||||
|
except Exception:
|
||||||
|
succ = 0
|
||||||
|
|
||||||
|
else:
|
||||||
|
succ = 0
|
||||||
|
|
||||||
|
return Response(template.render(
|
||||||
title=f"{self.core_config.server.name} | {self.nav_name}",
|
title=f"{self.core_config.server.name} | {self.nav_name}",
|
||||||
game_list=self.environment.globals["game_list"],
|
game_list=self.environment.globals["game_list"],
|
||||||
sesh=vars(usr_sesh)
|
sesh=vars(usr_sesh),
|
||||||
).encode("utf-16")
|
profile=pf,
|
||||||
|
error=err,
|
||||||
|
success=succ
|
||||||
|
))
|
||||||
|
|
||||||
|
async def change_name(self, request: Request) -> RedirectResponse:
|
||||||
|
usr_sesh = self.validate_session(request)
|
||||||
|
if not usr_sesh:
|
||||||
|
return RedirectResponse("/game/pokken/?e=9", 303)
|
||||||
|
|
||||||
|
frm = await request.form()
|
||||||
|
new_name = frm.get("new_name")
|
||||||
|
gender = frm.get("new_gender", 1)
|
||||||
|
|
||||||
|
if len(new_name) > 14:
|
||||||
|
return RedirectResponse("/game/pokken/?e=8", 303)
|
||||||
|
|
||||||
|
if not gender.isdigit():
|
||||||
|
return RedirectResponse("/game/pokken/?e=4", 303)
|
||||||
|
|
||||||
|
gender = int(gender)
|
||||||
|
|
||||||
|
if gender != 1 and gender != 2:
|
||||||
|
return RedirectResponse("/game/pokken/?e=4", 303) # no code for this yet, whatever
|
||||||
|
|
||||||
|
await self.data.profile.set_profile_name(usr_sesh.user_id, new_name, gender)
|
||||||
|
|
||||||
|
return RedirectResponse("/game/pokken/?s=1", 303)
|
||||||
|
@ -1,4 +0,0 @@
|
|||||||
{% extends "core/frontend/index.jinja" %}
|
|
||||||
{% block content %}
|
|
||||||
<h1>Pokken</h1>
|
|
||||||
{% endblock content %}
|
|
48
titles/pokken/templates/pokken_index.jinja
Normal file
48
titles/pokken/templates/pokken_index.jinja
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
{% extends "core/templates/index.jinja" %}
|
||||||
|
{% block content %}
|
||||||
|
<h1>Pokken</h1>
|
||||||
|
{% if profile is defined and profile is not none and profile.id > 0 %}
|
||||||
|
<script type="text/javascript">
|
||||||
|
function toggle_new_name_form() {
|
||||||
|
let frm = document.getElementById("new_name_form");
|
||||||
|
let btn = document.getElementById("btn_toggle_form");
|
||||||
|
|
||||||
|
if (frm.style['display'] != "") {
|
||||||
|
frm.style['display'] = "";
|
||||||
|
frm.style['max-height'] = "";
|
||||||
|
btn.innerText = "Cancel";
|
||||||
|
} else {
|
||||||
|
frm.style['display'] = "none";
|
||||||
|
frm.style['max-height'] = "0px";
|
||||||
|
btn.innerText = "Edit";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<h3>Profile for {{ profile.trainer_name }} <button onclick="toggle_new_name_form()" class="btn btn-secondary" id="btn_toggle_form">Edit</button></h3>
|
||||||
|
{% if error is defined %}
|
||||||
|
{% include "core/templates/widgets/err_banner.jinja" %}
|
||||||
|
{% endif %}
|
||||||
|
{% if success is defined and success == 1 %}
|
||||||
|
<div style="background-color: #00AA00; padding: 20px; margin-bottom: 10px; width: 15%;">
|
||||||
|
Update successful
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<form style="max-width: 33%; display: none; max-height: 0px;" action="/game/pokken/update.name" method="post" id="new_name_form">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="new_name" class="form-label">New Trainer Name</label>
|
||||||
|
<input type="text" class="form-control" id="new_name" name="new_name" aria-describedby="new_name_help" maxlength="14">
|
||||||
|
<div id="new_name_help" class="form-text">Must be 14 characters or less</div>
|
||||||
|
<br>
|
||||||
|
<input type="radio" id="new_gender_male" name="new_gender" value="1" {% if profile.avatar_gender is none or profile.avatar_gender == 1 %}checked{% endif %}>
|
||||||
|
<label for="new_gender_male">Male</label>
|
||||||
|
<input type="radio" id="new_gender_female" name="new_gender" value="2" {% if profile.avatar_gender == 2 %}checked{% endif %}>
|
||||||
|
<label for="new_gender_male">Female</label>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary">Submit</button>
|
||||||
|
</form>
|
||||||
|
{% elif sesh is defined and sesh is not none and sesh.user_id > 0 %}
|
||||||
|
No profile information found for this account.
|
||||||
|
{% else %}
|
||||||
|
Login to view profile information.
|
||||||
|
{% endif %}
|
||||||
|
{% endblock content %}
|
@ -1,10 +1,12 @@
|
|||||||
|
from typing import List
|
||||||
|
from starlette.routing import Route
|
||||||
|
from starlette.requests import Request
|
||||||
|
from starlette.responses import Response
|
||||||
|
from os import path
|
||||||
import yaml
|
import yaml
|
||||||
import jinja2
|
import jinja2
|
||||||
from starlette.requests import Request
|
|
||||||
from os import path
|
|
||||||
from twisted.web.server import Session
|
|
||||||
|
|
||||||
from core.frontend import FE_Base, IUserSession
|
from core.frontend import FE_Base, UserSession
|
||||||
from core.config import CoreConfig
|
from core.config import CoreConfig
|
||||||
from titles.wacca.database import WaccaData
|
from titles.wacca.database import WaccaData
|
||||||
from titles.wacca.config import WaccaConfig
|
from titles.wacca.config import WaccaConfig
|
||||||
@ -24,15 +26,21 @@ class WaccaFrontend(FE_Base):
|
|||||||
)
|
)
|
||||||
self.nav_name = "Wacca"
|
self.nav_name = "Wacca"
|
||||||
|
|
||||||
|
def get_routes(self) -> List[Route]:
|
||||||
|
return [
|
||||||
|
Route("/", self.render_GET, methods=['GET'])
|
||||||
|
]
|
||||||
|
|
||||||
async def render_GET(self, request: Request) -> bytes:
|
async def render_GET(self, request: Request) -> bytes:
|
||||||
template = self.environment.get_template(
|
template = self.environment.get_template(
|
||||||
"titles/wacca/frontend/wacca_index.jinja"
|
"titles/wacca/templates/wacca_index.jinja"
|
||||||
)
|
)
|
||||||
sesh: Session = request.getSession()
|
usr_sesh = self.validate_session(request)
|
||||||
usr_sesh = IUserSession(sesh)
|
if not usr_sesh:
|
||||||
|
usr_sesh = UserSession()
|
||||||
|
|
||||||
return template.render(
|
return Response(template.render(
|
||||||
title=f"{self.core_config.server.name} | {self.nav_name}",
|
title=f"{self.core_config.server.name} | {self.nav_name}",
|
||||||
game_list=self.environment.globals["game_list"],
|
game_list=self.environment.globals["game_list"],
|
||||||
sesh=vars(usr_sesh)
|
sesh=vars(usr_sesh)
|
||||||
).encode("utf-16")
|
))
|
@ -1,4 +0,0 @@
|
|||||||
{% extends "core/frontend/index.jinja" %}
|
|
||||||
{% block content %}
|
|
||||||
<h1>Wacca</h1>
|
|
||||||
{% endblock content %}
|
|
4
titles/wacca/templates/wacca_index.jinja
Normal file
4
titles/wacca/templates/wacca_index.jinja
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{% extends "core/templates/index.jinja" %}
|
||||||
|
{% block content %}
|
||||||
|
<h1>Wacca</h1>
|
||||||
|
{% endblock content %}
|
Loading…
Reference in New Issue
Block a user