add web
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
{% extends '__layout__.html' %}
|
||||
{% block title %}Admin{% endblock %}
|
||||
{% block head %}
|
||||
<link href="/static/css/admin.css" rel="stylesheet"/> <!-- Its just easier to resuse this-->
|
||||
<style>
|
||||
.show-on-hover {
|
||||
filter: blur(4px);
|
||||
transition: 0.2s;
|
||||
}
|
||||
.show-on-hover:hover {
|
||||
filter: blur(0px);
|
||||
transition: 0.2s;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div id="main">
|
||||
<div class="container" style="max-width: 800px;">
|
||||
<a class="mb-2" href="/admin/manage-users/{{userObj.id}}"> < Return to User Page</a>
|
||||
<div class="d-flex align-items-center">
|
||||
<img height="100px" style="aspect-ratio: 1/1;" class="rounded-2" src="/Thumbs/Avatar.ashx?userId={{userObj.id}}&x=100&y=100">
|
||||
<div>
|
||||
<h1 class="m-0 text-secondary">Ban <span class="text-white">{{userObj.username}}</span></h1>
|
||||
<p class="text-secondary m-0" style="font-size: 12px;">UserId: <span class="text-white">{{userObj.id}}</span></p>
|
||||
</div>
|
||||
</div>
|
||||
{% if LastestUserBanObj != None: %}
|
||||
<div class="alert alert-danger">
|
||||
This user currently has a ongoing ban, please revoke the previous ban before banning again.
|
||||
</div>
|
||||
{% endif %}
|
||||
{% with messages = get_flashed_messages() %}
|
||||
{% if messages %}
|
||||
<div>
|
||||
{% for message in messages %}
|
||||
<div class="alert border border-danger p-2 text-center messagealerts">
|
||||
{{ message }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
<div class="linebreak"></div>
|
||||
<form method="post">
|
||||
<div class="form-floating">
|
||||
<select name="ban_type" class="form-control" id="ban-type-select">
|
||||
<option value="0" selected>Warning</option>
|
||||
<option value="1">1 Day Ban</option>
|
||||
<option value="2">3 Day Ban</option>
|
||||
<option value="3">7 Day Ban</option>
|
||||
<option value="4">14 Day Ban</option>
|
||||
<option value="5">30 Day Ban</option>
|
||||
<option value="6">Account Deleted</option>
|
||||
</select>
|
||||
<label for="ban-type-select">Ban Type</label>
|
||||
</div>
|
||||
<div class="form-floating mt-2">
|
||||
<input type="text" class="form-control" name="ban_reason" id="ban-reason-input" placeholder="Reason" required>
|
||||
<label for="ban-reason-input">Reason</label>
|
||||
</div>
|
||||
<div class="form-floating mt-2">
|
||||
<input type="text" class="form-control" name="ban_notes" id="ban-notes-input" placeholder="Notes" required>
|
||||
<label for="ban-notes-input">Moderator Note ( Internal )</label>
|
||||
</div>
|
||||
<p class="m-0 text-secondary" style="font-size: 13px;">Please try to include links to evidence that lead to this ban</p>
|
||||
<div class="form-floating mt-2">
|
||||
<input type="password" class="form-control" name="totp_code" id="totp_code-input" placeholder="2FA Code" required>
|
||||
<label for="ban-notes-public-input">2FA Code</label>
|
||||
</div>
|
||||
<input type="hidden" name="csrf_token" value="{{csrf_token()}}">
|
||||
<div class="linebreak"></div>
|
||||
{% if not LastestUserBanObj != None: %}
|
||||
<button type="submit" class="btn btn-danger mt-2 w-100 btn-sm">Ban User</button>
|
||||
{% else%}
|
||||
<button class="btn btn-danger mt-2 w-100 btn-sm" disabled>Ban User</button>
|
||||
{% endif %}
|
||||
</form>
|
||||
{% if LastestUserBanObj != None: %}
|
||||
<form method="post" action="/admin/manage-users/{{userObj.id}}/ban-user/revoke-ban?banid={{LastestUserBanObj.id}}">
|
||||
<input type="hidden" name="csrf_token" value="{{csrf_token()}}">
|
||||
<button class="btn btn-warning mt-2 w-100 btn-sm">Unban User</button>
|
||||
</form>
|
||||
{%endif%}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,69 @@
|
||||
{% extends '__layout__.html' %}
|
||||
{% block title %}Admin{% endblock %}
|
||||
{% block head %}
|
||||
<link href="/static/css/admin.css" rel="stylesheet"/> <!-- Its just easier to resuse this-->
|
||||
<style>
|
||||
.show-on-hover {
|
||||
filter: blur(4px);
|
||||
transition: 0.2s;
|
||||
}
|
||||
.show-on-hover:hover {
|
||||
filter: blur(0px);
|
||||
transition: 0.2s;
|
||||
}
|
||||
.bg-dark {
|
||||
background-color: rgb(32, 32, 32) !important;
|
||||
box-shadow: 0px 0px 5px 0px rgb(0,0,0);
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div id="main">
|
||||
<div class="container" style="max-width: 800px;">
|
||||
<a class="mb-2" href="/admin/manage-users/{{userObj.id}}"> < Return to User Page</a>
|
||||
<div class="d-flex align-items-center">
|
||||
<img height="100px" style="aspect-ratio: 1/1;" class="rounded-2" src="/Thumbs/Avatar.ashx?userId={{userObj.id}}&x=100&y=100">
|
||||
<div>
|
||||
<h1 class="m-0 {% if userObj.accountstatus == 1 %}text-white{% elif userObj.accountstatus == 2 %}text-warning{% elif userObj.accountstatus == 3%}text-danger{% else %}text-decoration-line-through text-white{%endif%}">{{userObj.username}}<span class="text-secondary"> Ban History</span></h1>
|
||||
<p class="text-secondary m-0" style="font-size: 12px;">UserId: <span class="text-white">{{userObj.id}}</span></p>
|
||||
</div>
|
||||
</div>
|
||||
{% with messages = get_flashed_messages() %}
|
||||
{% if messages %}
|
||||
<div>
|
||||
{% for message in messages %}
|
||||
<div class="alert border border-danger p-2 text-center messagealerts">
|
||||
{{ message }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
|
||||
<div class="p-1">
|
||||
{% for banRecord in BanHistory %}
|
||||
<div class="w-100 bg-dark p-2 mb-2 mt-2">
|
||||
<h4 class="m-0">{{banRecord.ban_type.name}}</h4>
|
||||
<p class="m-0 text-secondary" style="font-size: 14px;">Reason: <span class="text-white">{{banRecord.reason}}</span></p>
|
||||
<p class="m-0 text-secondary" style="font-size: 14px;">Moderator Note: <span class="text-white">{{banRecord.moderator_note}}</span></p>
|
||||
<p class="m-0 text-secondary" style="font-size: 12px;">Ban Author: <a href="/admin/manage-users/{{banRecord.author_userid}}">{{GetBanAuthorName(banRecord)}}</a></p>
|
||||
<div class="linebreak"></div>
|
||||
<div class="d-flex">
|
||||
<p class="m-0 text-secondary me-auto ms-auto" style="font-size: 14px;">Banned At: <span class="text-white">{{banRecord.created_at}}</span></p>
|
||||
<p class="m-0 text-secondary me-auto ms-auto" style="font-size: 14px;">Expires At: <span class="text-white">{{banRecord.expires_at}}</span></p>
|
||||
<p class="m-0 text-secondary me-auto ms-auto" style="font-size: 14px;">Acknowledged: <span class="text-white">{{banRecord.acknowledged}}</span></p>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% if len(BanHistory.items) == 0 %}
|
||||
<p class="text-secondary m-5 text-center w-100">No previous bans</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="align-items-center d-flex justify-content-center mt-2 mb-2">
|
||||
<a class="ms-auto m-0 text-decoration-none {% if not BanHistory.has_prev %}text-secondary{%endif%}" {% if BanHistory.has_prev %}href="/admin/manage-users/{{userObj.id}}/ban-history?page={{BanHistory.prev_num}}"{%endif%}>Previous</a>
|
||||
<p class="ms-2 me-2 text-white m-0">Page {{BanHistory.page}} of {{BanHistory.pages}}</p>
|
||||
<a class="me-auto m-0 text-decoration-none {% if not BanHistory.has_next %}text-secondary{%endif%}" {% if BanHistory.has_next %}href="/admin/manage-users/{{userObj.id}}/ban-history?page={{BanHistory.next_num}}"{%endif%}>Next</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,65 @@
|
||||
{% extends '__layout__.html' %}
|
||||
{% block title %}Admin{% endblock %}
|
||||
{% block head %}
|
||||
<link href="/static/css/admin.css" rel="stylesheet"/> <!-- Its just easier to resuse this-->
|
||||
<style>
|
||||
.show-on-hover {
|
||||
filter: blur(4px);
|
||||
transition: 0.2s;
|
||||
}
|
||||
.show-on-hover:hover {
|
||||
filter: blur(0px);
|
||||
transition: 0.2s;
|
||||
}
|
||||
.bg-dark {
|
||||
background-color: rgb(32, 32, 32) !important;
|
||||
box-shadow: 0px 0px 5px 0px rgb(0,0,0);
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div id="main">
|
||||
<div class="container" style="max-width: 1200px;">
|
||||
<a class="mb-2" href="/admin/manage-users/{{userObj.id}}"> < Return to User Page</a>
|
||||
<div class="d-flex align-items-center">
|
||||
<img height="100px" style="aspect-ratio: 1/1;" class="rounded-2" src="/Thumbs/Avatar.ashx?userId={{userObj.id}}&x=100&y=100">
|
||||
<div>
|
||||
<h1 class="m-0 {% if userObj.accountstatus == 1 %}text-white{% elif userObj.accountstatus == 2 %}text-warning{% elif userObj.accountstatus == 3%}text-danger{% else %}text-decoration-line-through text-white{%endif%}">{{userObj.username}}<span class="text-secondary"> Game Sessions</span></h1>
|
||||
<p class="text-secondary m-0" style="font-size: 12px;">UserId: <span class="text-white">{{userObj.id}}</span></p>
|
||||
</div>
|
||||
</div>
|
||||
<table class="table table-dark table-striped">
|
||||
<thead>
|
||||
<tr class="rounded-top">
|
||||
<th scope="col" style="width: 5%;">ID</th>
|
||||
<th scope="col" style="width: 27.5%;">JobId</th>
|
||||
<th scope="col" style="width: 22.5%;">Joined At</th>
|
||||
<th scope="col" style="width: 22.5%;">Left At</th>
|
||||
<th scope="col" style="width: 22.5%;">Place</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="table-group-divider" style="border-color: rgb(20,20,20);">
|
||||
{% for session in GameSessions: %}
|
||||
<tr>
|
||||
<th scope="row">{{session.id}}</th>
|
||||
<td>{{session.serveruuid}}</td>
|
||||
<td>{{session.joined_at}} UTC</td>
|
||||
<td>{{session.left_at}} UTC</td>
|
||||
<td><a class="text-primary" href="/games/{{session.place_id}}/--">{{get_place_name(session.place_id)}}</a> ({{session.place_id}})</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% if len(GameSessions.items) == 0: %}
|
||||
<div class="text-center">
|
||||
<p class="text-secondary">No Game Session Logs Found</p>
|
||||
</div>
|
||||
{%endif%}
|
||||
<div class="align-items-center d-flex justify-content-center mt-2 mb-2">
|
||||
<a class="ms-auto m-0 text-decoration-none {% if not GameSessions.has_prev %}text-secondary{%endif%}" {% if GameSessions.has_prev %}href="/admin/manage-users/{{userObj.id}}/game-sessions?page={{GameSessions.prev_num}}"{%endif%}>Previous</a>
|
||||
<p class="ms-2 me-2 text-white m-0">Page {{GameSessions.page}} of {{GameSessions.pages}}</p>
|
||||
<a class="me-auto m-0 text-decoration-none {% if not GameSessions.has_next %}text-secondary{%endif%}" {% if GameSessions.has_next %}href="/admin/manage-users/{{userObj.id}}/game-sessions?page={{GameSessions.next_num}}"{%endif%}>Next</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,74 @@
|
||||
{% extends '__layout__.html' %}
|
||||
{% block title %}Admin{% endblock %}
|
||||
{% block head %}
|
||||
<link href="/static/css/admin.css" rel="stylesheet"/> <!-- Its just easier to resuse this-->
|
||||
<style>
|
||||
.show-on-hover {
|
||||
filter: blur(4px);
|
||||
transition: 0.2s;
|
||||
}
|
||||
.show-on-hover:hover {
|
||||
filter: blur(0px);
|
||||
transition: 0.2s;
|
||||
}
|
||||
.bg-dark {
|
||||
background-color: rgb(32, 32, 32) !important;
|
||||
box-shadow: 0px 0px 5px 0px rgb(0,0,0);
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div id="main">
|
||||
<div class="container" style="max-width: 1200px;">
|
||||
<a class="mb-2" href="/admin/manage-users/{{userObj.id}}"> < Return to User Page</a>
|
||||
<div class="d-flex align-items-center">
|
||||
<img height="100px" style="aspect-ratio: 1/1;" class="rounded-2" src="/Thumbs/Avatar.ashx?userId={{userObj.id}}&x=100&y=100">
|
||||
<div>
|
||||
<h1 class="m-0 {% if userObj.accountstatus == 1 %}text-white{% elif userObj.accountstatus == 2 %}text-warning{% elif userObj.accountstatus == 3%}text-danger{% else %}text-decoration-line-through text-white{%endif%}">{{userObj.username}}<span class="text-secondary"> Invite Keys</span></h1>
|
||||
<p class="text-secondary m-0" style="font-size: 12px;">UserId: <span class="text-white">{{userObj.id}}</span></p>
|
||||
</div>
|
||||
</div>
|
||||
<table class="table table-dark table-striped">
|
||||
<thead>
|
||||
<tr class="rounded-top">
|
||||
<th scope="col" style="width: 5%;">ID</th>
|
||||
<th scope="col" style="width: 20%;">Key</th>
|
||||
<th scope="col" style="width: 15%;">Created</th>
|
||||
<th scope="col" style="width: 15%;">Used On</th>
|
||||
<th scope="col" style="width: 45%;">Used By</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="table-group-divider" style="border-color: rgb(20,20,20);">
|
||||
{% for invite in InviteKeys: %}
|
||||
<tr>
|
||||
<th scope="row">{{invite.id}}</th>
|
||||
<td>{{invite.key}}</td>
|
||||
<td>{{invite.created_at}} UTC</td>
|
||||
<td>{% if invite.used_on != None: %}{{invite.used_on}} UTC{%else%}Unused{%endif%}</td>
|
||||
<td>
|
||||
{% if invite.used_by != None: %}
|
||||
<a href="/admin/manage-users/{{invite.used_by}}" class="text-decoration-none text-truncate">
|
||||
<img class="rounded" style="aspect-ratio: 1/1;width: 35px;" src="/Thumbs/Head.ashx?x=48&y=48&userId={{invite.used_by}}">
|
||||
{{invite.user.username}}
|
||||
</a>
|
||||
{% else%}
|
||||
Unused
|
||||
{%endif%}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% if len(InviteKeys.items) == 0: %}
|
||||
<div class="text-center">
|
||||
<p class="text-secondary">No Invite Keys found</p>
|
||||
</div>
|
||||
{%endif%}
|
||||
<div class="align-items-center d-flex justify-content-center mt-2 mb-2">
|
||||
<a class="ms-auto m-0 text-decoration-none {% if not InviteKeys.has_prev %}text-secondary{%endif%}" {% if InviteKeys.has_prev %}href="/admin/manage-users/{{userObj.id}}/invite-keys?page={{InviteKeys.prev_num}}"{%endif%}>Previous</a>
|
||||
<p class="ms-2 me-2 text-white m-0">Page {{InviteKeys.page}} of {{InviteKeys.pages}}</p>
|
||||
<a class="me-auto m-0 text-decoration-none {% if not InviteKeys.has_next %}text-secondary{%endif%}" {% if InviteKeys.has_next %}href="/admin/manage-users/{{userObj.id}}/invite-keys?page={{InviteKeys.next_num}}"{%endif%}>Next</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,105 @@
|
||||
{% extends '__layout__.html' %}
|
||||
{% block title %}Admin{% endblock %}
|
||||
{% block head %}
|
||||
<link href="/static/css/admin.css" rel="stylesheet"/> <!-- Its just easier to resuse this-->
|
||||
<style>
|
||||
.show-on-hover {
|
||||
filter: blur(4px);
|
||||
transition: 0.2s;
|
||||
}
|
||||
.show-on-hover:hover {
|
||||
filter: blur(0px);
|
||||
transition: 0.2s;
|
||||
}
|
||||
.bg-dark {
|
||||
background-color: rgb(32, 32, 32) !important;
|
||||
box-shadow: 0px 0px 5px 0px rgb(0,0,0);
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div id="main">
|
||||
<div class="container" style="max-width: 800px;">
|
||||
<a class="mb-2" href="/admin/manage-users/{{userObj.id}}"> < Return to User Page</a>
|
||||
<div class="d-flex align-items-center">
|
||||
<img height="100px" style="aspect-ratio: 1/1;" class="rounded-2" src="/Thumbs/Avatar.ashx?userId={{userObj.id}}&x=100&y=100">
|
||||
<div>
|
||||
<h1 class="m-0 {% if userObj.accountstatus == 1 %}text-white{% elif userObj.accountstatus == 2 %}text-warning{% elif userObj.accountstatus == 3%}text-danger{% else %}text-decoration-line-through text-white{%endif%}">{{userObj.username}}<span class="text-secondary"> Login History</span></h1>
|
||||
<p class="text-secondary m-0" style="font-size: 12px;">UserId: <span class="text-white">{{userObj.id}}</span></p>
|
||||
</div>
|
||||
</div>
|
||||
{% if len(AlternateAccounts) != 0 %}
|
||||
<div class="border-top border-bottom p-2 ">
|
||||
<h5 class="m-0 mb-1">Possible Alt Accounts ( {{len( AlternateAccounts )}} )</h5>
|
||||
{% for altAccount in AlternateAccounts %}
|
||||
<a class="d-flex align-items-center text-white text-decoration-none" href="/admin/manage-users/{{altAccount.id}}/login-history">
|
||||
<img height="50px" style="aspect-ratio: 1/1;" class="rounded-2" src="/Thumbs/Avatar.ashx?userId={{altAccount.id}}&x=50&y=50">
|
||||
<div>
|
||||
<h5 class="m-0 {% if altAccount.accountstatus == 1 %}text-white{% elif altAccount.accountstatus == 2 %}text-warning{% elif altAccount.accountstatus == 3%}text-danger{% else %}text-decoration-line-through text-white{%endif%}">
|
||||
{{altAccount.username}}
|
||||
{% if altAccount.flags['hwidmatch'] %}
|
||||
<span class="badge rounded-pill text-bg-light ms-1" style="font-size: 12px;font-weight: 400;">HWID</span>
|
||||
{% endif %}
|
||||
{% if altAccount.flags['sessiontokenmatch'] %}
|
||||
<span class="badge rounded-pill text-bg-danger ms-1" style="font-size: 12px;font-weight: 400;">SessionToken</span>
|
||||
{% endif %}
|
||||
{% if altAccount.flags['ipmatch'] %}
|
||||
<span class="badge rounded-pill text-bg-warning ms-1" style="font-size: 12px;font-weight: 400;">IPAddress</span>
|
||||
{% endif %}
|
||||
{% if altAccount.flags['useragentmatch'] %}
|
||||
<span class="badge rounded-pill text-bg-primary ms-1" style="font-size: 12px;font-weight: 400;">UserAgent</span>
|
||||
{% endif %}
|
||||
</h5>
|
||||
<p class="text-secondary m-0" style="font-size: 12px;">UserId: <span class="text-white">{{altAccount.id}}</span></p>
|
||||
</div>
|
||||
</a>
|
||||
{%endfor%}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% with messages = get_flashed_messages() %}
|
||||
{% if messages %}
|
||||
<div>
|
||||
{% for message in messages %}
|
||||
<div class="alert border border-danger p-2 text-center messagealerts">
|
||||
{{ message }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
{% if canViewSensitiveInfo %}
|
||||
<div class="p-1">
|
||||
{% for loginRecord in LoginHistory %}
|
||||
<div class="w-100 bg-dark p-2 mb-2 mt-2">
|
||||
<div class="d-flex">
|
||||
<div>
|
||||
<h5 class="m-0">{{loginRecord.ip}}</h5>
|
||||
<p class="m-0 text-secondary" style="font-size: 13px;">Timestamp: <span class="text-white">{{loginRecord.timestamp}} UTC</span></p>
|
||||
</div>
|
||||
<a data-bs-toggle="collapse" href="#additional-record-data-{{loginRecord.id}}" role="button" class="ms-auto mt-auto mb-auto me-2" style="font-size: 20px;"><i class="bi bi-info-circle"></i></a>
|
||||
</div>
|
||||
<div class="collapse mt-1" id="additional-record-data-{{loginRecord.id}}">
|
||||
<div class="border p-1">
|
||||
<p class="m-0 text-secondary" style="font-size: 13px;">Login Record ID: <span class="text-white">{{loginRecord.id}}</span></p>
|
||||
<div class="border mt-1" style="font-size: 13px;padding: 4px;">
|
||||
<p class="m-0 text-secondary">User Agent</p>
|
||||
<p class="text-white m-0" style="word-break: break-all;white-space: normal;">{{loginRecord.useragent}}</p>
|
||||
</div>
|
||||
<div class="border mt-1" style="font-size: 13px;padding: 4px;">
|
||||
<p class="m-0 text-secondary">Session Token</p>
|
||||
<p class="text-white m-0" style="word-break: break-all;white-space: normal;">{{loginRecord.session_token}}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="align-items-center d-flex justify-content-center mt-2 mb-2">
|
||||
<a class="ms-auto m-0 text-decoration-none {% if not LoginHistory.has_prev %}text-secondary{%endif%}" {% if LoginHistory.has_prev %}href="/admin/manage-users/{{userObj.id}}/login-history?page={{LoginHistory.prev_num}}"{%endif%}>Previous</a>
|
||||
<p class="ms-2 me-2 text-white m-0">Page {{LoginHistory.page}} of {{LoginHistory.pages}}</p>
|
||||
<a class="me-auto m-0 text-decoration-none {% if not LoginHistory.has_next %}text-secondary{%endif%}" {% if LoginHistory.has_next %}href="/admin/manage-users/{{userObj.id}}/login-history?page={{LoginHistory.next_num}}"{%endif%}>Next</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,208 @@
|
||||
{% extends '__layout__.html' %}
|
||||
{% block title %}Admin{% endblock %}
|
||||
{% block head %}
|
||||
<link href="/static/css/admin.css" rel="stylesheet"/> <!-- Its just easier to resuse this-->
|
||||
<style>
|
||||
.show-on-hover {
|
||||
filter: blur(4px);
|
||||
transition: 0.2s;
|
||||
}
|
||||
.show-on-hover:hover {
|
||||
filter: blur(0px);
|
||||
transition: 0.2s;
|
||||
}
|
||||
.bg-dark {
|
||||
background-color: rgb(32, 32, 32) !important;
|
||||
box-shadow: 0px 0px 5px 0px rgb(0,0,0);
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div id="main" data-csrf-token="{{ csrf_token() }}" data-userid="{{userObj.id}}">
|
||||
<div class="container" style="max-width: 800px;">
|
||||
<a class="mb-2" href="/admin/manage-users/{{userObj.id}}"> < Return to User Page</a>
|
||||
<div class="d-flex align-items-center">
|
||||
<img height="100px" style="aspect-ratio: 1/1;" class="rounded-2" src="/Thumbs/Avatar.ashx?userId={{userObj.id}}&x=100&y=100">
|
||||
<div>
|
||||
<h1 class="m-0 {% if userObj.accountstatus == 1 %}text-white{% elif userObj.accountstatus == 2 %}text-warning{% elif userObj.accountstatus == 3%}text-danger{% else %}text-decoration-line-through text-white{%endif%}">{{userObj.username}}<span class="text-secondary"> Admin Permissions</span></h1>
|
||||
<p class="text-secondary m-0" style="font-size: 12px;">UserId: <span class="text-white">{{userObj.id}}</span></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="border rounded p-2 mt-2 mb-2">
|
||||
<p class="mt-5 mb-5 w-100 text-center text-white" id="status-text" style="display: none;"></p>
|
||||
<div id="data-container">
|
||||
<div class="w-100 bg-dark p-2 mb-2 mt-2 admin-permission-template" style="display: none;">
|
||||
<div class="d-flex align-items-center w-100">
|
||||
<a data-bs-toggle="collapse" href="#admin-permission-info-PermissionName" role="button" class="mt-auto mb-auto me-2 permission-expand-info" style="font-size: 20px;"><i class="bi bi-info-circle"></i></a>
|
||||
<div class="d-flex align-items-center">
|
||||
<h5 class="m-0 permission-name-text">Admin Permission name</h5>
|
||||
<i class="bi ms-2 permission-icon" style="display: none;"></i>
|
||||
</div>
|
||||
<div class="form-check form-switch m-0 me-2 ms-auto">
|
||||
<input class="form-check-input" type="checkbox" role="switch">
|
||||
</div>
|
||||
</div>
|
||||
<div class="collapse mt-1 permission-info-container" id="admin-permission-info-PermissionName">
|
||||
<div class="border p-1">
|
||||
<p class="m-0 text-secondary" style="font-size: 13px;">Description: <span class="text-white permission-description-text">Permission Description</span></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<input type="password" class="form-control mb-2" placeholder="2FA Code" id="2fa-code-input">
|
||||
<input type="submit" class="btn btn-primary w-100 mb-2" value="Save Changes" id="save-changes-button">
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", async function () {
|
||||
const ActionCSRFToken = document.getElementById("main").getAttribute("data-csrf-token");
|
||||
const userId = document.getElementById("main").getAttribute("data-userid");
|
||||
const statusText = document.getElementById("status-text");
|
||||
const dataContainer = document.getElementById("data-container");
|
||||
const permissionTemplate = document.getElementsByClassName("admin-permission-template")[0];
|
||||
const saveChangesButton = document.getElementById("save-changes-button");
|
||||
const twoFactorCodeInput = document.getElementById("2fa-code-input");
|
||||
|
||||
var isPermissionsLoaded = false;
|
||||
|
||||
async function setStatusText( new_text ) {
|
||||
if ( new_text ) {
|
||||
statusText.innerText = new_text;
|
||||
statusText.style.display = "block";
|
||||
} else {
|
||||
statusText.style.display = "none";
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchUserPermissions() {
|
||||
const response = await fetch(`/admin/manage-users/${userId}/manage-admin-permissions/api/fetch-permissions`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"X-CSRF-Token": ActionCSRFToken
|
||||
}
|
||||
});
|
||||
const data = await response.json();
|
||||
return data;
|
||||
}
|
||||
|
||||
async function loadPermission( permission_data ) {
|
||||
const newPermission = permissionTemplate.cloneNode(true);
|
||||
newPermission.style.display = "block";
|
||||
newPermission.classList.remove("admin-permission-template");
|
||||
newPermission.classList.add("admin-permission-container");
|
||||
newPermission.setAttribute("data-permission-internal-name", permission_data.internal_name);
|
||||
|
||||
newPermission.getElementsByClassName("permission-name-text")[0].innerText = permission_data.friendly_name;
|
||||
newPermission.getElementsByClassName("permission-description-text")[0].innerText = permission_data.description;
|
||||
newPermission.getElementsByClassName("form-check-input")[0].checked = permission_data.hasPermission;
|
||||
newPermission.getElementsByClassName("permission-expand-info")[0].setAttribute("href", `#admin-permission-info-${permission_data.internal_name}`);
|
||||
newPermission.getElementsByClassName("permission-info-container")[0].setAttribute("id", `admin-permission-info-${permission_data.internal_name}`);
|
||||
|
||||
if ( permission_data.bi_icon ) {
|
||||
newPermission.getElementsByClassName("permission-icon")[0].style.display = "inline-block";
|
||||
newPermission.getElementsByClassName("permission-icon")[0].classList.add(permission_data.bi_icon);
|
||||
}
|
||||
|
||||
dataContainer.appendChild(newPermission);
|
||||
}
|
||||
|
||||
async function refreshData( new_data ) {
|
||||
dataContainer.innerHTML = "";
|
||||
|
||||
for ( const permission of new_data ) {
|
||||
await loadPermission( permission );
|
||||
}
|
||||
}
|
||||
|
||||
async function attemptFetchAndLoad() {
|
||||
dataContainer.innerHTML = "";
|
||||
isPermissionsLoaded = false;
|
||||
saveChangesButton.disabled = true;
|
||||
twoFactorCodeInput.disabled = true;
|
||||
setStatusText("Fetching Admin Permissions")
|
||||
|
||||
try{
|
||||
const permissionsData = await fetchUserPermissions();
|
||||
await refreshData( permissionsData );
|
||||
setStatusText();
|
||||
isPermissionsLoaded = true;
|
||||
twoFactorCodeInput.disabled = false;
|
||||
} catch (e) {
|
||||
setStatusText("Failed to fetch Admin Permissions");
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
twoFactorCodeInput.addEventListener("input", async function () {
|
||||
if ( !isPermissionsLoaded ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( twoFactorCodeInput.value.length != 6 ) {
|
||||
saveChangesButton.disabled = true;
|
||||
}
|
||||
|
||||
if ( twoFactorCodeInput.value.length == 6 ) {
|
||||
saveChangesButton.disabled = false;
|
||||
}
|
||||
});
|
||||
|
||||
saveChangesButton.addEventListener("click", async function() {
|
||||
if ( !isPermissionsLoaded ) {
|
||||
return;
|
||||
}
|
||||
if ( twoFactorCodeInput.value.length != 6 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
isPermissionsLoaded = false;
|
||||
saveChangesButton.disabled = true;
|
||||
|
||||
const permissionsData = [];
|
||||
const permissionContainers = document.getElementsByClassName("admin-permission-container");
|
||||
for ( const permissionContainer of permissionContainers ) {
|
||||
if ( permissionContainer.getElementsByClassName("form-check-input")[0].checked ) {
|
||||
permissionsData.push(permissionContainer.getAttribute("data-permission-internal-name"));
|
||||
}
|
||||
}
|
||||
|
||||
setStatusText("Saving Admin Permissions");
|
||||
|
||||
try {
|
||||
const response = await fetch(`/admin/manage-users/${userId}/manage-admin-permissions/api/set-permissions`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"X-CSRF-Token": ActionCSRFToken
|
||||
},
|
||||
body: JSON.stringify({
|
||||
"permissions": permissionsData,
|
||||
"2fa_code": twoFactorCodeInput.value
|
||||
})
|
||||
});
|
||||
const data = await response.json();
|
||||
if ( data.success ) {
|
||||
alert("Successfully saved Admin Permissions");
|
||||
twoFactorCodeInput.value = "";
|
||||
await attemptFetchAndLoad();
|
||||
return;
|
||||
} else {
|
||||
alert(`Failed to save Admin Permissions, reason: ${data.reason}`);
|
||||
}
|
||||
} catch (e) {
|
||||
alert("Failed to save Admin Permissions");
|
||||
console.error(e);
|
||||
}
|
||||
|
||||
isPermissionsLoaded = true;
|
||||
saveChangesButton.disabled = false;
|
||||
twoFactorCodeInput.value = "";
|
||||
twoFactorCodeInput.disabled = false;
|
||||
})
|
||||
|
||||
await attemptFetchAndLoad();
|
||||
})
|
||||
</script>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,94 @@
|
||||
{% extends '__layout__.html' %}
|
||||
{% block title %}Manage User{% endblock %}
|
||||
|
||||
{% block head %}
|
||||
<link href="/static/css/admin.css" rel="stylesheet"/>
|
||||
<style>
|
||||
.show-on-hover {
|
||||
filter: blur(4px);
|
||||
transition: 0.2s;
|
||||
}
|
||||
.show-on-hover:hover {
|
||||
filter: blur(0px);
|
||||
transition: 0.2s;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div id="main">
|
||||
<div class="container" style="max-width: 800px;">
|
||||
<a class="mb-2 d-block" href="/admin/manage-users/{{userObj.id}}">← Return to User Page</a>
|
||||
<div class="d-flex align-items-center mb-3">
|
||||
<img height="100px" style="aspect-ratio: 1/1;" class="rounded-2" src="/Thumbs/Avatar.ashx?userId={{userObj.id}}&x=100&y=100">
|
||||
<div class="ms-3">
|
||||
<h1 class="m-0 text-secondary">Manage Robux & Builders Club for <span class="text-white">{{userObj.username}}</span></h1>
|
||||
<p class="text-secondary m-0" style="font-size: 12px;">UserId: <span class="text-white">{{userObj.id}}</span></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% with messages = get_flashed_messages() %}
|
||||
{% if messages %}
|
||||
<div>
|
||||
{% for message in messages %}
|
||||
<div class="alert border border-danger p-2 text-center messagealerts">
|
||||
{{ message }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
|
||||
<div class="linebreak"></div>
|
||||
|
||||
<div class="robux-management">
|
||||
<h3>Robux & Tix Management</h3>
|
||||
<form method="POST" id="robux-form">
|
||||
<div class="form-floating">
|
||||
<select class="form-control" id="action-select" required>
|
||||
<option value="add">Add</option>
|
||||
<option value="subtract">Subtract</option>
|
||||
</select>
|
||||
<label for="action-select">Action</label>
|
||||
</div>
|
||||
|
||||
<div class="form-floating mt-2">
|
||||
<select class="form-control" name="currency_type" id="currency-type-select" required>
|
||||
<option value="robux">Robux</option>
|
||||
<option value="tix">Tix</option>
|
||||
</select>
|
||||
<label for="currency-type-select">Currency</label>
|
||||
</div>
|
||||
|
||||
<div class="form-floating mt-2">
|
||||
<input type="number" class="form-control" name="amount" id="amount-input" placeholder="Amount" required>
|
||||
<label for="amount-input">Amount</label>
|
||||
</div>
|
||||
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||
<div class="linebreak"></div>
|
||||
<button type="submit" class="btn btn-primary mt-2 w-100 btn-sm">Submit Currency Change</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="linebreak"></div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% block scripts %}
|
||||
<script>
|
||||
const actionSelect = document.getElementById("action-select");
|
||||
const amountInput = document.getElementById("amount-input");
|
||||
const robuxForm = document.getElementById("robux-form");
|
||||
|
||||
robuxForm.addEventListener("submit", function(event) {
|
||||
if (actionSelect.value === "subtract") {
|
||||
amountInput.value = -Math.abs(amountInput.value);
|
||||
} else {
|
||||
amountInput.value = Math.abs(amountInput.value);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,61 @@
|
||||
{% extends '__layout__.html' %}
|
||||
{% block title %}Admin{% endblock %}
|
||||
{% block head %}
|
||||
<link href="/static/css/admin.css" rel="stylesheet"/> <!-- Its just easier to resuse this-->
|
||||
<style>
|
||||
.show-on-hover {
|
||||
filter: blur(4px);
|
||||
transition: 0.2s;
|
||||
}
|
||||
.show-on-hover:hover {
|
||||
filter: blur(0px);
|
||||
transition: 0.2s;
|
||||
}
|
||||
.bg-dark {
|
||||
background-color: rgb(32, 32, 32) !important;
|
||||
box-shadow: 0px 0px 5px 0px rgb(0,0,0);
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div id="main">
|
||||
<div class="container" style="max-width: 800px;">
|
||||
<a class="mb-2" href="/admin/manage-users/{{userObj.id}}"> < Return to User Page</a>
|
||||
<div class="d-flex align-items-center">
|
||||
<img height="100px" style="aspect-ratio: 1/1;" class="rounded-2" src="/Thumbs/Avatar.ashx?userId={{userObj.id}}&x=100&y=100">
|
||||
<div>
|
||||
<h1 class="m-0 {% if userObj.accountstatus == 1 %}text-white{% elif userObj.accountstatus == 2 %}text-warning{% elif userObj.accountstatus == 3%}text-danger{% else %}text-decoration-line-through text-white{%endif%}">{{userObj.username}}<span class="text-secondary"> Moderator Notes</span></h1>
|
||||
<p class="text-secondary m-0" style="font-size: 12px;">UserId: <span class="text-white">{{userObj.id}}</span></p>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
{% for note in UserModeratorNotes.items %}
|
||||
<div class="rounded border bg-dark p-2 mb-1">
|
||||
<div class="d-flex align-items-center">
|
||||
<img height="40px" style="aspect-ratio: 1/1;" class="rounded-2 border" src="/Thumbs/Head.ashx?userId={{note.note_creator_id}}&x=50&y=50">
|
||||
<div class="ms-1">
|
||||
<h5 class="m-0 text-white">{{ GetUserName(note.note_creator_id) }}</h5>
|
||||
<p class="m-0" style="font-size: 12px;color: rgb(200,200,200);">Created at {{note.created_at}} UTC</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-1 border w-100 mt-1">
|
||||
{% for note_line in note.note.split("\n") %}
|
||||
<p class="m-0 text-white">{{note_line}}</p>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% if len(UserModeratorNotes.items) == 0 %}
|
||||
<div class="alert alert-danger">
|
||||
This user currently has no moderator notes.
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="align-items-center d-flex justify-content-center mt-2 mb-2">
|
||||
<a class="ms-auto m-0 text-decoration-none {% if not UserModeratorNotes.has_prev %}text-secondary{%endif%}" {% if UserModeratorNotes.has_prev %}href="/admin/manage-users/{{userObj.id}}/moderator-notes?page={{UserModeratorNotes.prev_num}}"{%endif%}>Previous</a>
|
||||
<p class="ms-2 me-2 text-white m-0">Page {{UserModeratorNotes.page}} of {{UserModeratorNotes.pages}}</p>
|
||||
<a class="me-auto m-0 text-decoration-none {% if not UserModeratorNotes.has_next %}text-secondary{%endif%}" {% if UserModeratorNotes.has_next %}href="/admin/manage-users/{{userObj.id}}/moderator-notes?page={{UserModeratorNotes.next_num}}"{%endif%}>Next</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,82 @@
|
||||
{% extends '__layout__.html' %}
|
||||
{% block title %}Admin{% endblock %}
|
||||
{% block head %}
|
||||
<link href="/static/css/admin.css" rel="stylesheet"/> <!-- Its just easier to resuse this-->
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div id="main">
|
||||
<div class="container" style="min-width: 1100px;">
|
||||
<a href="/admin" class="btn border-primary btn-sm mb-2">Back to Admin Panel</a>
|
||||
<h1>User Manager</h1>
|
||||
<form method="post">
|
||||
<input type="hidden" name="csrf_token" value="{{csrf_token()}}">
|
||||
<div class="input-group mb-3">
|
||||
<div class="form-floating" style="width: 52%;">
|
||||
<input type="text" class="form-control" id="user-search-input" name="user-search-input" value="{%if query != None: %}{{query}}{%endif%}" placeholder="ROBLOX">
|
||||
<label for="user-search-input">Search</label>
|
||||
</div>
|
||||
<div class="form-floating" style="min-width: 130px;">
|
||||
<select class="form-select" id="user-search-type" name="user-search-type">
|
||||
<option value="userid">UserID</option>
|
||||
<option value="username" {%if searchType == "username"%}selected{%endif%}>Username</option>
|
||||
<option value="discordid" {%if searchType == "discordid"%}selected{%endif%}>Discord ID</option>
|
||||
</select>
|
||||
<label for="user-search-type">Search With</label>
|
||||
</div>
|
||||
<div class="form-floating" style="min-width: 160px;">
|
||||
<select class="form-select" id="user-order-by" name="user-order-by">
|
||||
<option value="userid">UserID</option>
|
||||
<option value="creation" {%if orderBy == "creation"%}selected{%endif%}>Creation Date</option>
|
||||
<option value="lastonline" {%if orderBy == "lastonline"%}selected{%endif%}>Last Online</option>
|
||||
<option value="robux" {%if orderBy == "robux"%}selected{%endif%}>Robux Balance</option>
|
||||
<option value="tix" {%if orderBy == "tix"%}selected{%endif%}>Tickets Balance</option>
|
||||
</select>
|
||||
<label for="user-order-by">Order By</label>
|
||||
</div>
|
||||
<div class="form-floating" style="min-width: 130px;">
|
||||
<select class="form-select" id="user-order-direction" name="user-order-direction">
|
||||
<option value="asc">Ascending</option>
|
||||
<option value="desc" {%if orderType == "desc"%}selected{%endif%}>Descending</option>
|
||||
</select>
|
||||
<label for="user-order-direction">Order Direction</label>
|
||||
</div>
|
||||
<input type="submit" class="btn btn-outline-primary fw-bold" value="🔍">
|
||||
</div>
|
||||
</form>
|
||||
<table class="table table-dark table-striped">
|
||||
<thead>
|
||||
<tr class="rounded-top">
|
||||
<th scope="col">ID</th>
|
||||
<th scope="col">Username</th>
|
||||
<th scope="col">Created</th>
|
||||
<th scope="col">Last Online</th>
|
||||
<th scope="col">Robux</th>
|
||||
<th scope="col">Tickets</th>
|
||||
<th scope="col">Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="table-group-divider" style="border-color: rgb(20,20,20);">
|
||||
{% for user in returnList: %}
|
||||
<tr>
|
||||
<td>{{user.id}}</td>
|
||||
<td><span class="{% if user.accountstatus == 2%}text-warning{% endif %}{% if user.accountstatus > 2 %}text-danger{%endif%}">{{user.username}}</span></td>
|
||||
<td>{{user.creation}}</td>
|
||||
<td>{{user.lastonline}}</td>
|
||||
<td>{{user.robux}}</td>
|
||||
<td>{{user.tix}}</td>
|
||||
<td><a href="/admin/manage-users/{{user.id}}" class="text-decoration-none btn btn-primary btn-sm w-100">View</a></td>
|
||||
</tr>
|
||||
{%endfor%}
|
||||
</tbody>
|
||||
</table>
|
||||
{% if len(returnList) == 0: %}
|
||||
<p class="text-white w-100 text-center">No results found</p>
|
||||
{%endif%}
|
||||
<div class="mt-3 d-flex">
|
||||
<a class="text-decoration-none ms-auto {%if page <= 1: %}disabled text-secondary{%else%}text-primary{%endif%}" {% if page > 1:%}href="/admin/manage-users?searchType={{searchType}}&orderBy={{orderBy}}&page={{page-1}}&orderType={{orderType}}{%if query != None: %}&query={{query}}{%endif%}"{%endif%}>Previous</a>
|
||||
<p class="ms-2 me-2 text-white">Page {{page}}</p>
|
||||
<a class="text-decoration-none me-auto {%if not isThereNextPage: %}disabled text-secondary{%else%}text-primary{%endif%}" {%if isThereNextPage: %}href="/admin/manage-users?searchType={{searchType}}&orderBy={{orderBy}}&page={{page+1}}&orderType={{orderType}}{%if query != None: %}&query={{query}}{%endif%}"{%endif%}>Next</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,116 @@
|
||||
{% extends '__layout__.html' %}
|
||||
{% block title %}Admin{% endblock %}
|
||||
{% block head %}
|
||||
<link href="/static/css/admin.css" rel="stylesheet"/> <!-- Its just easier to resuse this-->
|
||||
<style>
|
||||
.show-on-hover {
|
||||
filter: blur(4px);
|
||||
transition: 0.2s;
|
||||
}
|
||||
.show-on-hover:hover {
|
||||
filter: blur(0px);
|
||||
transition: 0.2s;
|
||||
}
|
||||
.bg-dark {
|
||||
background-color: rgb(32, 32, 32) !important;
|
||||
box-shadow: 0px 0px 5px 0px rgb(0,0,0);
|
||||
}
|
||||
.text-secondary {
|
||||
color: rgb(199, 199, 199) !important;
|
||||
}
|
||||
.text-tickets {
|
||||
color: rgb(224, 224, 60) !important;
|
||||
font-weight: 600;
|
||||
}
|
||||
.text-robux {
|
||||
color: rgb(26, 212, 103) !important;
|
||||
font-weight: 600;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div id="main">
|
||||
<div class="container" style="max-width: 1000px;">
|
||||
<a class="mb-2" href="/admin/manage-users/{{userObj.id}}"> < Return to User Page</a>
|
||||
<div class="d-flex align-items-center">
|
||||
<img height="100px" style="aspect-ratio: 1/1;" class="rounded-2" src="/Thumbs/Avatar.ashx?userId={{userObj.id}}&x=100&y=100">
|
||||
<div>
|
||||
<h1 class="m-0 {% if userObj.accountstatus == 1 %}text-white{% elif userObj.accountstatus == 2 %}text-warning{% elif userObj.accountstatus == 3%}text-danger{% else %}text-decoration-line-through text-white{%endif%}">{{userObj.username}}<span class="text-secondary"> Transactions</span></h1>
|
||||
<p class="text-secondary m-0" style="font-size: 12px;">UserId: <span class="text-white">{{userObj.id}}</span></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-floating mb-2">
|
||||
<select class="form-control" id="change-category-select">
|
||||
<option value="purchase" {%if PageCategory == "purchase"%}selected{%endif%}>Purchases</option>
|
||||
<option value="sale" {%if PageCategory == "sale"%}selected{%endif%}>Sales</option>
|
||||
<option value="group-payout" {%if PageCategory == "group-payout"%}selected{%endif%}>Group Payouts</option>
|
||||
<option value="stipends" {%if PageCategory == "stipends"%}selected{%endif%}>Stipends</option>
|
||||
</select>
|
||||
<label for="change-category-select">Category</label>
|
||||
</div>
|
||||
<table class="table table-dark table-striped">
|
||||
<thead>
|
||||
<tr class="rounded-top">
|
||||
<th scope="col" style="width: 15%;">Date</th>
|
||||
<th scope="col" style="width: 20%;">Source</th>
|
||||
<th scope="col" style="width: 45%;">Description</th>
|
||||
<th scope="col" style="width: 20%;">Amount</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="table-group-divider" style="border-color: rgb(20,20,20);">
|
||||
{% for Transaction in TransactionInfo: %}
|
||||
<tr>
|
||||
<td>{{Transaction.created_at}}</td>
|
||||
<td>
|
||||
{% if Transaction["source"]["type"] == 0: %}
|
||||
<a href="/users/{{Transaction['source']['id']}}/profile" class="text-decoration-none text-white text-truncate">
|
||||
<img class="rounded-5 overflow-hidden me-2" width="38" height="38" src="/Thumbs/Head.ashx?x=48&y=48&userId={{Transaction['source']['id']}}" alt="{{Transaction['source']['name']}}">
|
||||
{{Transaction["source"]["name"]}}
|
||||
</a>
|
||||
{% else %}
|
||||
<a href="/groups/{{Transaction['source']['id']}}/--" class="text-decoration-none text-white text-truncate">
|
||||
<img class="rounded-5 overflow-hidden me-2" width="38" height="38" src="/Thumbs/GroupIcon.ashx?x=48&y=48&groupid={{Transaction['source']['id']}}" alt="{{Transaction['source']['name']}}">
|
||||
{{Transaction["source"]["name"]}}
|
||||
</a>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if Transaction['custom_text'] != None %}
|
||||
{{Transaction['custom_text']}}
|
||||
{% else %}
|
||||
{% if Transaction['asset'] != None: %}
|
||||
<a href="/catalog/{{Transaction['asset']['id']}}/--" class="text-decoration-none text-white">
|
||||
<img class="rounded-2 overflow-hidden me-2 border" width="38" height="38" src="/Thumbs/Asset.ashx?x=48&y=48&assetId={{Transaction['asset']['id']}}" alt="{{Transaction['asset']['name']}}">
|
||||
{{Transaction['asset']['name']}}
|
||||
</a>
|
||||
{% else %}
|
||||
Unknown
|
||||
{%endif%}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if Transaction['currency_type'] == 0 %}
|
||||
<span class="text-robux">R$ {{Transaction['currency_amount']}}</span>
|
||||
{% else %}
|
||||
<span class="text-tickets">T$ {{Transaction['currency_amount']}}</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% if len(TransactionInfo) == 0: %}
|
||||
<div class="text-center">
|
||||
<p class="text-secondary">No transactions found</p>
|
||||
</div>
|
||||
{%endif%}
|
||||
|
||||
<div class="align-items-center d-flex justify-content-center mt-2 mb-2">
|
||||
<a class="ms-auto m-0 text-decoration-none {% if not Pagination.has_prev %}text-secondary{%endif%}" {% if Pagination.has_prev %}href="/admin/manage-users/{{userObj.id}}/transactions?page={{Pagination.prev_num}}&category={{PageCategory}}"{%endif%}>Previous</a>
|
||||
<p class="ms-2 me-2 text-white m-0">Page {{Pagination.page}} of {{Pagination.pages}}</p>
|
||||
<a class="me-auto m-0 text-decoration-none {% if not Pagination.has_next %}text-secondary{%endif%}" {% if Pagination.has_next %}href="/admin/manage-users/{{userObj.id}}/transactions?page={{Pagination.next_num}}&category={{PageCategory}}"{%endif%}>Next</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>const s = document.getElementById("change-category-select");s.addEventListener("change", function() {var u = new URL(window.location.href);var sb = s.value;u.searchParams.set("category", sb);u.searchParams.set("page", 1);window.location.href = u;});</script>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,193 @@
|
||||
{% extends '__layout__.html' %}
|
||||
{% block title %}Admin{% endblock %}
|
||||
{% block head %}
|
||||
<link href="/static/css/admin.css" rel="stylesheet"/> <!-- Its just easier to resuse this-->
|
||||
<style>
|
||||
.show-on-hover {
|
||||
filter: blur(4px);
|
||||
transition: 0.2s;
|
||||
}
|
||||
.show-on-hover:hover {
|
||||
filter: blur(0px);
|
||||
transition: 0.2s;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div id="main">
|
||||
<div class="container" style="min-width: 1100px;">
|
||||
<div class="d-flex">
|
||||
<a href="/admin/manage-users" class="btn border-success btn-sm mb-2">Back to User Manager</a>
|
||||
<a href="/users/{{userObj.id}}/profile" class="btn border-primary btn-sm mb-2 ms-3">View user profile</a>
|
||||
</div>
|
||||
{% with messages = get_flashed_messages() %}
|
||||
{% if messages %}
|
||||
<div>
|
||||
{% for message in messages %}
|
||||
<div class="alert alert-danger p-2 text-center">
|
||||
{{ message }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
<div class="row">
|
||||
<div class="col-3">
|
||||
<div class="border rounded-2">
|
||||
<div class="bg-dark p-2 rounded-2 rounded-bottom-0 border-bottom">
|
||||
<div class="d-flex align-items-center">
|
||||
<img width="30px" height="30px" class="rounded me-2" src="/Thumbs/Head.ashx?x=48&y=48&userId={{userObj.id}}" style="aspect-ratio: 1/1;">
|
||||
<h5 class="m-0">{{userObj.username}}</h5>
|
||||
</div>
|
||||
</div>
|
||||
<img width="100%" class="p-2" src="/Thumbs/Avatar.ashx?x=420&y=420&userId={{userObj.id}}" style="aspect-ratio: 1/1;">
|
||||
{% if isAdministrator: %}
|
||||
<div class="border-top" style="background-color: rgba(24, 220, 255,0.5);font-size:12px;padding:3px;">
|
||||
<p class="m-0 fw-bold text-white text-center">Administrator</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if userObj.accountstatus == 2: %}
|
||||
<div class="border-top" style="background-color: rgba(255, 201, 24, 0.5);font-size:12px;padding:3px;">
|
||||
<p class="m-0 fw-bold text-white text-center">This account is temporarily banned</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if userObj.accountstatus == 3: %}
|
||||
<div class="border-top" style="background-color: rgba(255, 24, 24, 0.5);font-size:12px;padding:3px;">
|
||||
<p class="m-0 fw-bold text-white text-center">This account has been deleted</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if userObj.accountstatus == 4: %}
|
||||
<div class="border-top" style="background-color: rgba(255, 24, 243, 0.5);font-size:12px;padding:3px;">
|
||||
<p class="m-0 fw-bold text-white text-center">This account is GDPR deleted</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="bg-dark p-2 rounded-2 rounded-top-0 border-top text-secondary" style="font-size: 13px;">
|
||||
<span>Created: <span class="text-white">{{userObj.created}}</span></span>
|
||||
<br>
|
||||
<span>Last Online: <span class="text-white">{{userObj.lastonline}}</span></span>
|
||||
<br>
|
||||
<span>2FA Enabled: <span class="text-white">{{userObj.TOTPEnabled}}</span></span>
|
||||
<br>
|
||||
<span>Place Visits: <span class="text-white">{{TotalVisits}}</span></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="border rounded-2 mt-2">
|
||||
<div class="bg-dark p-2 rounded-2 rounded-bottom-0 border-bottom">
|
||||
<h5 class="m-0">Economy</h5>
|
||||
</div>
|
||||
<div class="text-secondary p-2 pt-1" style="font-size: 13px;">
|
||||
<span>Robux: <span class="text-success fw-bold">R${{UserEconomyObj.robux}}</span></span>
|
||||
<br>
|
||||
<span>Tickets: <span class="text-warning fw-bold">T${{UserEconomyObj.tix}}</span></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-7">
|
||||
{% if LastestUserBanObj != None: %}
|
||||
<div class="border rounded-2 mb-2">
|
||||
<div class="bg-dark p-2 rounded-2 rounded-bottom-0 border-bottom">
|
||||
<h5 class="m-0">Ban Information</h5>
|
||||
</div>
|
||||
<div class="p-2 text-secondary text-truncate" style="font-size: 14px;">
|
||||
<span>Ban Type: <span class="text-white">{{LastestUserBanObj.ban_type.name}}</span></span>
|
||||
<br>
|
||||
<span>Banned On: <span class="text-white">{{LastestUserBanObj.created_at}}</span></span>
|
||||
<br>
|
||||
<span>Expires On: <span class="text-white">{{LastestUserBanObj.expires_at}}</span></span>
|
||||
<br>
|
||||
<span>Reason: <span class="text-white">{{LastestUserBanObj.reason}}</span></span>
|
||||
<br>
|
||||
<span>Moderator Note: <span class="text-white" style="word-break: break-all;white-space: normal;">{{LastestUserBanObj.moderator_note}}</span></span>
|
||||
<br>
|
||||
<span>Banned By: <a class="text-decoration-underline text-primary" href="/admin/manage-users/{{LastestUserBanObj.author_userid}}">{{LastestUserBanObj.author_userid}}</a></span>
|
||||
</div>
|
||||
</div>
|
||||
{%endif%}
|
||||
<div class="border rounded-2" style="min-height: 150px;">
|
||||
<div class="bg-dark p-2 rounded-2 rounded-bottom-0 border-bottom">
|
||||
<h5 class="m-0">Description</h5>
|
||||
</div>
|
||||
<div class="p-2">
|
||||
{% for line in DescriptionLines %}
|
||||
<p class="text-white m-0">{{line}}</p>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% if LastLogin != None: %}
|
||||
<div class="border rounded-2 mt-2">
|
||||
<div class="bg-dark p-2 rounded-2 rounded-bottom-0 border-bottom">
|
||||
<h5 class="m-0">Latest Login</h5>
|
||||
</div>
|
||||
<div class="p-2 text-secondary text-truncate" style="font-size: 14px;">
|
||||
<span>User-Agent: <span class="text-white">{{LastLogin.useragent}}</span></span>
|
||||
<br>
|
||||
<span>Login Timestamp: <span class="text-white">{{LastLogin.timestamp}}</span></span>
|
||||
</div>
|
||||
</div>
|
||||
{%endif%}
|
||||
{% if LinkedDiscordObj != None: %}
|
||||
<div class="border rounded-2 mt-2">
|
||||
<div class="bg-dark p-2 rounded-2 rounded-bottom-0 border-bottom">
|
||||
<h5 class="m-0">Discord Account</h5>
|
||||
</div>
|
||||
<div class="p-2 text-secondary text-truncate" style="font-size: 14px;">
|
||||
<div class="row">
|
||||
<div class="col-10">
|
||||
<span>Discord ID: <span class="text-white">{{DiscordUserInfo.UserId}}</span></span>
|
||||
<br>
|
||||
<span>Username: <span class="text-white">{{DiscordUserInfo.Username}}</span></span>
|
||||
<br>
|
||||
<span>Discriminator: <span class="text-white">{{DiscordUserInfo.Discriminator}}</span></span>
|
||||
<br>
|
||||
<span>Linked On: <span class="text-white">{{LinkedDiscordObj.linked_on}}</span></span>
|
||||
<br>
|
||||
<span>Last Refresh: <span class="text-white">{{LinkedDiscordObj.last_updated}}</span></span>
|
||||
</div>
|
||||
<div class="col-2">
|
||||
<img width="100%" style="aspect-ratio: 1/1;" class="border rounded" src="{{DiscordUserInfo.GetAvatarURL()}}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{%endif%}
|
||||
{% if InviteKeyUsed != None: %}
|
||||
<div class="border rounded-2 mt-2">
|
||||
<div class="bg-dark p-2 rounded-2 rounded-bottom-0 border-bottom">
|
||||
<h5 class="m-0">Invite Key Info</h5>
|
||||
</div>
|
||||
<div class="p-2 text-secondary text-truncate" style="font-size: 14px;">
|
||||
<span>Invite Key: <span class="text-white show-on-hover">{{InviteKeyUsed.key}}</span></span>
|
||||
<br>
|
||||
<span>Creator: {% if InviteKeyUsed.created_by != None: %}<a class="text-decoration-none text-primary" href="/admin/manage-users/{{InviteKeyUsed.created_by}}">{{InviteKeyUsed.creator.username}}</a>{%else%}<span class="text-white">Unknown</span>{%endif%}</span>
|
||||
<br>
|
||||
<span>Created At: <span class="text-white">{{InviteKeyUsed.created_at}}</span></span>
|
||||
</div>
|
||||
</div>
|
||||
{%endif%}
|
||||
</div>
|
||||
<div class="col-2">
|
||||
<div class="border rounded-2">
|
||||
<div class="bg-dark p-2 rounded-2 rounded-bottom-0">
|
||||
<h5 class="m-0">Actions</h5>
|
||||
</div>
|
||||
<a class="btn border-top rounded-0 w-100 text-decoration-none" href="/admin/manage-users/{{userObj.id}}/ban-user"><i class="bi bi-hammer"></i> Ban User</a>
|
||||
{% if HasAdminPermission("ManageAdminPermissions") %}<a class="btn border-top rounded-0 w-100 text-decoration-none" href="/admin/manage-users/{{userObj.id}}/manage-admin-permissions"><i class="bi bi-shield"></i> Manage Permissions</a>{%endif%}
|
||||
<a class="btn border-top rounded-0 w-100 text-decoration-none" href="/admin/manage-users/{{userObj.id}}/manage-currency"><i class="bi bi-cash-stack"></i> Manage Robux & BC</a>
|
||||
|
||||
</div>
|
||||
<div class="border rounded-2 mt-2">
|
||||
<div class="bg-dark p-2 rounded-2 rounded-bottom-0">
|
||||
<h5 class="m-0">View</h5>
|
||||
</div>
|
||||
<a class="btn border-top rounded-0 w-100 text-decoration-none" href="/admin/manage-users/{{userObj.id}}/moderator-notes"><i class="bi bi-journal-text"></i></i> Moderator Notes</a>
|
||||
<a class="btn border-top rounded-0 w-100 text-decoration-none" href="/admin/manage-users/{{userObj.id}}/login-history"><i class="bi bi-clock-history"></i></i> Login History</a>
|
||||
<a class="btn border-top rounded-0 w-100 text-decoration-none" href="/admin/manage-users/{{userObj.id}}/invite-keys"><i class="bi bi-key"></i></i> Invite Keys</a>
|
||||
<a class="btn border-top rounded-0 w-100 text-decoration-none" href="/admin/manage-users/{{userObj.id}}/game-sessions"><i class="bi bi-controller"></i></i> Game Sessions</a>
|
||||
<a class="btn border-top rounded-0 w-100 text-decoration-none" href="/admin/manage-users/{{userObj.id}}/transactions"><i class="bi bi-bank"></i></i> Transactions</a>
|
||||
<a class="btn border-top rounded-0 w-100 text-decoration-none" href="/admin/manage-users/{{userObj.id}}/ban-history"><i class="bi bi-exclamation-triangle"></i></i> Ban History</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user