frontend: arcade management rework

pull/204/head
Hay1tsme 2025-03-25 10:43:26 -04:00
parent 20d9a2da9c
commit b6d22ef41d
8 changed files with 556 additions and 49 deletions

View File

@ -45,6 +45,14 @@ class AllnetCountryCode(Enum):
SOUTH_KOREA = "KOR"
TAIWAN = "TWN"
CHINA = "CHN"
AUSTRALIA = "AUS"
INDONESIA = "IDN"
MYANMAR = "MMR"
MALAYSIA = "MYS"
NEW_ZEALAND = "NZL"
PHILIPPINES = "PHL"
THAILAND = "THA"
VIETNAM = "VNM"
class AllnetJapanRegionId(Enum):

View File

@ -115,6 +115,15 @@ class ArcadeData(BaseData):
return None
return result.lastrowid
async def set_machine_arcade(self, machine_id: int, new_arcade: int) -> bool:
sql = machine.update(machine.c.id == machine_id).values(arcade = new_arcade)
result = await self.execute(sql)
if result is None:
self.logger.error(f"Failed to update machine {machine_id} arcade to {new_arcade}")
return False
return True
async def set_machine_serial(self, machine_id: int, serial: str) -> None:
result = await self.execute(
machine.update(machine.c.id == machine_id).values(keychip=serial)
@ -134,6 +143,60 @@ class ArcadeData(BaseData):
f"Failed to update board id for machine {machine_id} -> {boardid}"
)
async def set_machine_game(self, machine_id: int, new_game: Optional[str]) -> bool:
sql = machine.update(machine.c.id == machine_id).values(game = new_game)
result = await self.execute(sql)
if result is None:
self.logger.error(f"Failed to update machine {machine_id} game to {new_game}")
return False
return True
async def set_machine_country(self, machine_id: int, new_country: Optional[str]) -> bool:
sql = machine.update(machine.c.id == machine_id).values(country = new_country)
result = await self.execute(sql)
if result is None:
self.logger.error(f"Failed to update machine {machine_id} country to {new_country}")
return False
return True
async def set_machine_timezone(self, machine_id: int, new_timezone: Optional[str]) -> bool:
sql = machine.update(machine.c.id == machine_id).values(timezone = new_timezone)
result = await self.execute(sql)
if result is None:
self.logger.error(f"Failed to update machine {machine_id} timezone to {new_timezone}")
return False
return True
async def set_machine_real_cabinet(self, machine_id: int, is_real: bool = False) -> bool:
sql = machine.update(machine.c.id == machine_id).values(is_cab = is_real)
result = await self.execute(sql)
if result is None:
self.logger.error(f"Failed to update machine {machine_id} is_cab to {is_real}")
return False
return True
async def set_machine_can_ota(self, machine_id: int, can_ota: bool = False) -> bool:
sql = machine.update(machine.c.id == machine_id).values(ota_enable = can_ota)
result = await self.execute(sql)
if result is None:
self.logger.error(f"Failed to update machine {machine_id} ota_enable to {can_ota}")
return False
return True
async def set_machine_memo(self, machine_id: int, new_memo: Optional[str]) -> bool:
sql = machine.update(machine.c.id == machine_id).values(memo = new_memo)
result = await self.execute(sql)
if result is None:
self.logger.error(f"Failed to update machine {machine_id} memo")
return False
return True
async def get_arcade(self, id: int) -> Optional[Row]:
sql = arcade.select(arcade.c.id == id)
result = await self.execute(sql)
@ -187,8 +250,11 @@ class ArcadeData(BaseData):
sql = select(arcade_owner.c.permissions).where(and_(arcade_owner.c.user == user_id, arcade_owner.c.arcade == arcade_id))
result = await self.execute(sql)
if result is None:
return False
return result.fetchone()
return None
row = result.fetchone()
if row:
return row['permissions']
return None
async def get_arcade_owners(self, arcade_id: int) -> Optional[Row]:
sql = select(arcade_owner).where(arcade_owner.c.arcade == arcade_id)
@ -198,7 +264,7 @@ class ArcadeData(BaseData):
return None
return result.fetchall()
async def add_arcade_owner(self, arcade_id: int, user_id: int) -> None:
async def add_arcade_owner(self, arcade_id: int, user_id: int, permissions: int = 1) -> Optional[int]:
sql = insert(arcade_owner).values(arcade=arcade_id, user=user_id)
result = await self.execute(sql)
@ -206,6 +272,17 @@ class ArcadeData(BaseData):
return None
return result.lastrowid
async def set_arcade_owner_permissions(self, arcade_id: int, user_id: int, new_permissions: int = 1) -> bool:
sql = arcade_owner.update(
and_(arcade_owner.c.arcade == arcade_id, arcade_owner.c.user == user_id)
).values(permissions = new_permissions)
result = await self.execute(sql)
if result is None:
self.logger.error(f"Failed to update arcade owner permissions to {new_permissions} for user {user_id} arcade {arcade_id}")
return False
return True
async def get_arcade_by_name(self, name: str) -> Optional[List[Row]]:
sql = arcade.select(or_(arcade.c.name.like(f"%{name}%"), arcade.c.nickname.like(f"%{name}%")))
result = await self.execute(sql)
@ -219,7 +296,49 @@ class ArcadeData(BaseData):
if result is None:
return None
return result.fetchall()
async def set_arcade_name_nickname(self, arcade_id: int, new_name: Optional[str], new_nickname: Optional[str]) -> bool:
sql = arcade.update(arcade.c.id == arcade_id).values(name = new_name, nickname = new_nickname)
result = await self.execute(sql)
if result is None:
self.logger.error(f"Failed to update arcade {arcade_id} name to {new_name}/{new_nickname}")
return False
return True
async def set_arcade_region_info(self, arcade_id: int, new_country: Optional[str], new_state: Optional[str], new_city: Optional[str], new_region_id: Optional[int], new_country_id: Optional[int]) -> bool:
sql = arcade.update(arcade.c.id == arcade_id).values(
country = new_country,
state = new_state,
city = new_city,
region_id = new_region_id,
country_id = new_country_id
)
result = await self.execute(sql)
if result is None:
self.logger.error(f"Failed to update arcade {arcade_id} regional info to {new_country}/{new_state}/{new_city}/{new_region_id}/{new_country_id}")
return False
return True
async def set_arcade_timezone(self, arcade_id: int, new_timezone: Optional[str]) -> bool:
sql = arcade.update(arcade.c.id == arcade_id).values(timezone = new_timezone)
result = await self.execute(sql)
if result is None:
self.logger.error(f"Failed to update arcade {arcade_id} timezone to {new_timezone}")
return False
return True
async def set_arcade_vpn_ip(self, arcade_id: int, new_ip: Optional[str]) -> bool:
sql = arcade.update(arcade.c.id == arcade_id).values(ip = new_ip)
result = await self.execute(sql)
if result is None:
self.logger.error(f"Failed to update arcade {arcade_id} VPN address to {new_ip}")
return False
return True
async def get_num_generated_keychips(self) -> Optional[int]:
result = await self.execute(select(func.count("serial LIKE 'A69A%'")).select_from(machine))
if result:

View File

@ -12,7 +12,6 @@ import jwt
import yaml
import secrets
import string
import random
from base64 import b64decode
from enum import Enum
from datetime import datetime, timezone
@ -20,6 +19,7 @@ from os import path, environ, mkdir, W_OK, access
from core import CoreConfig, Utils
from core.data import Data
from core.const import AllnetCountryCode
# A-HJ-NP-Z
SERIAL_LETTERS = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
@ -37,8 +37,7 @@ class ShopPermissionOffset(Enum):
VIEW = 0 # View info and cabs
BOOKKEEP = 1 # View bookeeping info
EDITOR = 2 # Can edit name, settings
REGISTRAR = 3 # Can add cabs
# 4 - 6 reserved for future use
# 3 - 6 reserved for future use
OWNER = 7 # Can do anything
class ShopOwner():
@ -149,10 +148,13 @@ class FrontendServlet():
Mount("/shop", routes=[
Route("/", self.arcade.render_GET, methods=['GET']),
Route("/{shop_id:int}", self.arcade.render_GET, methods=['GET']),
Route("/{shop_id:int}/info.update", self.arcade.update_shop, methods=['POST']),
]),
Mount("/cab", routes=[
Route("/", self.machine.render_GET, methods=['GET']),
Route("/{machine_id:int}", self.machine.render_GET, methods=['GET']),
Route("/{machine_id:int}/info.update", self.machine.update_cab, methods=['POST']),
Route("/{machine_id:int}/reassign", self.machine.reassign_cab, methods=['POST']),
]),
Mount("/game", routes=g_routes),
Route("/robots.txt", self.robots)
@ -455,6 +457,16 @@ class FE_User(FE_Base):
card_data = []
arcade_data = []
managed_arcades = await self.data.arcade.get_arcades_managed_by_user(user_id)
if managed_arcades:
for arcade in managed_arcades:
ac = await self.data.arcade.get_arcade(arcade['id'])
if ac:
arcade_data.append({
"id": ac['id'],
"name": ac['name'],
})
for c in cards:
if c['is_locked']:
status = 'Locked'
@ -861,14 +873,16 @@ class FE_System(FE_Base):
name = frm.get("shopName", None)
country = frm.get("shopCountry", "JPN")
ip = frm.get("shopIp", None)
owner = frm.get("shopOwner", None)
acid = await self.data.arcade.create_arcade(name if name else None, name if name else None, country)
if not acid:
return RedirectResponse("/sys/?e=99", 303)
if ip:
# TODO: set IP
pass
await self.data.arcade.set_arcade_vpn_ip(acid, ip if ip else None)
if owner:
await self.data.arcade.add_arcade_owner(acid, int(owner), 255)
return Response(template.render(
title=f"{self.core_config.server.name} | System",
@ -947,15 +961,20 @@ class FE_Arcade(FE_Base):
shop_id = request.path_params.get('shop_id', None)
usr_sesh = self.validate_session(request)
if not usr_sesh or not self.test_perm(usr_sesh.permissions, PermissionOffset.ACMOD):
self.logger.warning(f"User {usr_sesh.user_id} does not have permission to view shops!")
if not usr_sesh:
return RedirectResponse("/gate/", 303)
if not shop_id:
return Response(template.render(
title=f"{self.core_config.server.name} | Arcade",
sesh=vars(usr_sesh),
), media_type="text/html; charset=utf-8")
return Response('Not Found', status_code=404)
is_acmod = self.test_perm(usr_sesh.permissions, PermissionOffset.ACMOD)
if not is_acmod:
usr_shop_perm = await self.data.arcade.get_manager_permissions(usr_sesh.user_id, shop_id)
if usr_shop_perm is None or usr_shop_perm == 0:
self.logger.warning(f"User {usr_sesh.user_id} does not have permission to view shop {shop_id}!")
return RedirectResponse("/", 303)
else:
usr_shop_perm = 15 # view, bookeep, edit
sinfo = await self.data.arcade.get_arcade(shop_id)
if not sinfo:
@ -974,38 +993,204 @@ class FE_Arcade(FE_Base):
"game": x['game'],
})
managers = []
if (usr_shop_perm & 1 << ShopPermissionOffset.OWNER.value) or is_acmod:
mgrs = await self.data.arcade.get_arcade_owners(sinfo['id'])
if mgrs:
for mgr in mgrs:
usr = await self.data.user.get_user(mgr['user'])
managers.append({
'user': mgr['user'],
'name': usr['username'] if usr['username'] else 'No Name Set',
'is_view': bool(mgr['permissions'] & 1 << ShopPermissionOffset.VIEW.value),
'is_bookkeep': bool(mgr['permissions'] & 1 << ShopPermissionOffset.BOOKKEEP.value),
'is_edit': bool(mgr['permissions'] & 1 << ShopPermissionOffset.EDITOR.value),
'is_owner': bool(mgr['permissions'] & 1 << ShopPermissionOffset.OWNER.value),
})
if request.query_params.get("e", None):
err = int(request.query_params.get("e"))
else:
err = 0
if request.query_params.get("s", None):
suc = int(request.query_params.get("s"))
else:
suc = 0
return Response(template.render(
title=f"{self.core_config.server.name} | Arcade",
sesh=vars(usr_sesh),
arcade={
"name": sinfo['name'],
"id": sinfo['id'],
"cabs": cablst
}
cablst=cablst,
arcade=sinfo._asdict(),
can_bookkeep=bool(usr_shop_perm & 1 << ShopPermissionOffset.BOOKKEEP.value) or is_acmod,
can_edit=bool(usr_shop_perm & 1 << ShopPermissionOffset.EDITOR.value) or is_acmod,
is_owner=usr_shop_perm & 1 << ShopPermissionOffset.OWNER.value,
is_acmod=is_acmod,
managers=managers,
error=err,
success=suc
), media_type="text/html; charset=utf-8")
async def update_shop(self, request: Request):
shop_id = request.path_params.get('shop_id', None)
usr_sesh = self.validate_session(request)
if not usr_sesh:
return RedirectResponse("/gate/", 303)
sinfo = await self.data.arcade.get_arcade(shop_id)
if not shop_id or not sinfo:
return RedirectResponse("/", 303)
if not self.test_perm(usr_sesh.permissions, PermissionOffset.ACMOD):
usr_shop_perm = await self.data.arcade.get_manager_permissions(usr_sesh.user_id, sinfo['id'])
if usr_shop_perm is None or usr_shop_perm == 0:
self.logger.warning(f"User {usr_sesh.user_id} does not have permission to view shop {sinfo['id']}!")
return RedirectResponse("/", 303)
frm = await request.form()
new_name = frm.get('name', None)
new_nickname = frm.get('nickname', None)
new_country = frm.get('country', None)
new_region1 = frm.get('region1', None)
new_region2 = frm.get('region2', None)
new_tz = frm.get('tz', None)
new_ip = frm.get('ip', None)
try:
AllnetCountryCode(new_country)
except ValueError:
new_country = 'JPN'
did_name = await self.data.arcade.set_arcade_name_nickname(sinfo['id'], new_name if new_name else f'Arcade{sinfo["id"]}', new_nickname if new_nickname else None)
did_region = await self.data.arcade.set_arcade_region_info(sinfo['id'], new_country, new_region1 if new_region1 else None, new_region2 if new_region2 else None, None, None)
did_timezone = await self.data.arcade.set_arcade_timezone(sinfo['id'], new_tz if new_tz else None)
did_vpn = await self.data.arcade.set_arcade_vpn_ip(sinfo['id'], new_ip if new_ip else None)
if not did_name or not did_region or not did_timezone or not did_vpn:
self.logger.error(f"Failed to update some shop into: Name: {did_name} Region: {did_region} TZ: {did_timezone} VPN: {did_vpn}")
return RedirectResponse(f"/shop/{shop_id}?e=15", 303)
return RedirectResponse(f"/shop/{shop_id}?s=1", 303)
class FE_Machine(FE_Base):
async def render_GET(self, request: Request):
template = self.environment.get_template("core/templates/machine/index.jinja")
cab_id = request.path_params.get('cab_id', None)
cab_id = request.path_params.get('machine_id', None)
usr_sesh = self.validate_session(request)
if not usr_sesh or not self.test_perm(usr_sesh.permissions, PermissionOffset.ACMOD):
self.logger.warning(f"User {usr_sesh.user_id} does not have permission to view shops!")
if not usr_sesh:
return RedirectResponse("/gate/", 303)
cab = await self.data.arcade.get_machine(id=cab_id)
if not cab_id:
return Response(template.render(
title=f"{self.core_config.server.name} | Machine",
sesh=vars(usr_sesh),
), media_type="text/html; charset=utf-8")
if not cab_id or not cab:
return Response('Not Found', status_code=404)
shop = await self.data.arcade.get_arcade(cab['arcade'])
is_acmod = self.test_perm(usr_sesh.permissions, PermissionOffset.ACMOD)
if not is_acmod:
usr_shop_perm = await self.data.arcade.get_manager_permissions(usr_sesh.user_id, shop['id'])
if usr_shop_perm is None or usr_shop_perm == 0:
self.logger.warning(f"User {usr_sesh.user_id} does not have permission to view shop {shop['id']}!")
return RedirectResponse("/", 303)
else:
usr_shop_perm = 15 # view, bookeep, edit
if request.query_params.get("e", None):
err = int(request.query_params.get("e"))
else:
err = 0
if request.query_params.get("s", None):
suc = int(request.query_params.get("s"))
else:
suc = 0
return Response(template.render(
title=f"{self.core_config.server.name} | Machine",
sesh=vars(usr_sesh),
arcade={}
arcade=shop._asdict(),
machine=cab._asdict(),
can_bookkeep=bool(usr_shop_perm & 1 << ShopPermissionOffset.BOOKKEEP.value) or is_acmod,
can_edit=bool(usr_shop_perm & 1 << ShopPermissionOffset.EDITOR.value) or is_acmod,
is_owner=usr_shop_perm & 1 << ShopPermissionOffset.OWNER.value,
is_acmod=is_acmod,
error=err,
success=suc
), media_type="text/html; charset=utf-8")
async def update_cab(self, request: Request):
cab_id = request.path_params.get('machine_id', None)
usr_sesh = self.validate_session(request)
if not usr_sesh:
return RedirectResponse("/gate/", 303)
cab = await self.data.arcade.get_machine(id=cab_id)
if not cab_id or not cab:
return RedirectResponse("/", 303)
if not self.test_perm(usr_sesh.permissions, PermissionOffset.ACMOD):
usr_shop_perm = await self.data.arcade.get_manager_permissions(usr_sesh.user_id, cab['arcade'])
if usr_shop_perm is None or usr_shop_perm == 0:
self.logger.warning(f"User {usr_sesh.user_id} does not have permission to view shop {cab['arcade']}!")
return RedirectResponse("/", 303)
frm = await request.form()
new_game = frm.get('game', None)
new_country = frm.get('country', None)
new_tz = frm.get('tz', None)
new_is_cab = frm.get('is_cab', False) == 'on'
new_is_ota = frm.get('is_ota', False) == 'on'
new_memo = frm.get('memo', None)
try:
AllnetCountryCode(new_country)
except ValueError:
new_country = None
did_game = await self.data.arcade.set_machine_game(cab['id'], new_game if new_game else None)
did_country = await self.data.arcade.set_machine_country(cab['id'], new_country if new_country else None)
did_timezone = await self.data.arcade.set_machine_timezone(cab['id'], new_tz if new_tz else None)
did_real_cab = await self.data.arcade.set_machine_real_cabinet(cab['id'], new_is_cab)
did_ota = await self.data.arcade.set_machine_can_ota(cab['id'], new_is_ota)
did_memo = await self.data.arcade.set_machine_memo(cab['id'], new_memo if new_memo else None)
if not did_game or not did_country or not did_timezone or not did_real_cab or not did_ota or not did_memo:
self.logger.error(f"Failed to update some shop into: Game: {did_game} Country: {did_country} TZ: {did_timezone} Real: {did_real_cab} OTA: {did_ota} Memo: {did_memo}")
return RedirectResponse(f"/cab/{cab['id']}?e=15", 303)
return RedirectResponse(f"/cab/{cab_id}?s=1", 303)
async def reassign_cab(self, request: Request):
cab_id = request.path_params.get('machine_id', None)
usr_sesh = self.validate_session(request)
if not usr_sesh:
return RedirectResponse("/gate/", 303)
cab = await self.data.arcade.get_machine(id=cab_id)
if not cab_id or not cab:
return RedirectResponse("/", 303)
frm = await request.form()
new_shop = frm.get('new_arcade', None)
if not self.test_perm(usr_sesh.permissions, PermissionOffset.ACMOD):
self.logger.warning(f"User {usr_sesh.user_id} does not have permission to reassign cab {cab['id']} to arcade !")
return RedirectResponse(f"/cab/{cab_id}?e=11", 303)
new_sinfo = await self.data.arcade.get_arcade(new_shop)
if not new_sinfo:
return RedirectResponse(f"/cab/{cab_id}?e=14", 303)
if not await self.data.arcade.set_machine_arcade(cab['id'], new_sinfo['id']):
return RedirectResponse(f"/cab/{cab_id}?e=99", 303)
return RedirectResponse(f"/cab/{cab_id}?s=2", 303)
cfg_dir = environ.get("ARTEMIS_CFG_DIR", "config")
cfg: CoreConfig = CoreConfig()

View File

@ -2,18 +2,104 @@
{% block content %}
{% if arcade is defined %}
<h1>{{ arcade.name }}</h1>
<h2>PCBs assigned to this arcade <button class="btn btn-success" id="btn_add_cab" onclick="toggle_add_cab_form()">Add</button></h2>
<h2>Assigned Machines</h2>
{% if success is defined and success == 3 %}
<div style="background-color: #00AA00; padding: 20px; margin-bottom: 10px; width: 15%;">
Cab added successfully
</div>
{% endif %}
{% if success is defined and success == 1 %}
<div style="background-color: #00AA00; padding: 20px; margin-bottom: 10px; width: 15%;">
Info Updated
</div>
{% endif %}
{% include "core/templates/widgets/err_banner.jinja" %}
<ul style="font-size: 20px;">
{% for c in arcade.cabs %}
<li><a href="/cab/{{ c.id }}">{{ c.serial }}</a> ({{ c.game if c.game else "Any" }})&nbsp;<button class="btn btn-secondary" onclick="prep_edit_form()">Edit</button>&nbsp;<button class="btn-danger btn">Delete</button></li>
{% for c in cablst %}
<li><a href="/cab/{{ c.id }}">{{ c.serial }}</a> ({{ c.game if c.game else "Any" }})</li>
{% endfor %}
</ul>
Info
<form style="max-width: 50%;" action="/shop/{{ arcade.id }}/info.update" method="post" id="shop_info">
<div class="row">
<div class="col mb-3">
<label for="name" class="form-label">Name</label>
<input type="text" class="form-control" id="name" name="name" maxlength="255" value="{{ arcade.name if arcade.name is not none else "" }}">
</div>
<div class="col mb-3">
<label for="nickname" class="form-label">Nickname</label>
<input type="text" class="form-control" id="nickname" name="nickname" maxlength="255" value="{{ arcade.nickname if arcade.nickname is not none else "" }}">
</div>
</div>
<div class="row">
<div class="col mb-3">
<label for="country" class="form-label">Country</label>
<select id="country" name="country" class="form-select bg-dark text-white">
<option value="JPN" {{ 'selected' if arcade.country == 'JPN' else ''}}>Japan</option>
<option value="USA" {{ 'selected' if arcade.country == 'USA' else ''}}>USA</option>
<option value="HKG" {{ 'selected' if arcade.country == 'HKG' else ''}}>Hong Kong</option>
<option value="SGP" {{ 'selected' if arcade.country == 'SGP' else ''}}>Singapore</option>
<option value="KOR" {{ 'selected' if arcade.country == 'KOR' else ''}}>South Korea</option>
<option value="TWN" {{ 'selected' if arcade.country == 'TWN' else ''}}>Taiwan</option>
<option value="CHN" {{ 'selected' if arcade.country == 'CHN' else ''}}>China</option>
<option value="AUS" {{ 'selected' if arcade.country == 'AUS' else ''}}>Australia</option>
<option value="IDN" {{ 'selected' if arcade.country == 'IDN' else ''}}>Indonesia</option>
<option value="MMR" {{ 'selected' if arcade.country == 'MMR' else ''}}>Myanmar</option>
<option value="MYS" {{ 'selected' if arcade.country == 'MYS' else ''}}>Malaysia</option>
<option value="NZL" {{ 'selected' if arcade.country == 'NZL' else ''}}>New Zealand</option>
<option value="PHL" {{ 'selected' if arcade.country == 'PHL' else ''}}>Philippines</option>
<option value="THA" {{ 'selected' if arcade.country == 'THA' else ''}}>Thailand</option>
<option value="VNM" {{ 'selected' if arcade.country == 'VNM' else ''}}>Vietnam</option>
</select>
</div>
<div class="col mb-3">
<label for="region1" class="form-label">Region 1</label>
<input type="text" class="form-control" id="region1" name="region1" maxlength="255" value="{{ arcade.state if arcade.state is not none else "" }}">
</div>
<div class="col mb-3">
<label for="region2" class="form-label">Region 2</label>
<input type="text" class="form-control" id="region2" name="region2" maxlength="255" value="{{ arcade.city if arcade.city is not none else "" }}">
</div>
<div class="col mb-3">
<label for="tz" class="form-label">Timezone</label>
<input type="text" class="form-control" id="tz" name="tz" placeholder="+09:00" maxlength="255" value="{{ arcade.timezone if arcade.timezone is not none else "" }}">
</div>
</div>
<div class="row">
<div class="col mb-3">
<label for="ip" class="form-label">VPN IP</label>
<input type="text" class="form-control" id="ip" name="ip" maxlength="39" value="{{ arcade.ip if arcade.ip is not none else "" }}">
</div>
</div>
{% if can_edit %}
<div class="row">
<div class="col mb-3">
<input type="submit" value="Update" class="btn btn-primary">
</div>
</div>
{% endif %}
</form>
{% if is_owner or is_acmod %}
<br>
<h2>Arcade Managers&nbsp;<button type="button" class="btn btn-success">Add</button></h2>
<ul style="font-size: 20px;">
{% for u in managers %}
<li>{{ u.name }}:
<label for="is_view_{{ u.user }}" class="form-label">View Arcade</label>
<input type="checkbox" class="form-control-check" id="is_view_{{ u.user }}" name="is_view" {{ 'checked' if u.is_view else ''}}>&nbsp;|
<label for="is_bookkeep_{{ u.user }}" class="form-label">View Bookkeeping</label>
<input type="checkbox" class="form-control-check" id="is_bookkeep_{{ u.user }}" name="is_bookkeep" {{ 'checked' if u.is_bookkeep else ''}}>&nbsp;|
<label for="is_edit_{{ u.user }}" class="form-label">Edit Arcade</label>
<input type="checkbox" class="form-control-check" id="is_edit_{{ u.user }}" name="is_edit" {{ 'checked' if u.is_edit else ''}}>&nbsp;|
<label for="is_owner_{{ u.user }}" class="form-label">Owner</label>
<input type="checkbox" class="form-control-check" id="is_owner_{{ u.user }}" name="is_owner" {{ 'checked' if u.is_owner else ''}}>&nbsp;|
<button type="submit" class="btn btn-primary">Update</button>
<button type="button" class="btn btn-danger">Delete</button>
</li>
{% endfor %}
</ul>
{% endif %}
{% else %}
<h3>Arcade Not Found</h3>
{% endif %}
{% endblock content %}
{% endblock content %}

View File

@ -1,4 +1,113 @@
{% extends "core/templates/index.jinja" %}
{% block content %}
<h1>Machine Management</h1>
{% endblock content %}
<script type="text/javascript">
function swap_ota() {
let is_cab = document.getElementById("is_cab").checked;
let cbx_ota = document.getElementById("is_ota");
cbx_ota.disabled = !is_cab;
if (cbx_ota.disabled) {
cbx_ota.checked = false;
}
}
</script>
<h1>Machine: {{machine.serial}}</h1>
<h3>Arcade: <a href=/shop/{{ arcade.id }}>{{ arcade.name }}</a>{% if is_acmod %}&nbsp;<button class="btn btn-danger" data-bs-toggle="modal" data-bs-target="#reassign_modal">Reassign</button>{% endif %}</h3>
{% include "core/templates/widgets/err_banner.jinja" %}
{% if success is defined and success == 1 %}
<div style="background-color: #00AA00; padding: 20px; margin-bottom: 10px; width: 15%;">
Info Updated
</div>
{% endif %}
{% if success is defined and success == 2 %}
<div style="background-color: #00AA00; padding: 20px; margin-bottom: 10px; width: 15%;">
Machine successfully reassigned
</div>
{% endif %}
Info
<form style="max-width: 50%;" action="/cab/{{ machine.id }}/info.update" method="post" id="mech_info">
<div class="row">
<div class="col mb-3">
<label for="game" class="form-label">Game</label>
<input type="text" class="form-control" id="game" name="game" placeholder="SXXX" maxlength="4" value="{{ machine.game if machine.game is not none else "" }}">
</div>
<div class="col mb-3">
<label for="country" class="form-label">Country Override</label>
<select id="country" name="country" class="form-select bg-dark text-white">
<option value="" {{ 'selected' if machine.country is none else ''}}>Same As Arcade</option>
<option value="JPN" {{ 'selected' if machine.country == 'JPN' else ''}}>Japan</option>
<option value="USA" {{ 'selected' if machine.country == 'USA' else ''}}>USA</option>
<option value="HKG" {{ 'selected' if machine.country == 'HKG' else ''}}>Hong Kong</option>
<option value="SGP" {{ 'selected' if machine.country == 'SGP' else ''}}>Singapore</option>
<option value="KOR" {{ 'selected' if machine.country == 'KOR' else ''}}>South Korea</option>
<option value="TWN" {{ 'selected' if machine.country == 'TWN' else ''}}>Taiwan</option>
<option value="CHN" {{ 'selected' if machine.country == 'CHN' else ''}}>China</option>
<option value="AUS" {{ 'selected' if machine.country == 'AUS' else ''}}>Australia</option>
<option value="IDN" {{ 'selected' if machine.country == 'IDN' else ''}}>Indonesia</option>
<option value="MMR" {{ 'selected' if machine.country == 'MMR' else ''}}>Myanmar</option>
<option value="MYS" {{ 'selected' if machine.country == 'MYS' else ''}}>Malaysia</option>
<option value="NZL" {{ 'selected' if machine.country == 'NZL' else ''}}>New Zealand</option>
<option value="PHL" {{ 'selected' if machine.country == 'PHL' else ''}}>Philippines</option>
<option value="THA" {{ 'selected' if machine.country == 'THA' else ''}}>Thailand</option>
<option value="VNM" {{ 'selected' if machine.country == 'VNM' else ''}}>Vietnam</option>
</select>
</div>
<div class="col mb-3">
<label for="tz" class="form-label">Timezone Override</label>
<input type="text" class="form-control" id="tz" name="tz" placeholder="+09:00" maxlength="6" value="{{ machine.timezone if machine.timezone is not none else "" }}">
</div>
</div>
<div class="row">
<div class="col mb-3">
<input type="checkbox" class="form-control-check" id="is_cab" name="is_cab" {{ 'checked' if machine.is_cab else ''}} onchange="swap_ota()">
<label for="is_cab" class="form-label">Real Cabinet</label>
</div>
<div class="col mb-3">
<input type="checkbox" class="form-control-check" id="is_ota" name="is_ota" {{ 'checked' if machine.ota_enable else ''}}>
<label for="is_ota" class="form-label">Allow OTA updates</label>
</div>
<div class="col mb-3">
</div>
</div>
<div class="row">
<div class="col mb-3">
<label for="memo" class="form-label">Memo</label>
<input type="text" class="form-control" id="memo" name="memo" maxlength="255" value="{{ machine.memo if machine.memo is not none else "" }}">
</div>
</div>
{% if can_edit %}
<div class="row">
<div class="col mb-3">
<input type="submit" value="Update" class="btn btn-primary">
</div>
</div>
{% endif %}
</form>
{% if is_acmod %}
<form id="frm_reassign" method="post" action="/cab/{{ machine.id }}/reassign" style="outline: 0px;">
<div class="modal" tabindex="-1" id="reassign_modal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Reassign {{ machine.serial }}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p>This will reassign this cabinet from the current arcade "{{ arcade.name }}" to the arcade who's ID you enter below.</p>
<label for="new_arcade" class="form-label">New Arcade</label>
<input type="text" class="form-control" id="new_arcade" name="new_arcade" value="{{ arcade.id }}">
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<button type="submit" class="btn btn-primary">Save changes</button>
</div>
</div>
</div>
</div>
</form>
{% endif %}
<script type="text/javascript">
swap_ota();
</script>
{% endblock content %}

View File

@ -137,6 +137,11 @@
<input type="text" class="form-control" id="shopIp" name="shopIp">
</div>
<br />
<div class="form-group">
<label for="shopOwner">Owner User ID</label>
<input type="text" class="form-control" id="shopOwner" name="shopOwner">
</div>
<br />
<button type="submit" class="btn btn-primary">Add</button>
</form>
</div>

View File

@ -159,19 +159,10 @@ Update successful
</form>
{% if arcades is defined and arcades|length > 0 %}
<h2>Arcades</h2>
<h2>Arcades you manage</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>
<li><h3><a href=/shop/{{a.id}}>{{ a.name }}</a></h3></li>
{% endfor %}
</ul>
{% endif %}

View File

@ -27,6 +27,10 @@ Access Denied
Card already registered
{% elif error == 13 %}
AmusementIC Access Codes beginning with 5 must have IDm
{% elif error == 14 %}
Arcade does not exist
{% elif error == 15 %}
Some info failed to update
{% else %}
An unknown error occoured
{% endif %}