feat(meta): separate between user and and admin pages
cargo devel CI / cargo CI (push) Successful in 2m4s Details

This commit is contained in:
Christoph J. Scherr 2024-09-08 03:57:42 +02:00
parent b3d542327b
commit 4714c6b2a7
7 changed files with 787 additions and 17 deletions

220
data/www/admin/index.html Normal file
View File

@ -0,0 +1,220 @@
<!doctype html>
<html lang="en" data-bs-theme="auto">
<head>
<script src="https://getbootstrap.com/docs/5.3/assets/js/color-modes.js"></script>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="author" content="{{author}}">
<title>{{title}}</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@docsearch/css@3">
<link href="https://getbootstrap.com/docs/5.3/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
<!-- Favicons -->
<link rel="icon" href="https://icons.getbootstrap.com/assets/icons/safe-fill.svg">
<meta name="theme-color" content="#712cf9">
<link rel="stylesheet" href="/styles.css">
</head>
<body>
<!-- All the svgs we use later -->
<svg xmlns="http://www.w3.org/2000/svg" class="d-none">
<symbol id="check2" viewBox="0 0 16 16">
<path
d="M13.854 3.646a.5.5 0 0 1 0 .708l-7 7a.5.5 0 0 1-.708 0l-3.5-3.5a.5.5 0 1 1 .708-.708L6.5 10.293l6.646-6.647a.5.5 0 0 1 .708 0z" />
</symbol>
<symbol id="circle-half" viewBox="0 0 16 16">
<path d="M8 15A7 7 0 1 0 8 1v14zm0 1A8 8 0 1 1 8 0a8 8 0 0 1 0 16z" />
</symbol>
<symbol id="moon-stars-fill" viewBox="0 0 16 16">
<path
d="M6 .278a.768.768 0 0 1 .08.858 7.208 7.208 0 0 0-.878 3.46c0 4.021 3.278 7.277 7.318 7.277.527 0 1.04-.055 1.533-.16a.787.787 0 0 1 .81.316.733.733 0 0 1-.031.893A8.349 8.349 0 0 1 8.344 16C3.734 16 0 12.286 0 7.71 0 4.266 2.114 1.312 5.124.06A.752.752 0 0 1 6 .278z" />
<path
d="M10.794 3.148a.217.217 0 0 1 .412 0l.387 1.162c.173.518.579.924 1.097 1.097l1.162.387a.217.217 0 0 1 0 .412l-1.162.387a1.734 1.734 0 0 0-1.097 1.097l-.387 1.162a.217.217 0 0 1-.412 0l-.387-1.162A1.734 1.734 0 0 0 9.31 6.593l-1.162-.387a.217.217 0 0 1 0-.412l1.162-.387a1.734 1.734 0 0 0 1.097-1.097l.387-1.162zM13.863.099a.145.145 0 0 1 .274 0l.258.774c.115.346.386.617.732.732l.774.258a.145.145 0 0 1 0 .274l-.774.258a1.156 1.156 0 0 0-.732.732l-.258.774a.145.145 0 0 1-.274 0l-.258-.774a1.156 1.156 0 0 0-.732-.732l-.774-.258a.145.145 0 0 1 0-.274l.774-.258c.346-.115.617-.386.732-.732L13.863.1z" />
</symbol>
<symbol id="sun-fill" viewBox="0 0 16 16">
<path
d="M8 12a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM8 0a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 0zm0 13a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 13zm8-5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2a.5.5 0 0 1 .5.5zM3 8a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2A.5.5 0 0 1 3 8zm10.657-5.657a.5.5 0 0 1 0 .707l-1.414 1.415a.5.5 0 1 1-.707-.708l1.414-1.414a.5.5 0 0 1 .707 0zm-9.193 9.193a.5.5 0 0 1 0 .707L3.05 13.657a.5.5 0 0 1-.707-.707l1.414-1.414a.5.5 0 0 1 .707 0zm9.193 2.121a.5.5 0 0 1-.707 0l-1.414-1.414a.5.5 0 0 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .707zM4.464 4.465a.5.5 0 0 1-.707 0L2.343 3.05a.5.5 0 1 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .708z" />
</symbol>
<symbol id="arrow-right-circle" viewBox="0 0 16 16">
<path
d="M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0zM4.5 7.5a.5.5 0 0 0 0 1h5.793l-2.147 2.146a.5.5 0 0 0 .708.708l3-3a.5.5 0 0 0 0-.708l-3-3a.5.5 0 1 0-.708.708L10.293 7.5H4.5z" />
</symbol>
<symbol id="vault" viewBox="0 0 16 16">
<title>Bootstrap</title>
<path d="M9.778 9.414A2 2 0 1 1 6.95 6.586a2 2 0 0 1 2.828 2.828" />
<path
d="M2.5 0A1.5 1.5 0 0 0 1 1.5V3H.5a.5.5 0 0 0 0 1H1v3.5H.5a.5.5 0 0 0 0 1H1V12H.5a.5.5 0 0 0 0 1H1v1.5A1.5 1.5 0 0 0 2.5 16h12a1.5 1.5 0 0 0 1.5-1.5v-13A1.5 1.5 0 0 0 14.5 0zm3.036 4.464 1.09 1.09a3 3 0 0 1 3.476 0l1.09-1.09a.5.5 0 1 1 .707.708l-1.09 1.09c.74 1.037.74 2.44 0 3.476l1.09 1.09a.5.5 0 1 1-.707.708l-1.09-1.09a3 3 0 0 1-3.476 0l-1.09 1.09a.5.5 0 1 1-.708-.708l1.09-1.09a3 3 0 0 1 0-3.476l-1.09-1.09a.5.5 0 1 1 .708-.708M14 6.5v3a.5.5 0 0 1-1 0v-3a.5.5 0 0 1 1 0" />
</symbol>
</svg>
<!-- theme switcher -->
<div class="dropdown position-fixed bottom-0 end-0 mb-3 me-3 bd-mode-toggle">
<button class="btn btn-bd-primary py-2 dropdown-toggle d-flex align-items-center" id="bd-theme" type="button"
aria-expanded="false" data-bs-toggle="dropdown" aria-label="Toggle theme (auto)">
<svg class="bi my-1 theme-icon-active" width="1em" height="1em">
<use href="#circle-half"></use>
</svg>
<span class="visually-hidden" id="bd-theme-text">Toggle theme</span>
</button>
<ul class="dropdown-menu dropdown-menu-end shadow" aria-labelledby="bd-theme-text">
<li>
<button type="button" class="dropdown-item d-flex align-items-center" data-bs-theme-value="light"
aria-pressed="false">
<svg class="bi me-2 opacity-50" width="1em" height="1em">
<use href="#sun-fill"></use>
</svg>
Light
<svg class="bi ms-auto d-none" width="1em" height="1em">
<use href="#check2"></use>
</svg>
</button>
</li>
<li>
<button type="button" class="dropdown-item d-flex align-items-center" data-bs-theme-value="dark"
aria-pressed="false">
<svg class="bi me-2 opacity-50" width="1em" height="1em">
<use href="#moon-stars-fill"></use>
</svg>
Dark
<svg class="bi ms-auto d-none" width="1em" height="1em">
<use href="#check2"></use>
</svg>
</button>
</li>
<li>
<button type="button" class="dropdown-item d-flex align-items-center active" data-bs-theme-value="auto"
aria-pressed="true">
<svg class="bi me-2 opacity-50" width="1em" height="1em">
<use href="#circle-half"></use>
</svg>
Auto
<svg class="bi ms-auto d-none" width="1em" height="1em">
<use href="#check2"></use>
</svg>
</button>
</li>
</ul>
</div>
<!-- basically everything but with some margin -->
<div class="col-lg-8 mx-auto p-4 py-md-5">
<header class="d-flex align-items-center pb-3 mb-5 border-bottom">
<a href="/" class="d-flex align-items-center text-body-emphasis text-decoration-none">
<svg class="bi me-2" width="40" height="32">
<use xlink:href="#vault" />
</svg>
<span class="fs-4">{{title}}</span>
</a>
</header>
<main>
<h1 class="text-body-emphasis">{{title}} Admin Interface</h1>
<p class="fs-5 col-md-8">
You have reached the {{title}} Admin Interface. This site can be used by
the host of the challenge to see the challenge progress, solution, and
hints for that challenge. This site is <b>NOT</b> part of the
challenge.
</p>
<hr class="mb-5">
<div class="row g-5 mb-5">
<h2 class="text-body-emphasis">Challenge {{ challenge_idx}} &mdash; {{ challenge_title }}</h2>
<p class="mt-1 mb-3">{{ challenge_description }}</p>
<div class="col mt-1">
<h3>Hints</h3>
<button class="btn btn-primary my-2" type="button" data-bs-toggle="collapse" data-bs-target="#hints"
aria-expanded="false" aria-controls="collapseExample">
Show hints
</button>
<div class="collapse" id="hints">
<ul class="list-unstyled ps-0">
{% for hint in challenge_hints %}
<li>
<p>
<svg class="bi" width="16" height="16">
<use xlink:href="#arrow-right-circle" />
</svg>
{{hint}}
</p>
</li>
{% endfor %}
</ul>
</div>
</div>
<div class="col mt-1">
<h3>Solution</h3>
<button class="btn btn-primary my-2" type="button" data-bs-toggle="collapse" data-bs-target="#solution"
aria-expanded="false" aria-controls="collapseExample">
Show solution
</button>
<div class="collapse" id="solution">
<p>
<svg class="bi" width="16" height="16">
<use xlink:href="#arrow-right-circle" />
</svg>
{{challenge_solution}}
</p>
</div>
</div>
</div>
<hr class="mb-5">
<div class="row g-5">
<div class="col-md-6">
<h2 class="text-body-emphasis">Contestants</h2>
<p>
There are cuttently {{ contestants_amount }} contestants.
These contestants currently have had at least one connection to
the challenge:
</p>
<ul class="list-unstyled ps-0">
{% for contestant in contestants %}
<li>
<a class="icon-link mb-1" href="https://whatismyipaddress.com/ip/{{ contestant.ip }}">
<svg class="bi" width="16" height="16">
<use xlink:href="#arrow-right-circle" />
</svg>
{{ contestant.ip }}
</a>
</li>
{% endfor %}
</ul>
</div>
<div class="col-md-6">
<h2 class="text-body-emphasis">Winners</h2>
<p>
There are cuttently {{ winners_amount }} winners. These contestants currently have been sent the secret:
</p>
<ul class="list-unstyled ps-0">
{% for winner in winners %}
<li>
<a class="icon-link mb-1" href="https://whatismyipaddress.com/ip/{{ winner.ip }}">
<svg class="bi" width="16" height="16">
<use xlink:href="#arrow-right-circle" />
</svg>
{{ winner.ip }}
</a>
</li>
{% endfor %}
</ul>
</div>
</div>
</main>
<footer class="pt-5 my-5 text-body-secondary border-top">
Created by {{ author }} &copy; {{year}}
</footer>
</div>
<script src="https://getbootstrap.com/docs/5.3/dist/js/bootstrap.bundle.min.js"
integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz"
crossorigin="anonymous"></script>
</body>
</html>

221
data/www/details.html Normal file
View File

@ -0,0 +1,221 @@
<!doctype html>
<html lang="en" data-bs-theme="auto">
<head>
<script src="https://getbootstrap.com/docs/5.3/assets/js/color-modes.js"></script>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="author" content="{{author}}">
<title>{{title}}</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@docsearch/css@3">
<link href="https://getbootstrap.com/docs/5.3/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
<!-- Favicons -->
<link rel="icon" href="https://icons.getbootstrap.com/assets/icons/safe-fill.svg">
<meta name="theme-color" content="#712cf9">
<link rel="stylesheet" href="/styles.css">
</head>
<body>
<!-- All the svgs we use later -->
<svg xmlns="http://www.w3.org/2000/svg" class="d-none">
<symbol id="check2" viewBox="0 0 16 16">
<path
d="M13.854 3.646a.5.5 0 0 1 0 .708l-7 7a.5.5 0 0 1-.708 0l-3.5-3.5a.5.5 0 1 1 .708-.708L6.5 10.293l6.646-6.647a.5.5 0 0 1 .708 0z" />
</symbol>
<symbol id="circle-half" viewBox="0 0 16 16">
<path d="M8 15A7 7 0 1 0 8 1v14zm0 1A8 8 0 1 1 8 0a8 8 0 0 1 0 16z" />
</symbol>
<symbol id="moon-stars-fill" viewBox="0 0 16 16">
<path
d="M6 .278a.768.768 0 0 1 .08.858 7.208 7.208 0 0 0-.878 3.46c0 4.021 3.278 7.277 7.318 7.277.527 0 1.04-.055 1.533-.16a.787.787 0 0 1 .81.316.733.733 0 0 1-.031.893A8.349 8.349 0 0 1 8.344 16C3.734 16 0 12.286 0 7.71 0 4.266 2.114 1.312 5.124.06A.752.752 0 0 1 6 .278z" />
<path
d="M10.794 3.148a.217.217 0 0 1 .412 0l.387 1.162c.173.518.579.924 1.097 1.097l1.162.387a.217.217 0 0 1 0 .412l-1.162.387a1.734 1.734 0 0 0-1.097 1.097l-.387 1.162a.217.217 0 0 1-.412 0l-.387-1.162A1.734 1.734 0 0 0 9.31 6.593l-1.162-.387a.217.217 0 0 1 0-.412l1.162-.387a1.734 1.734 0 0 0 1.097-1.097l.387-1.162zM13.863.099a.145.145 0 0 1 .274 0l.258.774c.115.346.386.617.732.732l.774.258a.145.145 0 0 1 0 .274l-.774.258a1.156 1.156 0 0 0-.732.732l-.258.774a.145.145 0 0 1-.274 0l-.258-.774a1.156 1.156 0 0 0-.732-.732l-.774-.258a.145.145 0 0 1 0-.274l.774-.258c.346-.115.617-.386.732-.732L13.863.1z" />
</symbol>
<symbol id="sun-fill" viewBox="0 0 16 16">
<path
d="M8 12a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM8 0a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 0zm0 13a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 13zm8-5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2a.5.5 0 0 1 .5.5zM3 8a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2A.5.5 0 0 1 3 8zm10.657-5.657a.5.5 0 0 1 0 .707l-1.414 1.415a.5.5 0 1 1-.707-.708l1.414-1.414a.5.5 0 0 1 .707 0zm-9.193 9.193a.5.5 0 0 1 0 .707L3.05 13.657a.5.5 0 0 1-.707-.707l1.414-1.414a.5.5 0 0 1 .707 0zm9.193 2.121a.5.5 0 0 1-.707 0l-1.414-1.414a.5.5 0 0 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .707zM4.464 4.465a.5.5 0 0 1-.707 0L2.343 3.05a.5.5 0 1 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .708z" />
</symbol>
<symbol id="arrow-right-circle" viewBox="0 0 16 16">
<path
d="M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0zM4.5 7.5a.5.5 0 0 0 0 1h5.793l-2.147 2.146a.5.5 0 0 0 .708.708l3-3a.5.5 0 0 0 0-.708l-3-3a.5.5 0 1 0-.708.708L10.293 7.5H4.5z" />
</symbol>
<symbol id="vault" viewBox="0 0 16 16">
<title>Bootstrap</title>
<path d="M9.778 9.414A2 2 0 1 1 6.95 6.586a2 2 0 0 1 2.828 2.828" />
<path
d="M2.5 0A1.5 1.5 0 0 0 1 1.5V3H.5a.5.5 0 0 0 0 1H1v3.5H.5a.5.5 0 0 0 0 1H1V12H.5a.5.5 0 0 0 0 1H1v1.5A1.5 1.5 0 0 0 2.5 16h12a1.5 1.5 0 0 0 1.5-1.5v-13A1.5 1.5 0 0 0 14.5 0zm3.036 4.464 1.09 1.09a3 3 0 0 1 3.476 0l1.09-1.09a.5.5 0 1 1 .707.708l-1.09 1.09c.74 1.037.74 2.44 0 3.476l1.09 1.09a.5.5 0 1 1-.707.708l-1.09-1.09a3 3 0 0 1-3.476 0l-1.09 1.09a.5.5 0 1 1-.708-.708l1.09-1.09a3 3 0 0 1 0-3.476l-1.09-1.09a.5.5 0 1 1 .708-.708M14 6.5v3a.5.5 0 0 1-1 0v-3a.5.5 0 0 1 1 0" />
</symbol>
</svg>
<!-- theme switcher -->
<div class="dropdown position-fixed bottom-0 end-0 mb-3 me-3 bd-mode-toggle">
<button class="btn btn-bd-primary py-2 dropdown-toggle d-flex align-items-center" id="bd-theme" type="button"
aria-expanded="false" data-bs-toggle="dropdown" aria-label="Toggle theme (auto)">
<svg class="bi my-1 theme-icon-active" width="1em" height="1em">
<use href="#circle-half"></use>
</svg>
<span class="visually-hidden" id="bd-theme-text">Toggle theme</span>
</button>
<ul class="dropdown-menu dropdown-menu-end shadow" aria-labelledby="bd-theme-text">
<li>
<button type="button" class="dropdown-item d-flex align-items-center" data-bs-theme-value="light"
aria-pressed="false">
<svg class="bi me-2 opacity-50" width="1em" height="1em">
<use href="#sun-fill"></use>
</svg>
Light
<svg class="bi ms-auto d-none" width="1em" height="1em">
<use href="#check2"></use>
</svg>
</button>
</li>
<li>
<button type="button" class="dropdown-item d-flex align-items-center" data-bs-theme-value="dark"
aria-pressed="false">
<svg class="bi me-2 opacity-50" width="1em" height="1em">
<use href="#moon-stars-fill"></use>
</svg>
Dark
<svg class="bi ms-auto d-none" width="1em" height="1em">
<use href="#check2"></use>
</svg>
</button>
</li>
<li>
<button type="button" class="dropdown-item d-flex align-items-center active" data-bs-theme-value="auto"
aria-pressed="true">
<svg class="bi me-2 opacity-50" width="1em" height="1em">
<use href="#circle-half"></use>
</svg>
Auto
<svg class="bi ms-auto d-none" width="1em" height="1em">
<use href="#check2"></use>
</svg>
</button>
</li>
</ul>
</div>
<!-- basically everything but with some margin -->
<div class="col-lg-8 mx-auto p-4 py-md-5">
<header class="d-flex align-items-center pb-3 mb-5 border-bottom">
<a href="/" class="d-flex align-items-center text-body-emphasis text-decoration-none">
<svg class="bi me-2" width="40" height="32">
<use xlink:href="#vault" />
</svg>
<span class="fs-4">{{title}}</span>
</a>
</header>
<main>
<h1 class="text-body-emphasis">{{title}} Admin Interface</h1>
<p class="fs-5 col-md-8">
You have reached the {{title}} Admin Interface. This site can be used by
the host of the challenge to see the challenge progress, solution, and
hints for that challenge. This site is <b>NOT</b> part of the
challenge.
</p>
<hr class="mb-5">
<div class="row g-5 mb-5">
<h2 class="text-body-emphasis">Challenge {{ challenge_idx}} &mdash; {{ challenge_title }}</h2>
<p class="mt-1 mb-3">{{ challenge_description }}</p>
<div class="col mt-1">
<h3>Hints</h3>
<button class="btn btn-primary my-2" type="button" data-bs-toggle="collapse" data-bs-target="#hints"
aria-expanded="false" aria-controls="collapseExample">
Show hints
</button>
<div class="collapse" id="hints">
<ul class="list-unstyled ps-0">
{% for hint in challenge_hints %}
<li>
<p>
<svg class="bi" width="16" height="16">
<use xlink:href="#arrow-right-circle" />
</svg>
{{hint}}
</p>
</li>
{% endfor %}
</ul>
</div>
</div>
<div class="col mt-1">
<h3>Solution</h3>
<button class="btn btn-primary my-2" type="button" data-bs-toggle="collapse"
data-bs-target="#solution" aria-expanded="false" aria-controls="collapseExample">
Show solution
</button>
<div class="collapse" id="solution">
<p>
<svg class="bi" width="16" height="16">
<use xlink:href="#arrow-right-circle" />
</svg>
{{challenge_solution}}
</p>
</div>
</div>
</div>
<hr class="mb-5">
<div class="row g-5">
<div class="col-md-6">
<h2 class="text-body-emphasis">Contestants</h2>
<p>
There are cuttently {{ contestants_amount }} contestants.
These contestants currently have had at least one connection to
the challenge:
</p>
<ul class="list-unstyled ps-0">
{% for contestant in contestants %}
<li>
<a class="icon-link mb-1" href="https://whatismyipaddress.com/ip/{{ contestant.ip }}">
<svg class="bi" width="16" height="16">
<use xlink:href="#arrow-right-circle" />
</svg>
{{ contestant.ip }}
</a>
</li>
{% endfor %}
</ul>
</div>
<div class="col-md-6">
<h2 class="text-body-emphasis">Winners</h2>
<p>
There are cuttently {{ winners_amount }} winners. These contestants currently have been sent the
secret:
</p>
<ul class="list-unstyled ps-0">
{% for winner in winners %}
<li>
<a class="icon-link mb-1" href="https://whatismyipaddress.com/ip/{{ winner.ip }}">
<svg class="bi" width="16" height="16">
<use xlink:href="#arrow-right-circle" />
</svg>
{{ winner.ip }}
</a>
</li>
{% endfor %}
</ul>
</div>
</div>
</main>
<footer class="pt-5 my-5 text-body-secondary border-top">
Created by {{ author }} &copy; {{year}}
</footer>
</div>
<script src="https://getbootstrap.com/docs/5.3/dist/js/bootstrap.bundle.min.js"
integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz"
crossorigin="anonymous"></script>
</body>
</html>

221
data/www/index.html Normal file
View File

@ -0,0 +1,221 @@
<!doctype html>
<html lang="en" data-bs-theme="auto">
<head>
<script src="https://getbootstrap.com/docs/5.3/assets/js/color-modes.js"></script>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="author" content="{{author}}">
<title>{{title}}</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@docsearch/css@3">
<link href="https://getbootstrap.com/docs/5.3/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
<!-- Favicons -->
<link rel="icon" href="https://icons.getbootstrap.com/assets/icons/safe-fill.svg">
<meta name="theme-color" content="#712cf9">
<link rel="stylesheet" href="/styles.css">
</head>
<body>
<!-- All the svgs we use later -->
<svg xmlns="http://www.w3.org/2000/svg" class="d-none">
<symbol id="check2" viewBox="0 0 16 16">
<path
d="M13.854 3.646a.5.5 0 0 1 0 .708l-7 7a.5.5 0 0 1-.708 0l-3.5-3.5a.5.5 0 1 1 .708-.708L6.5 10.293l6.646-6.647a.5.5 0 0 1 .708 0z" />
</symbol>
<symbol id="circle-half" viewBox="0 0 16 16">
<path d="M8 15A7 7 0 1 0 8 1v14zm0 1A8 8 0 1 1 8 0a8 8 0 0 1 0 16z" />
</symbol>
<symbol id="moon-stars-fill" viewBox="0 0 16 16">
<path
d="M6 .278a.768.768 0 0 1 .08.858 7.208 7.208 0 0 0-.878 3.46c0 4.021 3.278 7.277 7.318 7.277.527 0 1.04-.055 1.533-.16a.787.787 0 0 1 .81.316.733.733 0 0 1-.031.893A8.349 8.349 0 0 1 8.344 16C3.734 16 0 12.286 0 7.71 0 4.266 2.114 1.312 5.124.06A.752.752 0 0 1 6 .278z" />
<path
d="M10.794 3.148a.217.217 0 0 1 .412 0l.387 1.162c.173.518.579.924 1.097 1.097l1.162.387a.217.217 0 0 1 0 .412l-1.162.387a1.734 1.734 0 0 0-1.097 1.097l-.387 1.162a.217.217 0 0 1-.412 0l-.387-1.162A1.734 1.734 0 0 0 9.31 6.593l-1.162-.387a.217.217 0 0 1 0-.412l1.162-.387a1.734 1.734 0 0 0 1.097-1.097l.387-1.162zM13.863.099a.145.145 0 0 1 .274 0l.258.774c.115.346.386.617.732.732l.774.258a.145.145 0 0 1 0 .274l-.774.258a1.156 1.156 0 0 0-.732.732l-.258.774a.145.145 0 0 1-.274 0l-.258-.774a1.156 1.156 0 0 0-.732-.732l-.774-.258a.145.145 0 0 1 0-.274l.774-.258c.346-.115.617-.386.732-.732L13.863.1z" />
</symbol>
<symbol id="sun-fill" viewBox="0 0 16 16">
<path
d="M8 12a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM8 0a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 0zm0 13a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 13zm8-5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2a.5.5 0 0 1 .5.5zM3 8a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2A.5.5 0 0 1 3 8zm10.657-5.657a.5.5 0 0 1 0 .707l-1.414 1.415a.5.5 0 1 1-.707-.708l1.414-1.414a.5.5 0 0 1 .707 0zm-9.193 9.193a.5.5 0 0 1 0 .707L3.05 13.657a.5.5 0 0 1-.707-.707l1.414-1.414a.5.5 0 0 1 .707 0zm9.193 2.121a.5.5 0 0 1-.707 0l-1.414-1.414a.5.5 0 0 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .707zM4.464 4.465a.5.5 0 0 1-.707 0L2.343 3.05a.5.5 0 1 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .708z" />
</symbol>
<symbol id="arrow-right-circle" viewBox="0 0 16 16">
<path
d="M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0zM4.5 7.5a.5.5 0 0 0 0 1h5.793l-2.147 2.146a.5.5 0 0 0 .708.708l3-3a.5.5 0 0 0 0-.708l-3-3a.5.5 0 1 0-.708.708L10.293 7.5H4.5z" />
</symbol>
<symbol id="vault" viewBox="0 0 16 16">
<title>Bootstrap</title>
<path d="M9.778 9.414A2 2 0 1 1 6.95 6.586a2 2 0 0 1 2.828 2.828" />
<path
d="M2.5 0A1.5 1.5 0 0 0 1 1.5V3H.5a.5.5 0 0 0 0 1H1v3.5H.5a.5.5 0 0 0 0 1H1V12H.5a.5.5 0 0 0 0 1H1v1.5A1.5 1.5 0 0 0 2.5 16h12a1.5 1.5 0 0 0 1.5-1.5v-13A1.5 1.5 0 0 0 14.5 0zm3.036 4.464 1.09 1.09a3 3 0 0 1 3.476 0l1.09-1.09a.5.5 0 1 1 .707.708l-1.09 1.09c.74 1.037.74 2.44 0 3.476l1.09 1.09a.5.5 0 1 1-.707.708l-1.09-1.09a3 3 0 0 1-3.476 0l-1.09 1.09a.5.5 0 1 1-.708-.708l1.09-1.09a3 3 0 0 1 0-3.476l-1.09-1.09a.5.5 0 1 1 .708-.708M14 6.5v3a.5.5 0 0 1-1 0v-3a.5.5 0 0 1 1 0" />
</symbol>
</svg>
<!-- theme switcher -->
<div class="dropdown position-fixed bottom-0 end-0 mb-3 me-3 bd-mode-toggle">
<button class="btn btn-bd-primary py-2 dropdown-toggle d-flex align-items-center" id="bd-theme" type="button"
aria-expanded="false" data-bs-toggle="dropdown" aria-label="Toggle theme (auto)">
<svg class="bi my-1 theme-icon-active" width="1em" height="1em">
<use href="#circle-half"></use>
</svg>
<span class="visually-hidden" id="bd-theme-text">Toggle theme</span>
</button>
<ul class="dropdown-menu dropdown-menu-end shadow" aria-labelledby="bd-theme-text">
<li>
<button type="button" class="dropdown-item d-flex align-items-center" data-bs-theme-value="light"
aria-pressed="false">
<svg class="bi me-2 opacity-50" width="1em" height="1em">
<use href="#sun-fill"></use>
</svg>
Light
<svg class="bi ms-auto d-none" width="1em" height="1em">
<use href="#check2"></use>
</svg>
</button>
</li>
<li>
<button type="button" class="dropdown-item d-flex align-items-center" data-bs-theme-value="dark"
aria-pressed="false">
<svg class="bi me-2 opacity-50" width="1em" height="1em">
<use href="#moon-stars-fill"></use>
</svg>
Dark
<svg class="bi ms-auto d-none" width="1em" height="1em">
<use href="#check2"></use>
</svg>
</button>
</li>
<li>
<button type="button" class="dropdown-item d-flex align-items-center active" data-bs-theme-value="auto"
aria-pressed="true">
<svg class="bi me-2 opacity-50" width="1em" height="1em">
<use href="#circle-half"></use>
</svg>
Auto
<svg class="bi ms-auto d-none" width="1em" height="1em">
<use href="#check2"></use>
</svg>
</button>
</li>
</ul>
</div>
<!-- basically everything but with some margin -->
<div class="col-lg-8 mx-auto p-4 py-md-5">
<header class="d-flex align-items-center pb-3 mb-5 border-bottom">
<a href="/" class="d-flex align-items-center text-body-emphasis text-decoration-none">
<svg class="bi me-2" width="40" height="32">
<use xlink:href="#vault" />
</svg>
<span class="fs-4">{{title}}</span>
</a>
</header>
<main>
<h1 class="text-body-emphasis">{{title}} Admin Interface</h1>
<p class="fs-5 col-md-8">
You have reached the {{title}} Admin Interface. This site can be used by
the host of the challenge to see the challenge progress, solution, and
hints for that challenge. This site is <b>NOT</b> part of the
challenge.
</p>
<hr class="mb-5">
<div class="row g-5 mb-5">
<h2 class="text-body-emphasis">Challenge {{ challenge_idx}} &mdash; {{ challenge_title }}</h2>
<p class="mt-1 mb-3">{{ challenge_description }}</p>
<div class="col mt-1">
<h3>Hints</h3>
<button class="btn btn-primary my-2" type="button" data-bs-toggle="collapse" data-bs-target="#hints"
aria-expanded="false" aria-controls="collapseExample">
Show hints
</button>
<div class="collapse" id="hints">
<ul class="list-unstyled ps-0">
{% for hint in challenge_hints %}
<li>
<p>
<svg class="bi" width="16" height="16">
<use xlink:href="#arrow-right-circle" />
</svg>
{{hint}}
</p>
</li>
{% endfor %}
</ul>
</div>
</div>
<div class="col mt-1">
<h3>Solution</h3>
<button class="btn btn-primary my-2" type="button" data-bs-toggle="collapse"
data-bs-target="#solution" aria-expanded="false" aria-controls="collapseExample">
Show solution
</button>
<div class="collapse" id="solution">
<p>
<svg class="bi" width="16" height="16">
<use xlink:href="#arrow-right-circle" />
</svg>
{{challenge_solution}}
</p>
</div>
</div>
</div>
<hr class="mb-5">
<div class="row g-5">
<div class="col-md-6">
<h2 class="text-body-emphasis">Contestants</h2>
<p>
There are cuttently {{ contestants_amount }} contestants.
These contestants currently have had at least one connection to
the challenge:
</p>
<ul class="list-unstyled ps-0">
{% for contestant in contestants %}
<li>
<a class="icon-link mb-1" href="https://whatismyipaddress.com/ip/{{ contestant.ip }}">
<svg class="bi" width="16" height="16">
<use xlink:href="#arrow-right-circle" />
</svg>
{{ contestant.ip }}
</a>
</li>
{% endfor %}
</ul>
</div>
<div class="col-md-6">
<h2 class="text-body-emphasis">Winners</h2>
<p>
There are cuttently {{ winners_amount }} winners. These contestants currently have been sent the
secret:
</p>
<ul class="list-unstyled ps-0">
{% for winner in winners %}
<li>
<a class="icon-link mb-1" href="https://whatismyipaddress.com/ip/{{ winner.ip }}">
<svg class="bi" width="16" height="16">
<use xlink:href="#arrow-right-circle" />
</svg>
{{ winner.ip }}
</a>
</li>
{% endfor %}
</ul>
</div>
</div>
</main>
<footer class="pt-5 my-5 text-body-secondary border-top">
Created by {{ author }} &copy; {{year}}
</footer>
</div>
<script src="https://getbootstrap.com/docs/5.3/dist/js/bootstrap.bundle.min.js"
integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz"
crossorigin="anonymous"></script>
</body>
</html>

View File

@ -8,17 +8,27 @@ use warp::reply::Reply;
use warp::reply::Response; use warp::reply::Response;
use warp::Filter; use warp::Filter;
use crate::challenge::ChallengeDesc;
use crate::vault::VaultRef;
use super::errors::TemplateError; use super::errors::TemplateError;
use super::Service; use super::Service;
fn with_serv(
serv: Arc<Service>,
) -> impl Filter<Extract = (Arc<Service>,), Error = std::convert::Infallible> + Clone {
warp::any().map(move || serv.clone())
}
impl<'tp> Service<'tp> { impl<'tp> Service<'tp> {
pub fn admin_routes( pub fn admin_routes(
this: Arc<Self>, this: Arc<Self>,
) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone + 'tp { ) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone + 'tp {
let serv = this.clone(); let serv = this.clone();
warp::path::end() warp::path!("admin" / "challenge" / usize)
.map(move || serv.clone()) .map(move |id: usize| serv.challenges[id + 1].clone())
.and_then(overview) .and(with_serv(this.clone()))
.and_then(details)
.with(warp::trace(|info| { .with(warp::trace(|info| {
// Create a span using tracing macros // Create a span using tracing macros
tracing::info_span!( tracing::info_span!(
@ -27,33 +37,33 @@ impl<'tp> Service<'tp> {
path = %info.path(), path = %info.path(),
) )
})) }))
.or(warp::path!("admin").and(with_serv(this.clone()).and_then(index)))
} }
} }
async fn overview(serv: Arc<Service<'_>>) -> Result<Box<dyn warp::Reply>, warp::Rejection> { async fn details(
let challenge = serv.challenges[0].clone(); data: (ChallengeDesc, VaultRef),
let contestants = serv serv: Arc<Service<'_>>,
.vault ) -> Result<Box<dyn warp::Reply>, warp::Rejection> {
.contestants() let challenge = data.0;
.await let vault = data.1;
.into_iter() let contestants = vault.contestants().await.into_iter().collect::<Vec<_>>();
.collect::<Vec<_>>(); let winners = vault.winners().await.into_iter().collect::<Vec<_>>();
let winners = serv.vault.winners().await.into_iter().collect::<Vec<_>>();
let r = Response::new( let r = Response::new(
serv.env serv.env
.get_template("index") .get_template("admin:index")
.map_err(TemplateError::from)? .map_err(TemplateError::from)?
.render(context!( .render(context!(
title => "Wooly-Vault", title => "Wooly-Vault",
author => env!("CARGO_PKG_AUTHORS"), author => env!("CARGO_PKG_AUTHORS"),
year => "2024", year => "2024",
challenge_idx => serv.config.challenge, challenge_idx => challenge.id(),
challenge_title => challenge.title(), challenge_title => challenge.title(),
challenge_description => challenge.description(), challenge_description => challenge.description(),
challenge_hints => challenge.hints(), challenge_hints => challenge.hints(),
challenge_solution => challenge.solution(), challenge_solution => challenge.solution(),
contestants => serv.vault.contestants().await.iter().collect::<Vec<_>>(), contestants => vault.contestants().await.iter().collect::<Vec<_>>(),
winners => serv.vault.winners().await.iter().collect::<Vec<_>>(), winners => vault.winners().await.iter().collect::<Vec<_>>(),
contestants => contestants, contestants => contestants,
winners => winners, winners => winners,
contestants_amount => contestants.len(), contestants_amount => contestants.len(),
@ -65,3 +75,8 @@ async fn overview(serv: Arc<Service<'_>>) -> Result<Box<dyn warp::Reply>, warp::
Ok(Box::new(r)) Ok(Box::new(r))
} }
async fn index(serv: Arc<Service<'_>>) -> Result<Box<dyn warp::Reply>, warp::Rejection> {
let r = Response::new("todo".into());
Ok(Box::new(r))
}

View File

@ -18,6 +18,7 @@ use self::errors::handle_rejection;
pub mod admin; pub mod admin;
pub mod errors; pub mod errors;
pub mod user;
#[derive(Clone)] #[derive(Clone)]
pub struct Service<'tp> { pub struct Service<'tp> {
@ -28,7 +29,16 @@ pub struct Service<'tp> {
impl<'tp> Service<'tp> { impl<'tp> Service<'tp> {
fn build(config: Config, challenges: Vec<(ChallengeDesc, VaultRef)>) -> Result<Arc<Self>> { fn build(config: Config, challenges: Vec<(ChallengeDesc, VaultRef)>) -> Result<Arc<Self>> {
let mut env = Environment::new(); let mut env = Environment::new();
env.add_template("index", include_str!("../../data/www/admin.html"))?; env.add_template(
"admin:index",
include_str!("../../data/www/admin/index.html"),
)?;
env.add_template(
"admin:details",
include_str!("../../data/www/admin/details.html"),
)?;
env.add_template("index", include_str!("../../data/www/index.html"))?;
env.add_template("details", include_str!("../../data/www/details.html"))?;
Ok(Self { Ok(Self {
config, config,
env, env,
@ -43,6 +53,7 @@ pub async fn serve(challenges: Vec<(ChallengeDesc, VaultRef)>, config: Config) -
let routes = Service::admin_routes(service.clone()) let routes = Service::admin_routes(service.clone())
.or(Service::ressources_routes()) .or(Service::ressources_routes())
.or(Service::user_routes(service.clone()))
.recover(handle_rejection) .recover(handle_rejection)
.with(warp::trace(|info| { .with(warp::trace(|info| {
// Create a span using tracing macros // Create a span using tracing macros

82
src/meta/user.rs Normal file
View File

@ -0,0 +1,82 @@
use std::sync::Arc;
use anyhow::Result;
use libpt::log::tracing;
use minijinja::context;
use warp::reject::Rejection;
use warp::reply::Reply;
use warp::reply::Response;
use warp::Filter;
use crate::challenge::ChallengeDesc;
use crate::vault::VaultRef;
use super::errors::TemplateError;
use super::Service;
fn with_serv(
serv: Arc<Service>,
) -> impl Filter<Extract = (Arc<Service>,), Error = std::convert::Infallible> + Clone {
warp::any().map(move || serv.clone())
}
impl<'tp> Service<'tp> {
pub fn user_routes(
this: Arc<Self>,
) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone + 'tp {
let serv = this.clone();
warp::path!("challenge" / usize)
.map(move |id: usize| serv.challenges[id + 1].clone())
.and(with_serv(this.clone()))
.and_then(details)
.with(warp::trace(|info| {
// Create a span using tracing macros
tracing::info_span!(
"admin",
method = %info.method(),
path = %info.path(),
)
}))
.or(warp::path::end().and(with_serv(this.clone()).and_then(index)))
}
}
async fn details(
data: (ChallengeDesc, VaultRef),
serv: Arc<Service<'_>>,
) -> Result<Box<dyn warp::Reply>, warp::Rejection> {
let challenge = data.0;
let vault = data.1;
let contestants = vault.contestants().await.into_iter().collect::<Vec<_>>();
let winners = vault.winners().await.into_iter().collect::<Vec<_>>();
let r = Response::new(
serv.env
.get_template("index")
.map_err(TemplateError::from)?
.render(context!(
title => "Wooly-Vault",
author => env!("CARGO_PKG_AUTHORS"),
year => "2024",
challenge_idx => challenge.id(),
challenge_title => challenge.title(),
challenge_description => challenge.description(),
challenge_hints => challenge.hints(),
// challenge_solution => challenge.solution(),
contestants => vault.contestants().await.iter().collect::<Vec<_>>(),
winners => vault.winners().await.iter().collect::<Vec<_>>(),
contestants => contestants,
winners => winners,
contestants_amount => contestants.len(),
winners_amount => winners.len(),
))
.map_err(TemplateError::from)?
.into(),
);
Ok(Box::new(r))
}
async fn index(serv: Arc<Service<'_>>) -> Result<Box<dyn warp::Reply>, warp::Rejection> {
let r = Response::new("todo".into());
Ok(Box::new(r))
}