aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--README.md9
-rw-r--r--admin/callback/index.php60
-rw-r--r--admin/contact/index.php118
-rw-r--r--admin/index.php45
-rw-r--r--admin/login/index.php4
-rw-r--r--admin/pluralkit/index.php50
-rw-r--r--admin/projects/index.php155
-rw-r--r--admin/users/index.php75
-rw-r--r--api/contact/count/index.php5
-rw-r--r--api/contact/index.php15
-rw-r--r--api/contact/list/index.php12
-rw-r--r--data/admins.json4
-rw-r--r--data/contact.json12
-rw-r--r--data/pluralkit.json2
-rw-r--r--data/projects.json35
-rw-r--r--includes/admin/footer.php2
-rw-r--r--includes/admin/header.php14
-rw-r--r--includes/admin/navigation.php27
-rw-r--r--includes/admin/session.php15
-rw-r--r--includes/gui/navigation.php28
21 files changed, 655 insertions, 34 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..9c32fb1
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+data/github.json
+data/tokens \ No newline at end of file
diff --git a/README.md b/README.md
index aa224fb..540d6ea 100644
--- a/README.md
+++ b/README.md
@@ -9,15 +9,10 @@ As per Cloudburst's requirements, this project features a REST-ful API that allo
### Endpoints
The following endpoints are available on the API:
-* Publicly accessible endpoints:
* `pluralkit` (PluralKit data, read-only)
* `projects` (projects list, read-only)
-* Endpoints that require admin authentication:
- * `users` (add/remove administrators)
- * `project-manager` (projects list, read-write)
- * `pluralkit-config` (PluralKit data, read-write)
+ * `contact` (contact info)
(visit `/api` on the live website for all available endpoints)
-### Authentication
-Authentication is made using the `pcdAdminToken` cookie, which is set when OAuth2 with GitHub is completed and the website confirmed the user is an allowed administrator. \ No newline at end of file
+Admin panel data is not accessible from the API for security reasons (the real reason is laziness). If you really need access to the data, you may parse it manually from the HTML code. \ No newline at end of file
diff --git a/admin/callback/index.php b/admin/callback/index.php
new file mode 100644
index 0000000..7aaed70
--- /dev/null
+++ b/admin/callback/index.php
@@ -0,0 +1,60 @@
+<?php
+
+if (!isset($_GET['code'])) {
+ throw new ErrorException("GitHub OAuth Flow interrupted", 214, E_ERROR);
+}
+
+$data = array(
+ 'client_id' => json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/data/github.json"), true)["id"],
+ 'client_secret' => json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/data/github.json"), true)["secret"],
+ 'code' => $_GET['code']
+);
+
+$post_data = json_encode($data);
+
+$crl = curl_init('https://github.com/login/oauth/access_token');
+curl_setopt($crl, CURLOPT_RETURNTRANSFER, true);
+curl_setopt($crl, CURLINFO_HEADER_OUT, true);
+curl_setopt($crl, CURLOPT_POST, true);
+curl_setopt($crl, CURLOPT_POSTFIELDS, $post_data);
+
+curl_setopt($crl, CURLOPT_HTTPHEADER, array(
+ 'Content-Type: application/json',
+ "Accept: application/json"
+));
+
+$result = curl_exec($crl);
+
+if ($result === false) {
+ throw new ErrorException("GitHub OAuth Flow interrupted", 214, E_ERROR);
+}
+
+curl_close($crl);
+
+$data = json_decode($result, true);
+$crl = curl_init('https://api.github.com/user');
+curl_setopt($crl, CURLOPT_RETURNTRANSFER, true);
+curl_setopt($crl, CURLINFO_HEADER_OUT, true);
+curl_setopt($crl, CURLOPT_POST, false);
+
+curl_setopt($crl, CURLOPT_HTTPHEADER, array(
+ 'Content-Type: application/json',
+ "Accept: application/json",
+ "Authorization: token " . $data["access_token"],
+ "User-Agent: ProjectCloudsdale-Admin/0.0.0 (contact@minteck.org)"
+));
+
+$result = curl_exec($crl);
+$ndata = json_decode($result, true);
+
+if (!in_array($ndata["login"], json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/data/admins.json"), true))) {
+ header("Location: /");
+ die();
+}
+
+if (!file_exists($_SERVER['DOCUMENT_ROOT'] . "/data/tokens")) mkdir($_SERVER['DOCUMENT_ROOT'] . "/data/tokens");
+file_put_contents($_SERVER['DOCUMENT_ROOT'] . "/data/tokens/" . $data["access_token"], $ndata["login"]);
+setcookie("pcdAdminToken", $data["access_token"], 0, "/");
+
+header("Location: /admin");
+die();
diff --git a/admin/contact/index.php b/admin/contact/index.php
new file mode 100644
index 0000000..3310762
--- /dev/null
+++ b/admin/contact/index.php
@@ -0,0 +1,118 @@
+<?php require_once $_SERVER["DOCUMENT_ROOT"] . "/includes/admin/session.php"; global $_USER; ?>
+<?php
+
+$projects = json_decode(file_get_contents($_SERVER["DOCUMENT_ROOT"] . "/data/contact.json"), true);
+
+if (isset($_GET['submit'])) {
+ if (isset($_GET["add-project"]) && isset($_GET["add-project-src"])) {
+ $projects[] = [
+ "name" => $_GET["add-project"],
+ "link" => $_GET["add-project-src"],
+ "icon" => "about:blank"
+ ];
+ file_put_contents($_SERVER["DOCUMENT_ROOT"] . "/data/contact.json", json_encode($projects, JSON_PRETTY_PRINT));
+ header("Location: /admin/contact");
+ die();
+ }
+
+ if (isset($_GET["delete-project"])) {
+ if (isset($projects[(int)$_GET["delete-project"]])) {
+ unset($projects[(int)$_GET["delete-project"]]);
+ file_put_contents($_SERVER["DOCUMENT_ROOT"] . "/data/contact.json", json_encode($projects, JSON_PRETTY_PRINT));
+ header("Location: /admin/contact");
+ die();
+ }
+ }
+
+ if (isset($_GET["edit-project"]) && isset($_GET["edit-project-name"]) && isset($_GET["edit-project-source"]) && isset($_GET["edit-project-icon"])) {
+ if (isset($projects[(int)$_GET["edit-project"]])) {
+ $projects[(int)$_GET["edit-project"]]["name"] = $_GET["edit-project-name"];
+ $projects[(int)$_GET["edit-project"]]["link"] = $_GET["edit-project-source"];
+ $projects[(int)$_GET["edit-project"]]["icon"] = $_GET["edit-project-icon"];
+ file_put_contents($_SERVER["DOCUMENT_ROOT"] . "/data/contact.json", json_encode($projects, JSON_PRETTY_PRINT));
+ header("Location: /admin/contact");
+ die();
+ }
+ }
+}
+
+?>
+<?php require_once $_SERVER["DOCUMENT_ROOT"] . "/includes/admin/header.php"; ?>
+
+<br>
+<div class="container">
+ <?php if (isset($_GET['change']) && isset($_GET['edit-project']) && isset($projects[(int)$_GET["edit-project"]])): $project = $projects[(int)$_GET["edit-project"]]; ?>
+
+ <h1>Edit <b><?= $project["name"] ?></b> (<code><?= (int)$_GET["edit-project"] ?></code>)</h1>
+
+ <form>
+ <p>
+ Social Network Name:<br>
+ <input name="edit-project-name" class="form-control" type="text" value="<?= $project["name"] ?>">
+ </p>
+ <p>
+ Link:<br>
+ <input name="edit-project-source" class="form-control" type="text" value="<?= $project["link"] ?>">
+ </p>
+ <p>
+ Icon URL (can be relative):<br>
+ <input name="edit-project-icon" class="form-control" type="text" value="<?= $project["icon"] ?>">
+ </p>
+ <input name="submit" type="hidden">
+ <input name="edit-project" type="hidden" value="<?= (int)$_GET["edit-project"] ?>">
+ <button type="submit" class="btn btn-primary">Save and apply changes</button>
+ </form>
+
+ <?php else: ?>
+ <h1>Contact Info Management</h1>
+ <p>Contact info added to this list is publicly shown on the website's Contact page and will lead users to containg you.</p>
+
+ <ul class="list-group">
+ <?php foreach (json_decode(file_get_contents($_SERVER["DOCUMENT_ROOT"] . "/data/contact.json"), true) as $index => $project): ?>
+ <li class="list-group-item">
+ <span style="vertical-align: middle;padding-top:10px;">
+ <img src="<?= $project["icon"] ?>" class="project-icon"> <?= $project["name"] ?>
+ </span>
+ <form style="display:inline;float:right;">
+ <input name="delete-project" type="hidden" value="<?= $index ?>">
+ <input name="submit" type="hidden">
+ <button type="submit" class="btn btn-danger">Remove</button>
+ </form>
+ <form style="display:inline;float:right;margin-right:10px;">
+ <input name="edit-project" type="hidden" value="<?= $index ?>">
+ <input name="change" type="hidden">
+ <button type="submit" class="btn btn-primary">Edit</button>
+ </form>
+ </li>
+ <?php endforeach; ?>
+ </ul>
+ <br>
+
+ <button type="button" id="admin-add-s0" class="btn btn-outline-primary" onclick="document.getElementById('admin-add-s0').style.display='none';document.getElementById('admin-add-s1').style.display='';document.getElementById('admin-add-s2').focus();">Add another contact method</button>
+ <div class="card" style="max-width:550px;display:none;" id="admin-add-s1">
+ <form class="card-body">
+ <h4 class="card-title">Add contact method</h4>
+ <p>Once added, this contact method will be shown on the Contact page.</p>
+ <p>
+ <input id="admin-add-s2" name="add-project" type="text" class="form-control" placeholder="Social network name">
+ <input id="admin-add-s2a" name="add-project-src" type="text" class="form-control" placeholder="Link">
+ </p>
+ <p>You are able to add additional details after adding the contact method.</p>
+ <input name="submit" type="hidden">
+ <button type="submit" class="btn btn-success">Create</button> <button onclick="document.getElementById('admin-add-s1').style.display='none';document.getElementById('admin-add-s0').style.display='';" type="button" class="btn btn-outline-danger">Cancel</button>
+ </form>
+ </div>
+ <?php endif; ?>
+</div>
+
+<style>
+ .project-icon {
+ border-radius: 999px;
+ width: 24px;
+ vertical-align: middle;
+ background: lightgray;
+ margin-right: 5px;
+ }
+</style>
+
+<?php require_once $_SERVER["DOCUMENT_ROOT"] . "/includes/admin/footer.php"; ?> \ No newline at end of file
diff --git a/admin/index.php b/admin/index.php
new file mode 100644
index 0000000..0b03f35
--- /dev/null
+++ b/admin/index.php
@@ -0,0 +1,45 @@
+<?php require_once $_SERVER["DOCUMENT_ROOT"] . "/includes/admin/session.php"; global $_USER; ?>
+<?php require_once $_SERVER["DOCUMENT_ROOT"] . "/includes/admin/header.php"; ?>
+
+<br>
+<div class="container">
+ <h1>Welcome back <?= $_USER ?>!</h1>
+ <br>
+
+ <div class="row">
+ <div class="col">
+ <div class="card">
+ <div class="card-body">
+ <h4 class="card-title">PluralKit</h4>
+ <p class="card-text">Configure PluralKit system ID.</p>
+ <a href="/admin/pluralkit" class="btn btn-primary">Manage</a>
+ </div>
+ </div>
+ </div>
+ <div class="col">
+ <div class="card">
+ <div class="card-body">
+ <h4 class="card-title">Projects</h4>
+ <p class="card-text">Add, edit, delete or showcase projects.</p>
+ <a href="/admin/projects" class="btn btn-primary">Manage</a>
+ </div>
+ </div>
+ </div>
+ <div class="col">
+ <div class="card">
+ <div class="card-body">
+ <h4 class="card-title">Contact Info</h4>
+ <p class="card-text">Add, edit or delete contact information.</p>
+ <a href="/admin/contact" class="btn btn-primary">Manage</a>
+ </div>
+ </div>
+ </div>
+ </div>
+ <br>
+
+ <p>This website is managed by <?php $admins = json_decode(file_get_contents($_SERVER["DOCUMENT_ROOT"] . "/data/admins.json"), true); foreach ($admins as $index => $item): ?><b><?= $item ?></b><?php if ($item === $_USER): ?> (you)<?php endif; ?><?php if ($index !== count($admins) - 1): ?><?php if ($index + 1 === count($admins) - 1): ?> and <?php else: ?>, <?php endif; ?><?php endif; ?><?php endforeach; ?>, <a href="/admin/users">edit...</a></p>
+
+ <p class="small text-muted">powered by Pawer Technologies</p>
+</div>
+
+<?php require_once $_SERVER["DOCUMENT_ROOT"] . "/includes/admin/footer.php"; ?> \ No newline at end of file
diff --git a/admin/login/index.php b/admin/login/index.php
new file mode 100644
index 0000000..41c4298
--- /dev/null
+++ b/admin/login/index.php
@@ -0,0 +1,4 @@
+<?php
+
+header("Location: https://github.com/login/oauth/authorize?client_id=" . json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/data/github.json"), true)["id"] . "&redirect_uri=http://$_SERVER[HTTP_HOST]/admin/callback/&allow_signups=false&scope=read:user");
+die();
diff --git a/admin/pluralkit/index.php b/admin/pluralkit/index.php
new file mode 100644
index 0000000..c976882
--- /dev/null
+++ b/admin/pluralkit/index.php
@@ -0,0 +1,50 @@
+<?php require_once $_SERVER["DOCUMENT_ROOT"] . "/includes/admin/session.php"; global $_USER; ?>
+<?php
+
+if (isset($_GET['submit'])) {
+ if (isset($_GET["system-id"])) {
+ file_put_contents($_SERVER["DOCUMENT_ROOT"] . "/data/pluralkit.json", json_encode([
+ "system" => $_GET['system-id']
+ ], JSON_PRETTY_PRINT));
+ header("Location: /admin/pluralkit");
+ die();
+ }
+}
+
+?>
+<?php require_once $_SERVER["DOCUMENT_ROOT"] . "/includes/admin/header.php"; ?>
+
+<br>
+<div class="container">
+ <h1>PluralKit Configuration</h1>
+
+ <p><b>Current System:</b> <?php
+
+ $config = json_decode(file_get_contents($_SERVER["DOCUMENT_ROOT"] . "/data/pluralkit.json"), true);
+ $data = @file_get_contents("https://api.pluralkit.me/v2/systems/$config[system]");
+
+ if (isset($data) && $data !== false):
+ $parsed = json_decode($data, true);
+ ?>
+ <img src="<?= $parsed["avatar_url"] ?>" id="system-icon"><?= $parsed["name"] ?> (<code><?= $parsed["id"] ?></code>)
+ <?php else: ?>
+ <span class="text-danger">Not found, please make sure the ID is entered correctly</span>
+ <?php endif; ?></p>
+ <form class="input-group mb-3" style="max-width: 500px;">
+ <input name="system-id" value="<?= $config['system'] ?>" type="text" class="form-control" placeholder="System ID">
+ <input type="hidden" name="submit">
+ <button class="btn btn-primary" type="submit">Save and apply</button>
+ </form>
+</div>
+
+<style>
+ #system-icon {
+ border-radius: 999px;
+ width: 24px;
+ vertical-align: middle;
+ background: lightgray;
+ margin-right: 5px;
+ }
+</style>
+
+<?php require_once $_SERVER["DOCUMENT_ROOT"] . "/includes/admin/footer.php"; ?> \ No newline at end of file
diff --git a/admin/projects/index.php b/admin/projects/index.php
new file mode 100644
index 0000000..78e2b04
--- /dev/null
+++ b/admin/projects/index.php
@@ -0,0 +1,155 @@
+<?php require_once $_SERVER["DOCUMENT_ROOT"] . "/includes/admin/session.php"; global $_USER; ?>
+<?php
+
+$projects = json_decode(file_get_contents($_SERVER["DOCUMENT_ROOT"] . "/data/projects.json"), true);
+
+if (isset($_GET['submit'])) {
+ if (isset($_GET["showcase-yes"])) {
+ if (isset($projects[(int)$_GET["showcase-yes"]])) {
+ $projects[(int)$_GET["showcase-yes"]]["showcase"] = true;
+ file_put_contents($_SERVER["DOCUMENT_ROOT"] . "/data/projects.json", json_encode($projects, JSON_PRETTY_PRINT));
+ header("Location: /admin/projects");
+ die();
+ }
+ }
+
+ if (isset($_GET["showcase-no"])) {
+ if (isset($projects[(int)$_GET["showcase-no"]])) {
+ $projects[(int)$_GET["showcase-no"]]["showcase"] = false;
+ file_put_contents($_SERVER["DOCUMENT_ROOT"] . "/data/projects.json", json_encode($projects, JSON_PRETTY_PRINT));
+ header("Location: /admin/projects");
+ die();
+ }
+ }
+
+ if (isset($_GET["add-project"]) && isset($_GET["add-project-src"])) {
+ $projects[] = [
+ "name" => $_GET["add-project"],
+ "description" => $_GET["add-project"],
+ "icon" => "about:blank",
+ "source" => $_GET["add-project-src"],
+ "showcase" => false
+ ];
+ file_put_contents($_SERVER["DOCUMENT_ROOT"] . "/data/projects.json", json_encode($projects, JSON_PRETTY_PRINT));
+ header("Location: /admin/projects");
+ die();
+ }
+
+ if (isset($_GET["delete-project"])) {
+ if (isset($projects[(int)$_GET["delete-project"]])) {
+ unset($projects[(int)$_GET["delete-project"]]);
+ file_put_contents($_SERVER["DOCUMENT_ROOT"] . "/data/projects.json", json_encode($projects, JSON_PRETTY_PRINT));
+ header("Location: /admin/projects");
+ die();
+ }
+ }
+
+ if (isset($_GET["edit-project"]) && isset($_GET["edit-project-name"]) && isset($_GET["edit-project-source"]) && isset($_GET["edit-project-icon"]) && isset($_GET["edit-project-description"])) {
+ if (isset($projects[(int)$_GET["edit-project"]])) {
+ $projects[(int)$_GET["edit-project"]]["name"] = $_GET["edit-project-name"];
+ $projects[(int)$_GET["edit-project"]]["description"] = $_GET["edit-project-description"];
+ $projects[(int)$_GET["edit-project"]]["icon"] = $_GET["edit-project-icon"];
+ file_put_contents($_SERVER["DOCUMENT_ROOT"] . "/data/projects.json", json_encode($projects, JSON_PRETTY_PRINT));
+ header("Location: /admin/projects");
+ die();
+ }
+ }
+}
+
+?>
+<?php require_once $_SERVER["DOCUMENT_ROOT"] . "/includes/admin/header.php"; ?>
+
+<br>
+<div class="container">
+ <?php if (isset($_GET['change']) && isset($_GET['edit-project']) && isset($projects[(int)$_GET["edit-project"]])): $project = $projects[(int)$_GET["edit-project"]]; ?>
+
+ <h1>Edit <b><?= $project["name"] ?></b> (<code><?= (int)$_GET["edit-project"] ?></code>)</h1>
+
+ <form>
+ <p>
+ Project Name:<br>
+ <input name="edit-project-name" class="form-control" type="text" value="<?= $project["name"] ?>">
+ </p>
+ <p>
+ Project VCS Repository:<br>
+ <input name="edit-project-source" class="form-control" type="text" value="<?= $project["source"] ?>">
+ </p>
+ <p>
+ Icon URL (can be relative):<br>
+ <input name="edit-project-icon" class="form-control" type="text" value="<?= $project["icon"] ?>">
+ </p>
+ <p>
+ Project Description (can contain HTML tags):<br>
+ <textarea name="edit-project-description" class="form-control font-monospace" type="text"><?= $project["description"] ?></textarea>
+ </p>
+ <input name="submit" type="hidden">
+ <input name="edit-project" type="hidden" value="<?= (int)$_GET["edit-project"] ?>">
+ <button type="submit" class="btn btn-primary">Save and apply changes</button>
+ </form>
+
+ <?php else: ?>
+ <h1>Projects Management</h1>
+ <p>Projects added to this list are publicly shown on the website's Projects page and (for select projects) on the homepage.</p>
+
+ <ul class="list-group">
+ <?php foreach (json_decode(file_get_contents($_SERVER["DOCUMENT_ROOT"] . "/data/projects.json"), true) as $index => $project): ?>
+ <li class="list-group-item">
+ <span style="vertical-align: middle;padding-top:10px;">
+ <img src="<?= $project["icon"] ?>" class="project-icon"> <?= $project["name"] ?>
+ </span>
+ <form style="display:inline;float:right;">
+ <input name="delete-project" type="hidden" value="<?= $index ?>">
+ <input name="submit" type="hidden">
+ <button type="submit" class="btn btn-danger">Remove</button>
+ </form>
+ <form style="display:inline;float:right;margin-right:10px;">
+ <input name="edit-project" type="hidden" value="<?= $index ?>">
+ <input name="change" type="hidden">
+ <button type="submit" class="btn btn-primary">Edit</button>
+ </form>
+ <?php if ($project["showcase"]): ?>
+ <form style="display:inline;float:right;margin-right:10px;">
+ <input name="showcase-no" type="hidden" value="<?= $index ?>">
+ <input name="submit" type="hidden">
+ <button type="submit" class="btn btn-outline-danger">Hide on homepage</button>
+ </form>
+ <?php else: ?>
+ <form style="display:inline;float:right;margin-right:10px;">
+ <input name="showcase-yes" type="hidden" value="<?= $index ?>">
+ <input name="submit" type="hidden">
+ <button type="submit" class="btn btn-outline-success">Show on homepage</button>
+ </form>
+ <?php endif; ?>
+ </li>
+ <?php endforeach; ?>
+ </ul>
+ <br>
+
+ <button type="button" id="admin-add-s0" class="btn btn-outline-primary" onclick="document.getElementById('admin-add-s0').style.display='none';document.getElementById('admin-add-s1').style.display='';document.getElementById('admin-add-s2').focus();">Create another project</button>
+ <div class="card" style="max-width:550px;display:none;" id="admin-add-s1">
+ <form class="card-body">
+ <h4 class="card-title">Create project</h4>
+ <p>Once added, this project will be shown on the Projects page.</p>
+ <p>
+ <input id="admin-add-s2" name="add-project" type="text" class="form-control" placeholder="Project name">
+ <input id="admin-add-s2a" name="add-project-src" type="text" class="form-control" placeholder="VCS repository">
+ </p>
+ <p>You are able to add additional details after creating the project.</p>
+ <input name="submit" type="hidden">
+ <button type="submit" class="btn btn-success">Create</button> <button onclick="document.getElementById('admin-add-s1').style.display='none';document.getElementById('admin-add-s0').style.display='';" type="button" class="btn btn-outline-danger">Cancel</button>
+ </form>
+ </div>
+ <?php endif; ?>
+</div>
+
+<style>
+ .project-icon {
+ border-radius: 999px;
+ width: 24px;
+ vertical-align: middle;
+ background: lightgray;
+ margin-right: 5px;
+ }
+</style>
+
+<?php require_once $_SERVER["DOCUMENT_ROOT"] . "/includes/admin/footer.php"; ?> \ No newline at end of file
diff --git a/admin/users/index.php b/admin/users/index.php
new file mode 100644
index 0000000..1bc81d2
--- /dev/null
+++ b/admin/users/index.php
@@ -0,0 +1,75 @@
+<?php require_once $_SERVER["DOCUMENT_ROOT"] . "/includes/admin/session.php"; global $_USER; ?>
+<?php
+
+if (isset($_GET['submit'])) {
+ $admins = json_decode(file_get_contents($_SERVER["DOCUMENT_ROOT"] . "/data/admins.json"), true);
+
+ if (isset($_GET["delete-user"])) {
+ $newlist = [];
+
+ foreach ($admins as $admin) {
+ if ($admin !== $_GET["delete-user"]) {
+ $newlist[] = $admin;
+ }
+ }
+
+ file_put_contents($_SERVER["DOCUMENT_ROOT"] . "/data/admins.json", json_encode($newlist, JSON_PRETTY_PRINT));
+ header("Location: /admin/users");
+ die();
+ }
+
+ if (isset($_GET["add-user"])) {
+ $admins[] = $_GET["add-user"];
+ file_put_contents($_SERVER["DOCUMENT_ROOT"] . "/data/admins.json", json_encode($admins, JSON_PRETTY_PRINT));
+ header("Location: /admin/users");
+ die();
+ }
+}
+
+?>
+<?php require_once $_SERVER["DOCUMENT_ROOT"] . "/includes/admin/header.php"; ?>
+
+<br>
+<div class="container">
+ <h1>Administrators Management</h1>
+ <p>Administrators added to this list are able to login to this admin panel using their GitHub account. Make sure you trust the person before giving them administrative permissions.</p>
+
+ <ul class="list-group">
+ <?php foreach (json_decode(file_get_contents($_SERVER["DOCUMENT_ROOT"] . "/data/admins.json"), true) as $user): ?>
+ <li class="list-group-item">
+ <form>
+ <span style="vertical-align: middle;padding-top:10px;">
+ <a href="https://github.com/<?= $user ?>" target="_blank"><?= $user ?></a>
+ <?php if ($user === $_USER): ?>
+ <span class="badge bg-warning rounded-pill">You!</span>
+ <?php endif; ?>
+ <?php if ($user === "Minteck"): ?>
+ <span class="badge bg-danger rounded-pill">Immutable</span>
+ <?php endif; ?>
+ </span>
+ <input name="delete-user" type="hidden" value="<?= $user ?>">
+ <input name="submit" type="hidden">
+ <button type="submit" class="btn btn-danger" style="float:right;vertical-align: middle;"
+ <?php if ($user === $_USER || $user === "Minteck"): ?> disabled<?php endif; ?>
+ >Remove</button>
+ </form>
+ </li>
+ <?php endforeach; ?>
+ </ul>
+ <br>
+
+ <button type="button" id="admin-add-s0" class="btn btn-outline-primary" onclick="document.getElementById('admin-add-s0').style.display='none';document.getElementById('admin-add-s1').style.display='';document.getElementById('admin-add-s2').focus();">Add another administrator</button>
+ <div class="card" style="max-width:550px;display:none;" id="admin-add-s1">
+ <form class="card-body">
+ <h4 class="card-title">Add administrator</h4>
+ <p>This will give this user full control over this website, including permission to add or remove other administrators. <b>Make sure you trust this user.</b></p>
+ <p>
+ <input id="admin-add-s2" name="add-user" type="text" class="form-control" placeholder="GitHub user name">
+ </p>
+ <input name="submit" type="hidden">
+ <button type="submit" class="btn btn-success">Add</button> <button onclick="document.getElementById('admin-add-s1').style.display='none';document.getElementById('admin-add-s0').style.display='';" type="button" class="btn btn-outline-danger">Cancel</button>
+ </form>
+ </div>
+</div>
+
+<?php require_once $_SERVER["DOCUMENT_ROOT"] . "/includes/admin/footer.php"; ?> \ No newline at end of file
diff --git a/api/contact/count/index.php b/api/contact/count/index.php
new file mode 100644
index 0000000..809902b
--- /dev/null
+++ b/api/contact/count/index.php
@@ -0,0 +1,5 @@
+<?php
+
+$config = json_decode(file_get_contents($_SERVER["DOCUMENT_ROOT"] . "/data/projects.json"), true);
+header("Content-Type: application/json");
+echo(count($config)); \ No newline at end of file
diff --git a/api/contact/index.php b/api/contact/index.php
new file mode 100644
index 0000000..71cf001
--- /dev/null
+++ b/api/contact/index.php
@@ -0,0 +1,15 @@
+<?php header("Content-Type: text/html"); if (str_ends_with($_SERVER["REQUEST_URI"], "/")) $_SERVER["REQUEST_URI"] = substr($_SERVER["REQUEST_URI"], 0, -1); if (str_contains($_SERVER["REQUEST_URI"], "..")) die(); ?>
+<h1>Available endpoints at <?= $_SERVER['REQUEST_URI'] ?></h1>
+
+<ul>
+ <?php foreach (scandir($_SERVER["DOCUMENT_ROOT"] . $_SERVER['REQUEST_URI']) as $i1): if (is_dir($_SERVER["DOCUMENT_ROOT"] . $_SERVER['REQUEST_URI'] . "/" . $i1) && !str_starts_with($i1, ".")): ?>
+ <li><a href="<?= $_SERVER['REQUEST_URI'] ?>/<?= $i1 ?>"><?= $_SERVER['REQUEST_URI'] ?>/<?= $i1 ?></a><ul>
+
+ <?php foreach (scandir($_SERVER["DOCUMENT_ROOT"] . $_SERVER['REQUEST_URI'] . "/" . $i1) as $i2): if (is_dir($_SERVER["DOCUMENT_ROOT"] . $_SERVER['REQUEST_URI'] . "/" . $i1 . "/" . $i2) && !str_starts_with($i2, ".")): ?>
+ <li><a href="<?= $_SERVER['REQUEST_URI'] ?>/<?= $i1 ?>/<?= $i2 ?>"><?= $_SERVER['REQUEST_URI'] ?>/<?= $i1 ?>/<?= $i2 ?></a></li>
+
+ <?php endif; endforeach; ?>
+ </ul></li>
+ <?php endif; endforeach; ?>
+
+</ul> \ No newline at end of file
diff --git a/api/contact/list/index.php b/api/contact/list/index.php
new file mode 100644
index 0000000..884821c
--- /dev/null
+++ b/api/contact/list/index.php
@@ -0,0 +1,12 @@
+<?php
+
+$config = json_decode(file_get_contents($_SERVER["DOCUMENT_ROOT"] . "/data/projects.json"), true);
+$listed = [];
+header("Content-Type: application/json");
+
+foreach ($config as $project) {
+ unset($project["showcase"]);
+ $listed[] = $project;
+}
+
+die(json_encode($listed)); \ No newline at end of file
diff --git a/data/admins.json b/data/admins.json
new file mode 100644
index 0000000..a3932e5
--- /dev/null
+++ b/data/admins.json
@@ -0,0 +1,4 @@
+[
+ "Minteck",
+ "CloudburstSys"
+] \ No newline at end of file
diff --git a/data/contact.json b/data/contact.json
new file mode 100644
index 0000000..878f22d
--- /dev/null
+++ b/data/contact.json
@@ -0,0 +1,12 @@
+[
+ {
+ "name": "Twittero",
+ "link": "https:\/\/twitter.com\/MinteckPony",
+ "icon": "https:\/\/staging.minteck.org\/assets\/icons\/contact.svg"
+ },
+ {
+ "name": "Mastodon",
+ "link": "https:\/\/equestria.social\/minteck",
+ "icon": "https:\/\/staging.minteck.org\/assets\/icons\/contact.svg"
+ }
+] \ No newline at end of file
diff --git a/data/pluralkit.json b/data/pluralkit.json
index 2704f18..8577b3b 100644
--- a/data/pluralkit.json
+++ b/data/pluralkit.json
@@ -1,3 +1,3 @@
{
- "system": "186730180872634368"
+ "system": "186730180872634368"
} \ No newline at end of file
diff --git a/data/projects.json b/data/projects.json
index a9a3b8a..507a3be 100644
--- a/data/projects.json
+++ b/data/projects.json
@@ -1,16 +1,23 @@
[
- {
- "name": "Some random project",
- "description": "This is a random project",
- "icon": "https://sattelite.cdn.minteck.org/logo.svg",
- "source": "https://gitlab.minteck.org/minteck/rainbow",
- "showcase": true
- },
- {
- "name": "ContactPlus",
- "description": "A fake PIM app created for this template",
- "icon": "https://sattelite.cdn.minteck.org/logo.svg",
- "source": "https://gitlab.minteck.org/minteck/cloudsdale",
- "showcase": false
- }
+ {
+ "name": "Some random project",
+ "description": "This is a random project",
+ "icon": "https:\/\/sattelite.cdn.minteck.org\/logo.svg",
+ "source": "https:\/\/gitlab.minteck.org\/minteck\/rainbow",
+ "showcase": false
+ },
+ {
+ "name": "ContactPlus",
+ "description": "A fake PIM app created for this template",
+ "icon": "https:\/\/sattelite.cdn.minteck.org\/logo.svg",
+ "source": "https:\/\/gitlab.minteck.org\/minteck\/cloudsdale",
+ "showcase": false
+ },
+ {
+ "name": "Random Projecto",
+ "description": "Random Project, yay!",
+ "icon": "about:blank",
+ "source": "https:\/\/gitlab.minteck.org\/minteck\/kartik-client",
+ "showcase": false
+ }
] \ No newline at end of file
diff --git a/includes/admin/footer.php b/includes/admin/footer.php
new file mode 100644
index 0000000..691287b
--- /dev/null
+++ b/includes/admin/footer.php
@@ -0,0 +1,2 @@
+</body>
+</html> \ No newline at end of file
diff --git a/includes/admin/header.php b/includes/admin/header.php
new file mode 100644
index 0000000..58a1b74
--- /dev/null
+++ b/includes/admin/header.php
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <title>
+ Project Cloudsdale Admin Panel
+ </title>
+ <meta http-equiv="X-UA-Compatible" content="ie=edge">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <link rel="stylesheet" href="/assets/css/bootstrap.css">
+ <script src="/assets/js/bootstrap.js"></script>
+</head>
+<body>
+ <?php require_once $_SERVER["DOCUMENT_ROOT"] . "/includes/admin/navigation.php"; ?> \ No newline at end of file
diff --git a/includes/admin/navigation.php b/includes/admin/navigation.php
new file mode 100644
index 0000000..23cc361
--- /dev/null
+++ b/includes/admin/navigation.php
@@ -0,0 +1,27 @@
+<nav class="navbar navbar-expand-sm bg-dark navbar-dark">
+ <div class="container-fluid">
+ <a class="navbar-brand" href="/admin">Project Cloudsdale Admin Panel</a>
+ <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#collapsibleNavbar">
+ <span class="navbar-toggler-icon"></span>
+ </button>
+ <div class="collapse navbar-collapse" id="collapsibleNavbar">
+ <ul class="navbar-nav">
+ <li class="nav-item">
+ <a class="nav-link" href="/">← Go back to website</a>
+ </li>
+ <li class="nav-item">
+ <a class="nav-link" href="/admin/pluralkit">PluralKit</a>
+ </li>
+ <li class="nav-item">
+ <a class="nav-link" href="/admin/users">Administrators</a>
+ </li>
+ <li class="nav-item">
+ <a class="nav-link" href="/admin/projects">Projects</a>
+ </li>
+ <li class="nav-item">
+ <a class="nav-link" href="/admin/contact">Contact Info</a>
+ </li>
+ </ul>
+ </div>
+ </div>
+</nav> \ No newline at end of file
diff --git a/includes/admin/session.php b/includes/admin/session.php
new file mode 100644
index 0000000..3c75ee0
--- /dev/null
+++ b/includes/admin/session.php
@@ -0,0 +1,15 @@
+<?php
+
+global $_USER;
+$admin = true;
+if (!isset($_COOKIE["pcdAdminToken"])) {
+ $admin = false;
+ if (isset($__ADMIN)) header("Location: /admin/login") and die();
+} else {
+ if (!(!str_contains("/", $_COOKIE['pcdAdminToken']) && !str_contains(".", $_COOKIE['pcdAdminToken']) && (file_exists($_SERVER['DOCUMENT_ROOT'] . "/data/tokens/" . $_COOKIE['pcdAdminToken'])))) {
+ $admin = false;
+ if (isset($__ADMIN)) header("Location: /admin/login") and die();
+ } else {
+ $_USER = trim(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/data/tokens/" . $_COOKIE['pcdAdminToken']));
+ }
+}
diff --git a/includes/gui/navigation.php b/includes/gui/navigation.php
index d68b3bf..68d27c5 100644
--- a/includes/gui/navigation.php
+++ b/includes/gui/navigation.php
@@ -1,17 +1,21 @@
<nav class="navbar navbar-expand-sm bg-dark navbar-dark">
<div class="container-fluid">
<a class="navbar-brand" href="/">Cloudburst System</a>
-
- <ul class="navbar-nav">
- <li class="nav-item">
- <a class="nav-link" href="/members">System Members</a>
- </li>
- <li class="nav-item">
- <a class="nav-link" href="/projects">Projects</a>
- </li>
- <li class="nav-item">
- <a class="nav-link" href="/social">Social</a>
- </li>
- </ul>
+ <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#collapsibleNavbar">
+ <span class="navbar-toggler-icon"></span>
+ </button>
+ <div class="collapse navbar-collapse" id="collapsibleNavbar">
+ <ul class="navbar-nav" style="margin-left: auto;">
+ <li class="nav-item">
+ <a class="nav-link" href="/members">System Members</a>
+ </li>
+ <li class="nav-item">
+ <a class="nav-link" href="/projects">Projects</a>
+ </li>
+ <li class="nav-item">
+ <a class="nav-link" href="/social">Social</a>
+ </li>
+ </ul>
+ </div>
</div>
</nav> \ No newline at end of file