refactor: split templates further for reusability
cargo devel CI / cargo CI (push) Has been cancelled Details

This commit is contained in:
Christoph J. Scherr 2024-09-08 14:19:58 +02:00
parent 0db78399bd
commit 9437eb5a54
11 changed files with 224 additions and 122 deletions

13
data/www/admin/base.html Normal file
View File

@ -0,0 +1,13 @@
{% extends "base" %}
{% block title %}{{ title }}{% endblock %}
{% block main %}
<h1 class="text-body-emphasis">{{title}}</h1>
<p class="fs-5 col-md-8">
You have reached the {{title}}. 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">
{% block content %}{% endblock content %}
{% endblock %}

View File

@ -1,5 +1,5 @@
{% extends "base" %} {% extends "admin:base" %}
{% block main %} {% block content %}
<h1 class="text-body-emphasis">{{title}}</h1> <h1 class="text-body-emphasis">{{title}}</h1>
<p class="fs-5 col-md-8"> <p class="fs-5 col-md-8">
You have reached the {{title}}. This site can be used by You have reached the {{title}}. This site can be used by
@ -11,8 +11,8 @@
<hr class="mb-5"> <hr class="mb-5">
<div class="row g-5 mb-5"> <div class="row g-5 mb-5">
<h2 class="text-body-emphasis">Challenge {{ challenge_idx}} &mdash; {{ challenge_title }}</h2> <h2 class="text-body-emphasis">Challenge {{ challenge.id }} &mdash; {{ challenge.title }}</h2>
<p class="mt-1 mb-3">{{ challenge_description }}</p> <p class="mt-1 mb-3">{{ challenge.description }}</p>
<div class="col mt-1"> <div class="col mt-1">
<h3>Hints</h3> <h3>Hints</h3>
<button class="btn btn-primary my-2" type="button" data-bs-toggle="collapse" data-bs-target="#hints" <button class="btn btn-primary my-2" type="button" data-bs-toggle="collapse" data-bs-target="#hints"
@ -21,7 +21,7 @@
</button> </button>
<div class="collapse" id="hints"> <div class="collapse" id="hints">
<ul class="list-unstyled ps-0"> <ul class="list-unstyled ps-0">
{% for hint in challenge_hints %} {% for hint in challenge.hints %}
<li> <li>
<p> <p>
<svg class="bi" width="16" height="16"> <svg class="bi" width="16" height="16">
@ -45,53 +45,11 @@
<svg class="bi" width="16" height="16"> <svg class="bi" width="16" height="16">
<use xlink:href="#arrow-right-circle" /> <use xlink:href="#arrow-right-circle" />
</svg> </svg>
{{challenge_solution}} {{challenge.solution}}
</p> </p>
</div> </div>
</div> </div>
</div> </div>
<hr class="mb-5"> <hr class="mb-5">
{% include "comp:contestants_winners" %}
<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>
{% endblock %} {% endblock %}

View File

@ -1,14 +1,5 @@
{% extends "base" %} {% extends "admin:base" %}
{% block main %} {% block content %}
<h1 class="text-body-emphasis">{{title}}</h1>
<p class="fs-5 col-md-8">
You have reached the {{title}}. 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"> <div class="row g-5">
<div class="col-md-6"> <div class="col-md-6">
@ -25,6 +16,7 @@
</svg> </svg>
{{ challenge.id }} &mdash; {{ challenge.title }} {{ challenge.id }} &mdash; {{ challenge.title }}
</a> </a>
&rArr; {{ challenge.addr }}
</li> </li>
{% endfor %} {% endfor %}
</ul> </ul>

View File

@ -44,11 +44,21 @@
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" /> 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>
<symbol id="vault" viewBox="0 0 16 16"> <symbol id="vault" viewBox="0 0 16 16">
<title>Bootstrap</title> <title>{{app_name}}</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="M9.778 9.414A2 2 0 1 1 6.95 6.586a2 2 0 0 1 2.828 2.828" />
<path <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" /> 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> </symbol>
<symbol id="admin" viewBox="0 0 16 16">
<path
d="M5.338 1.59a61 61 0 0 0-2.837.856.48.48 0 0 0-.328.39c-.554 4.157.726 7.19 2.253 9.188a10.7 10.7 0 0 0 2.287 2.233c.346.244.652.42.893.533q.18.085.293.118a1 1 0 0 0 .101.025 1 1 0 0 0 .1-.025q.114-.034.294-.118c.24-.113.547-.29.893-.533a10.7 10.7 0 0 0 2.287-2.233c1.527-1.997 2.807-5.031 2.253-9.188a.48.48 0 0 0-.328-.39c-.651-.213-1.75-.56-2.837-.855C9.552 1.29 8.531 1.067 8 1.067c-.53 0-1.552.223-2.662.524zM5.072.56C6.157.265 7.31 0 8 0s1.843.265 2.928.56c1.11.3 2.229.655 2.887.87a1.54 1.54 0 0 1 1.044 1.262c.596 4.477-.787 7.795-2.465 9.99a11.8 11.8 0 0 1-2.517 2.453 7 7 0 0 1-1.048.625c-.28.132-.581.24-.829.24s-.548-.108-.829-.24a7 7 0 0 1-1.048-.625 11.8 11.8 0 0 1-2.517-2.453C1.928 10.487.545 7.169 1.141 2.692A1.54 1.54 0 0 1 2.185 1.43 63 63 0 0 1 5.072.56" />
<path d="M5.5 7a.5.5 0 0 1 .5-.5h4a.5.5 0 0 1 0 1H6a.5.5 0 0 1-.5-.5" />
</symbol>
<symbol id="user" viewBox="0 0 16 16">
<path
d="M15 14s1 0 1-1-1-4-5-4-5 3-5 4 1 1 1 1zm-7.978-1L7 12.996c.001-.264.167-1.03.76-1.72C8.312 10.629 9.282 10 11 10c1.717 0 2.687.63 3.24 1.276.593.69.758 1.457.76 1.72l-.008.002-.014.002zM11 7a2 2 0 1 0 0-4 2 2 0 0 0 0 4m3-2a3 3 0 1 1-6 0 3 3 0 0 1 6 0M6.936 9.28a6 6 0 0 0-1.23-.247A7 7 0 0 0 5 9c-4 0-5 3-5 4q0 1 1 1h4.216A2.24 2.24 0 0 1 5 13c0-1.01.377-2.042 1.09-2.904.243-.294.526-.569.846-.816M4.92 10A5.5 5.5 0 0 0 4 13H1c0-.26.164-1.03.76-1.724.545-.636 1.492-1.256 3.16-1.275ZM1.5 5.5a3 3 0 1 1 6 0 3 3 0 0 1-6 0m3-2a2 2 0 1 0 0 4 2 2 0 0 0 0-4" />
</symbol>
</svg> </svg>
<!-- theme switcher --> <!-- theme switcher -->
@ -100,24 +110,86 @@
</ul> </ul>
</div> </div>
<!-- basically everything but with some margin --> <div class="container-fluid">
<div class="col-lg-8 mx-auto p-4 py-md-5"> <div class="row">
<header class="d-flex align-items-center pb-3 mb-5 border-bottom"> <!-- side panel -->
<a href="/" class="d-flex align-items-center text-body-emphasis text-decoration-none"> <div class="d-flex flex-column flex-shrink-0 p-3 border-end" style="width: 280px;">
<svg class="bi me-2" width="40" height="32"> <a href="/"
<use xlink:href="#vault" /> class="d-flex align-items-center mb-3 mb-md-0 me-md-auto text-decoration-none text-body-emphasis">
</svg> <svg class="bi pe-none me-2" width="40" height="32">
<span class="fs-4">{{title}}</span> <use xlink:href="#vault"></use>
</a> </svg>
</header> <span class="fs-4">{{app_name}}</span>
</a>
<hr>
<ul class="nav nav-pills flex-column mb-auto">
<li class="nav-item">
<a href="/" class="nav-link text-body-emphasis active" aria-current="page">
<svg class="bi pe-none me-2" width="16" height="16">
<use xlink:href="#home"></use>
</svg>
Home
</a>
</li>
<li class="border-top my-3"></li>
<li>
<a href="/" class="nav-link text-body-emphasis">
<svg class="bi pe-none me-2" width="16" height="16">
<use xlink:href="#user"></use>
</svg>
User Interface
</a>
</li>
<li>
<a href="/admin" class="nav-link text-body-emphasis">
<svg class="bi pe-none me-2" width="16" height="16">
<use xlink:href="#admin"></use>
</svg>
Admin Interface
</a>
</li>
</ul>
<hr>
<div class="dropdown">
<a href="#"
class="d-flex align-items-center text-decoration-none dropdown-toggle text-body-emphasis"
data-bs-toggle="dropdown" aria-expanded="false">
<img src="https://github.com/mdo.png" alt="" width="32" height="32" class="rounded-circle me-2">
<strong>anon</strong>
</a>
<ul class="dropdown-menu dropdown-menu-dark text-small shadow">
<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="#">Sign out</a></li>
</ul>
</div>
</div>
<main> <!-- basically everything but with some margin -->
{% block main %}{% endblock %} <div class="col-lg-8 mx-auto p-4 py-md-5">
</main> <header class="d-flex align-items-center pb-3 mb-5 border-bottom">
<footer class="pt-5 my-5 text-body-secondary border-top"> <a href="/" class="d-flex align-items-center text-body-emphasis text-decoration-none">
Created by {{ author }} &copy; {{year}} <svg class="bi me-2" width="40" height="32">
</footer> <use xlink:href="#vault" />
</svg>
<span class="fs-4">{{app_name}}</span>
</a>
</header>
<main>
{% block main %}{% endblock %}
</main>
<footer class="pt-5 my-5 text-body-secondary border-top">
Created by {{ author }} &copy; {{year}}
</footer>
</div>
</div>
</div> </div>
<script src="https://getbootstrap.com/docs/5.3/dist/js/bootstrap.bundle.min.js" <script src="https://getbootstrap.com/docs/5.3/dist/js/bootstrap.bundle.min.js"
integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz"
crossorigin="anonymous"></script> crossorigin="anonymous"></script>

View File

@ -0,0 +1,41 @@
<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>

12
data/www/user/base.html Normal file
View File

@ -0,0 +1,12 @@
{% extends "base" %}
{% block title %}{{ title }}{% endblock %}
{% block main %}
<h1 class="text-body-emphasis">{{title}}</h1>
<p class="fs-5 col-md-8">
You have reached the {{title}}. This site can be used by the
contestants see the challenges and their progress and hints for that
challenge. This site is <b>NOT</b> part of the challenge.
</p>
<hr class="mb-5">
{% block content %}{% endblock content %}
{% endblock %}

View File

@ -1,6 +1,6 @@
{% extends "base" %} {% extends "user:base" %}
{% block title %}{{ title }}{% endblock %} {% block title %}{{ title }}{% endblock %}
{% block main %} {% block content %}
<h1 class="text-body-emphasis">{{title}} User Interface</h1> <h1 class="text-body-emphasis">{{title}} User Interface</h1>
<p class="fs-5 col-md-8"> <p class="fs-5 col-md-8">
You have reached the {{title}} User You have reached the {{title}} User
@ -13,8 +13,8 @@
<hr class="mb-5"> <hr class="mb-5">
<div class="row g-5 mb-5"> <div class="row g-5 mb-5">
<h2 class="text-body-emphasis">Challenge {{ challenge_idx}} &mdash; {{ challenge_title }}</h2> <h2 class="text-body-emphasis">Challenge {{ challenge.idx}} &mdash; {{ challenge.title }}</h2>
<p class="mt-1 mb-3">{{ challenge_description }}</p> <p class="mt-1 mb-3">{{ challenge.description }}</p>
<div class="col mt-1"> <div class="col mt-1">
<h3>Hints</h3> <h3>Hints</h3>
<button class="btn btn-primary my-2" type="button" data-bs-toggle="collapse" data-bs-target="#hints" <button class="btn btn-primary my-2" type="button" data-bs-toggle="collapse" data-bs-target="#hints"
@ -23,7 +23,7 @@
</button> </button>
<div class="collapse" id="hints"> <div class="collapse" id="hints">
<ul class="list-unstyled ps-0"> <ul class="list-unstyled ps-0">
{% for hint in challenge_hints %} {% for hint in challenge.hints %}
<li> <li>
<p> <p>
<svg class="bi" width="16" height="16"> <svg class="bi" width="16" height="16">

View File

@ -1,15 +1,6 @@
{% extends "base" %} {% extends "user:base" %}
{% block title %}{{ title }}{% endblock %} {% block title %}{{ title }}{% endblock %}
{% block main %} {% block content %}
<h1 class="text-body-emphasis">{{title}} Admin Interface</h1>
<p class="fs-5 col-md-8">
You have reached the {{title}} User
Interface. This site can be used by the
contestants see the challenges and their
progress 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"> <div class="row g-5">
<div class="col-md-6"> <div class="col-md-6">
<h2 class="text-body-emphasis">Challenges</h2> <h2 class="text-body-emphasis">Challenges</h2>
@ -23,8 +14,9 @@
<svg class="bi" width="16" height="16"> <svg class="bi" width="16" height="16">
<use xlink:href="#arrow-right-circle" /> <use xlink:href="#arrow-right-circle" />
</svg> </svg>
{{ challenge.id }} &mdash; {{ challenge.title }} &rArr; {{ challenge.addr }} {{ challenge.id }} &mdash; {{ challenge.title }}
</a> </a>
&rArr; {{ challenge.addr }}
</li> </li>
{% endfor %} {% endfor %}
</ul> </ul>

View File

@ -11,6 +11,7 @@ use warp::Filter;
use crate::challenge::ChallengeDesc; use crate::challenge::ChallengeDesc;
use crate::vault::VaultRef; use crate::vault::VaultRef;
use super::default_context;
use super::errors::TemplateError; use super::errors::TemplateError;
use super::Service; use super::Service;
@ -54,20 +55,17 @@ async fn details(
.get_template("admin:details") .get_template("admin:details")
.map_err(TemplateError::from)? .map_err(TemplateError::from)?
.render(context!( .render(context!(
..default_context(),
..context!(
challenge => challenge,
title => "Wooly-Vault Admin Interface", title => "Wooly-Vault Admin Interface",
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<_>>(), contestants => vault.contestants().await.iter().collect::<Vec<_>>(),
winners => 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(),
winners_amount => winners.len(), winners_amount => winners.len(),
)
)) ))
.map_err(TemplateError::from)? .map_err(TemplateError::from)?
.into(), .into(),
@ -87,11 +85,12 @@ async fn index(serv: Arc<Service<'_>>) -> Result<Box<dyn warp::Reply>, warp::Rej
.get_template("admin:index") .get_template("admin:index")
.map_err(TemplateError::from)? .map_err(TemplateError::from)?
.render(context!( .render(context!(
title => "Wooly-Vault Admin Interface", ..default_context(),
author => env!("CARGO_PKG_AUTHORS"), ..context!(
year => "2024", title => "Wooly-Vault Admin Interface",
challenges => challenges, challenges => challenges,
challenges_amount => challenges.len(), challenges_amount => challenges.len(),
)
)) ))
.map_err(TemplateError::from)? .map_err(TemplateError::from)?
.into(), .into(),

View File

@ -1,8 +1,12 @@
use std::fs::read_to_string;
use std::path::Path;
use std::sync::Arc; use std::sync::Arc;
use anyhow::anyhow;
use anyhow::Result; use anyhow::Result;
use libpt::log::tracing; use libpt::log::tracing;
use libpt::log::warn; use libpt::log::warn;
use minijinja::context;
use minijinja::Environment; use minijinja::Environment;
use warp::reject::Rejection; use warp::reject::Rejection;
use warp::reply::Reply; use warp::reply::Reply;
@ -29,6 +33,13 @@ 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("base", include_str!("../../data/www/base.html"))?; env.add_template("base", include_str!("../../data/www/base.html"))?;
env.add_template(
"comp:contestants_winners",
include_str!("../../data/www/components/contestants_winners.html"),
)?;
env.add_template("admin:base", include_str!("../../data/www/admin/base.html"))?;
env.add_template( env.add_template(
"admin:index", "admin:index",
include_str!("../../data/www/admin/index.html"), include_str!("../../data/www/admin/index.html"),
@ -37,8 +48,13 @@ impl<'tp> Service<'tp> {
"admin:details", "admin:details",
include_str!("../../data/www/admin/details.html"), 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"))?; env.add_template("user:base", include_str!("../../data/www/user/base.html"))?;
env.add_template("user:index", include_str!("../../data/www/user/index.html"))?;
env.add_template(
"user:details",
include_str!("../../data/www/user/details.html"),
)?;
Ok(Self { Ok(Self {
config, config,
env, env,
@ -82,3 +98,11 @@ async fn styles() -> Result<Box<dyn warp::Reply>, warp::Rejection> {
let r = Response::new(include_str!("../../data/www/styles.css").to_string().into()); let r = Response::new(include_str!("../../data/www/styles.css").to_string().into());
Ok(Box::new(r)) Ok(Box::new(r))
} }
pub fn default_context() -> minijinja::Value {
context!(
app_name => "Wooly-Vault",
author => env!("CARGO_PKG_AUTHORS"),
year => "2024",
)
}

View File

@ -11,6 +11,7 @@ use warp::Filter;
use crate::challenge::ChallengeDesc; use crate::challenge::ChallengeDesc;
use crate::vault::VaultRef; use crate::vault::VaultRef;
use super::default_context;
use super::errors::TemplateError; use super::errors::TemplateError;
use super::Service; use super::Service;
@ -51,23 +52,20 @@ async fn details(
let winners = vault.winners().await.into_iter().collect::<Vec<_>>(); let winners = vault.winners().await.into_iter().collect::<Vec<_>>();
let r = Response::new( let r = Response::new(
serv.env serv.env
.get_template("details") .get_template("user:details")
.map_err(TemplateError::from)? .map_err(TemplateError::from)?
.render(context!( .render(context!(
..default_context(),
..context!(
challenge => challenge,
title => "Wooly-Vault User Interface", title => "Wooly-Vault User Interface",
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<_>>(), contestants => vault.contestants().await.iter().collect::<Vec<_>>(),
winners => 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(),
winners_amount => winners.len(), winners_amount => winners.len(),
)
)) ))
.map_err(TemplateError::from)? .map_err(TemplateError::from)?
.into(), .into(),
@ -84,14 +82,15 @@ async fn index(serv: Arc<Service<'_>>) -> Result<Box<dyn warp::Reply>, warp::Rej
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let r = Response::new( let r = Response::new(
serv.env serv.env
.get_template("admin:index") .get_template("user:index")
.map_err(TemplateError::from)? .map_err(TemplateError::from)?
.render(context!( .render(context!(
title => "Wooly-Vault User Interface", ..default_context(),
author => env!("CARGO_PKG_AUTHORS"), ..context!(
year => "2024", title => "Wooly-Vault User Interface",
challenges => challenges, challenges => challenges,
challenges_amount => challenges.len(), challenges_amount => challenges.len(),
)
)) ))
.map_err(TemplateError::from)? .map_err(TemplateError::from)?
.into(), .into(),