forked from CRSS/Website
feat: start making an admin panel
This commit is contained in:
parent
c8b4814aef
commit
063292dd9d
13
auth.php
13
auth.php
|
@ -21,17 +21,22 @@
|
|||
$guildIds[] = $guild['id'];
|
||||
}
|
||||
|
||||
if(!in_array('1127731341283307520', $guildIds)) {
|
||||
if(!in_array('1127731341283307520', $guildIds) || !in_array('1195393418151596032', $guildIds)) {
|
||||
echo json_encode(array(
|
||||
'error' => true,
|
||||
'error_description' => 'You are not in the CRSS guild.'
|
||||
'error_description' => 'You are not in any of CRSS\'s guilds.'
|
||||
));
|
||||
} else {
|
||||
$_SESSION['user'] = $discord->getUser($res['access_token']);
|
||||
|
||||
$mysql->createUserRecord($_SESSION['user']);
|
||||
|
||||
header('Location: /');
|
||||
|
||||
|
||||
if (isset($_GET['state'])) {
|
||||
header('Location: ' . $_GET['state']);
|
||||
} else {
|
||||
header('Location: /');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
echo json_encode($res);
|
||||
|
|
51
index.php
51
index.php
|
@ -1,5 +1,5 @@
|
|||
<?php
|
||||
global $twig;
|
||||
global $twig, $mysql;
|
||||
|
||||
require_once '_config.php';
|
||||
|
||||
|
@ -32,8 +32,15 @@
|
|||
)
|
||||
);
|
||||
|
||||
if(isset($_SESSION['user']))
|
||||
$twig->addGlobal('user', $_SESSION['user']);
|
||||
if(isset($_SESSION['user'])) {
|
||||
$dbUser = $mysql->getUserRecordFromId($_SESSION['user']['id']);
|
||||
|
||||
$user = $_SESSION['user'];
|
||||
|
||||
$user['is_admin'] = $dbUser['is_admin'];
|
||||
|
||||
$twig->addGlobal('user', $user);
|
||||
}
|
||||
|
||||
$res = $curl->get('https://crss.blurryface.xyz/api/v1/players');
|
||||
|
||||
|
@ -45,6 +52,7 @@
|
|||
$twig->addGlobal('playerCount', $json);
|
||||
|
||||
$twig->addGlobal('nations', $nations);
|
||||
$twig->addGlobal('dc_uri', 'https://discord.com/api/oauth2/authorize?client_id=1144248396467683338&redirect_uri=' . urlencode($_ENV['DISCORD_REDIRECT']) . '&response_type=code&scope=identify%20guilds&state=' . urlencode($_SERVER['REQUEST_URI']));
|
||||
|
||||
$twig->addGlobal('reduced', isset($_GET['reduced']));
|
||||
|
||||
|
@ -152,16 +160,45 @@
|
|||
$markers = $mysql->getMarkers();
|
||||
|
||||
if ($user == null && $user['admin'] == 0) {
|
||||
http_response_code(404);
|
||||
http_response_code(401);
|
||||
|
||||
echo $twig->render('404.twig');
|
||||
echo '<style>body { overflow: hidden; height: 100svh; background: black; display: flex; justify-content: center; align-items: center; }</style><img src="https://http.cat/401" alt="401 Unauthorized" />';
|
||||
} else {
|
||||
echo $twig->render('admin/index.twig', array('users' => $users, 'markers' => $markers));
|
||||
}
|
||||
} else {
|
||||
http_response_code(404);
|
||||
http_response_code(401);
|
||||
|
||||
echo $twig->render('404.twig');
|
||||
echo '<style>body { overflow: hidden; height: 100svh; background: black; display: flex; justify-content: center; align-items: center; }</style><img src="https://http.cat/401" alt="401 Unauthorized" />';
|
||||
}
|
||||
});
|
||||
|
||||
$router->get('/admin/__data/page/([a-z]+)', function($page) {
|
||||
global $twig, $mysql;
|
||||
|
||||
if (isset($_SESSION['user'])) {
|
||||
$user = $mysql->getUserRecordFromId($_SESSION['user']['id']);
|
||||
|
||||
$users = $mysql->getUsers();
|
||||
$markers = $mysql->getMarkers();
|
||||
|
||||
if ($user == null && $user['admin'] == 0) {
|
||||
http_response_code(401);
|
||||
|
||||
echo '<style>body { overflow: hidden; height: 100svh; background: black; display: flex; justify-content: center; align-items: center; }</style><img src="https://http.cat/401" alt="401 Unauthorized" />';
|
||||
} else {
|
||||
try {
|
||||
echo $twig->render('admin/pages/' . urlencode($page) . '.twig', array('users' => $users, 'markers' => $markers));
|
||||
} catch (Exception $e) {
|
||||
http_response_code(404);
|
||||
|
||||
echo $twig->render('admin/pages/404.twig');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
http_response_code(401);
|
||||
|
||||
echo '<style>body { overflow: hidden; height: 100svh; background: black; display: flex; justify-content: center; align-items: center; }</style><img src="https://http.cat/401" alt="401 Unauthorized" />';
|
||||
}
|
||||
});
|
||||
|
||||
|
|
50
js/admin/nav.js
Normal file
50
js/admin/nav.js
Normal file
|
@ -0,0 +1,50 @@
|
|||
const pageContainer = document.querySelector('.pageContainer');
|
||||
|
||||
window.history.pushState({}, '', '#/');
|
||||
|
||||
window.addEventListener('hashchange', () => {
|
||||
let uri = window.location.href.split('#')[1];
|
||||
|
||||
if (!window.location.href.includes('#'))
|
||||
uri = '/admin#/'
|
||||
|
||||
const allActiveLinks = document.querySelectorAll('.nav a.active');
|
||||
const allLinksWithThisUrl = document.querySelectorAll(`.nav a[href="#${uri}"]`);
|
||||
|
||||
allActiveLinks.forEach(activeLink => {
|
||||
activeLink.classList.remove('active');
|
||||
activeLink.classList.add('link-body-emphasis');
|
||||
});
|
||||
|
||||
allLinksWithThisUrl.forEach(link => {
|
||||
link.classList.add('active');
|
||||
link.classList.remove('link-body-emphasis');
|
||||
});
|
||||
|
||||
changePage(window.location.href.split('#')[1].replace('/', ''));
|
||||
});
|
||||
|
||||
const changePage = (url) => {
|
||||
if (!url)
|
||||
url = 'dashboard';
|
||||
|
||||
pageContainer.innerHTML = `<i class="loader" data-lucide="loader-circle"></i>`;
|
||||
|
||||
pageContainer.classList.add('d-flex');
|
||||
pageContainer.classList.add('align-items-center');
|
||||
pageContainer.classList.add('justify-content-center');
|
||||
|
||||
lucide.createIcons();
|
||||
|
||||
fetch(`/admin/__data/page/${url}`)
|
||||
.then(res => res.text())
|
||||
.then(html => {
|
||||
pageContainer.innerHTML = html;
|
||||
|
||||
pageContainer.classList.remove('d-flex');
|
||||
pageContainer.classList.remove('align-items-center');
|
||||
pageContainer.classList.remove('justify-content-center');
|
||||
|
||||
lucide.createIcons();
|
||||
});
|
||||
};
|
12
template/admin/includes/foot.twig
Normal file
12
template/admin/includes/foot.twig
Normal file
|
@ -0,0 +1,12 @@
|
|||
<script src="/js/admin/nav.js"></script>
|
||||
|
||||
<script src="https://unpkg.com/lucide@latest"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||
|
||||
<script>
|
||||
lucide.createIcons();
|
||||
|
||||
changePage();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
35
template/admin/includes/head.twig
Normal file
35
template/admin/includes/head.twig
Normal file
|
@ -0,0 +1,35 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
||||
|
||||
<title>{{ pageTitle }} - Admin • Clyde's Real Survival SMP</title>
|
||||
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" />
|
||||
|
||||
<style>
|
||||
body {
|
||||
min-height: 100svh;
|
||||
}
|
||||
|
||||
.lucide {
|
||||
vertical-align: -10%;
|
||||
}
|
||||
|
||||
/* Loading Animation */
|
||||
|
||||
.loader {
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
100% {
|
||||
-webkit-transform: rotate(360deg);
|
||||
transform:rotate(360deg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="d-flex flex-column">
|
52
template/admin/includes/sidebar.twig
Normal file
52
template/admin/includes/sidebar.twig
Normal file
|
@ -0,0 +1,52 @@
|
|||
<div class="d-flex flex-column flex-shrink-0 p-3 bg-body-tertiary" style="width: 280px;">
|
||||
<a href="/" class="d-flex align-items-center mb-3 mb-md-0 me-md-auto link-body-emphasis text-decoration-none">
|
||||
<i class="pe-none me-2" data-lucide="gauge" width="40" height="32"></i>
|
||||
<span class="fs-4">Admin Panel</span>
|
||||
</a>
|
||||
<hr>
|
||||
<ul class="nav nav-pills flex-column mb-auto">
|
||||
<li class="nav-item">
|
||||
<a href="#/" class="nav-link active" aria-current="page">
|
||||
<i class="pe-none me-2" data-lucide="home" width="16" height="16"></i>
|
||||
|
||||
Home
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="#/pages" class="nav-link link-body-emphasis">
|
||||
<i class="pe-none me-2" data-lucide="notebook-text" width="16" height="16"></i>
|
||||
|
||||
Pages
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="#/users" class="nav-link link-body-emphasis">
|
||||
<i class="pe-none me-2" data-lucide="users" width="16" height="16"></i>
|
||||
|
||||
Users
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="#/markers" class="nav-link link-body-emphasis">
|
||||
<i class="pe-none me-2" data-lucide="map-pin" width="16" height="16"></i>
|
||||
|
||||
Markers
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<div class="dropdown">
|
||||
<a href="#" class="d-flex align-items-center link-body-emphasis text-decoration-none dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<img src="https://cdn.discordapp.com/avatars/{{ user.id }}/{{ user.avatar }}.png" alt="" width="32" height="32" class="rounded-circle me-2">
|
||||
|
||||
<span>{{ user.global_name }}</span>
|
||||
</a>
|
||||
<ul class="dropdown-menu text-small shadow">
|
||||
<li><a class="dropdown-item" href="#">New project...</a></li>
|
||||
<li><a class="dropdown-item" href="#">Settings</a></li>
|
||||
<li><a class="dropdown-item" href="#">Profile</a></li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li><a class="dropdown-item" href="/">Exit Admin</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
|
@ -1,150 +1,11 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
{% include 'admin/includes/head.twig' with {'pageTitle': 'Dashboard'} %}
|
||||
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0" />
|
||||
<div class="d-flex flex-grow-1">
|
||||
{% include 'admin/includes/sidebar.twig' %}
|
||||
|
||||
<title>Admin Panel</title>
|
||||
<div class="flex-grow-1 pageContainer">
|
||||
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h3>Users</h3>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">id</th>
|
||||
<th scope="col">username</th>
|
||||
<th scope="col">display_name</th>
|
||||
<th scope="col">is_admin</th>
|
||||
<th scope="col">date</th>
|
||||
<th scope="col">actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for user in users %}
|
||||
<tr>
|
||||
<th scope="row">{{ user.id }}</th>
|
||||
<td>{{ user.username }}</td>
|
||||
<td>{{ user.display_name }}</td>
|
||||
<td>{{ user.is_admin == 1 ? "true" : "false" }}</td>
|
||||
<td>{{ user.date }}</td>
|
||||
<td>
|
||||
{% if user.is_admin == 1 %}
|
||||
<button type="button" class="btn btn-success" data-bs-toggle="modal" data-bs-target="#confirmModal">Revoke Admin</button>
|
||||
{% else %}
|
||||
<button type="button" class="btn btn-danger" data-bs-toggle="modal" data-bs-target="#confirmModal">Make Admin</button>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="staticBackdrop" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="staticBackdropLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h1 class="modal-title fs-5" id="staticBackdropLabel">Are you sure?</h1>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Accusamus aspernatur fugit id impedit iusto perferendis possimus provident quos sunt vero. Assumenda blanditiis culpa eaque error ipsa non quasi sint unde?
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-success" data-bs-dismiss="modal">Yes</button>
|
||||
<button type="button" class="btn btn-danger">No</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<h3>Markers</h3>
|
||||
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">id</th>
|
||||
<th scope="col">name</th>
|
||||
<th scope="col">category</th>
|
||||
<th scope="col">data</th>
|
||||
<th scope="col">actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for marker in markers %}
|
||||
<tr>
|
||||
<th scope="row">{{ marker.id }}</th>
|
||||
<td>{{ marker.name }}</td>
|
||||
<td>{{ marker.category }}</td>
|
||||
<td>{{ marker.data }}</td>
|
||||
<td>
|
||||
<button type="button" class="btn btn-success" data-bs-toggle="modal" data-bs-target="#editModal">Edit</button>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="confirmModal" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="staticBackdropLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h1 class="modal-title fs-5" id="staticBackdropLabel">Are you sure?</h1>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Accusamus aspernatur fugit id impedit iusto perferendis possimus provident quos sunt vero. Assumenda blanditiis culpa eaque error ipsa non quasi sint unde?
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-success" data-bs-dismiss="modal">Yes</button>
|
||||
<button type="button" class="btn btn-danger">No</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="editModal" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="staticBackdropLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h1 class="modal-title fs-5" id="staticBackdropLabel">Editing "${name}"</h1>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form class="frm">
|
||||
<input value="update_marker" name="action" class="visually-hidden">
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="exampleInputEmail1" class="form-label">Email address</label>
|
||||
<input type="email" class="form-control" id="exampleInputEmail1" aria-describedby="emailHelp">
|
||||
<div id="emailHelp" class="form-text">We'll never share your email with anyone else.</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="exampleInputPassword1" class="form-label">Password</label>
|
||||
<input type="password" class="form-control" id="exampleInputPassword1">
|
||||
</div>
|
||||
<div class="mb-3 form-check">
|
||||
<input type="checkbox" class="form-check-input" id="exampleCheck1">
|
||||
<label class="form-check-label" for="exampleCheck1">Check me out</label>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Submit</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-success" data-bs-dismiss="modal" onclick="document.querySelector('.frm').submit()">Save</button>
|
||||
<button type="button" class="btn btn-danger" data-bs-dismiss="modal">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
{% include 'admin/includes/foot.twig' %}
|
3
template/admin/pages/404.twig
Normal file
3
template/admin/pages/404.twig
Normal file
|
@ -0,0 +1,3 @@
|
|||
<div class="d-flex align-items-center justify-content-center h-100">
|
||||
<h1>Not Found :(</h1>
|
||||
</div>
|
3
template/admin/pages/dashboard.twig
Normal file
3
template/admin/pages/dashboard.twig
Normal file
|
@ -0,0 +1,3 @@
|
|||
<div class="d-flex align-items-center justify-content-center h-100">
|
||||
<h1>Welcome {{ user.global_name }} to the admin panel!</h1>
|
||||
</div>
|
24
template/admin/pages/markers.twig
Normal file
24
template/admin/pages/markers.twig
Normal file
|
@ -0,0 +1,24 @@
|
|||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">id</th>
|
||||
<th scope="col">name</th>
|
||||
<th scope="col">category</th>
|
||||
<th scope="col">data</th>
|
||||
<th scope="col">actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for marker in markers %}
|
||||
<tr>
|
||||
<th scope="row">{{ marker.id }}</th>
|
||||
<td>{{ marker.name }}</td>
|
||||
<td>{{ marker.category }}</td>
|
||||
<td>{{ marker.data }}</td>
|
||||
<td>
|
||||
<button type="button" class="btn btn-success" data-bs-toggle="modal" data-bs-target="#editModal">Edit</button>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
1
template/admin/pages/pages.twig
Normal file
1
template/admin/pages/pages.twig
Normal file
|
@ -0,0 +1 @@
|
|||
pages
|
30
template/admin/pages/users.twig
Normal file
30
template/admin/pages/users.twig
Normal file
|
@ -0,0 +1,30 @@
|
|||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">id</th>
|
||||
<th scope="col">username</th>
|
||||
<th scope="col">display_name</th>
|
||||
<th scope="col">is_admin</th>
|
||||
<th scope="col">date</th>
|
||||
<th scope="col">actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for user in users %}
|
||||
<tr>
|
||||
<th scope="row">{{ user.id }}</th>
|
||||
<td>{{ user.username }}</td>
|
||||
<td>{{ user.display_name }}</td>
|
||||
<td>{{ user.is_admin == 1 ? "true" : "false" }}</td>
|
||||
<td>{{ user.date }}</td>
|
||||
<td>
|
||||
{% if user.is_admin == 1 %}
|
||||
<button type="button" class="btn btn-success" data-bs-toggle="modal" data-bs-target="#confirmModal">Revoke Admin</button>
|
||||
{% else %}
|
||||
<button type="button" class="btn btn-danger" data-bs-toggle="modal" data-bs-target="#confirmModal">Make Admin</button>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
|
@ -16,14 +16,19 @@
|
|||
<a class="{% if page == 'map.js' %} active {% endif %}" href="/map">
|
||||
Map & Rails
|
||||
</a>
|
||||
{% if user.is_admin == 1 %}
|
||||
<a href="/admin">
|
||||
Admin Panel
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="navRight">
|
||||
{% if user %}
|
||||
<a class="userButton {% if page == 'profile' %}active{% endif %}" href="{% if page == 'profile' %}#{% else %}/profile{% endif %}">
|
||||
<a class="transitionEnabled userButton {% if page == 'profile' %}active{% endif %}" href="{% if page == 'profile' %}#{% else %}/profile{% endif %}">
|
||||
{{ user.global_name }}
|
||||
</a>
|
||||
{% else %}
|
||||
<a class="buttonPrimary" href="{{ discord_auth }}">
|
||||
<a class="buttonPrimary" href="{{ dc_uri }}">
|
||||
Login
|
||||
</a>
|
||||
{% endif %}
|
||||
|
|
Loading…
Reference in a new issue