this repo has no description
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

admin panel stuff

+145 -1
+3 -1
.env.template
··· 9 9 # Set's the hostname for oauth 10 10 #OAUTH_HOST=advent.codes 11 11 12 - # Enable global day unlock via the settings table (for workshop use) 12 + # Enable global day unlock via the settings table 13 13 #GLOBAL_UNLOCK_ENABLED=true 14 + # Comma separated list of DIDs allowed to access the /admin page 15 + #ADMIN_DIDS=did:plc:example1,did:plc:example2 14 16 15 17 # Challenge account. The account that writes some records for challenges 16 18 CHALLENGE_PDS=https://skeetcentral.com
+93
web/src/handlers/admin.rs
··· 1 + use axum::extract::State; 2 + use axum::response::{IntoResponse, Redirect}; 3 + use axum::Form; 4 + use axum::http::StatusCode; 5 + use shared::advent::get_global_unlock_day; 6 + use sqlx::PgPool; 7 + 8 + use crate::session::AxumSessionStore; 9 + use crate::templates::HtmlTemplate; 10 + use crate::templates::admin::AdminTemplate; 11 + 12 + fn get_admin_allow_list() -> Vec<String> { 13 + std::env::var("ADMIN_DIDS") 14 + .unwrap_or_default() 15 + .split(',') 16 + .map(|s| s.trim().to_string()) 17 + .filter(|s| !s.is_empty()) 18 + .collect() 19 + } 20 + 21 + fn is_admin(did: Option<&String>) -> bool { 22 + let did = match did { 23 + Some(d) => d, 24 + None => return false, 25 + }; 26 + let allow_list = get_admin_allow_list(); 27 + allow_list.contains(did) 28 + } 29 + 30 + pub async fn admin_page_handler( 31 + State(pool): State<PgPool>, 32 + session: AxumSessionStore, 33 + ) -> impl IntoResponse { 34 + let did = session.get_did(); 35 + 36 + if !is_admin(did.as_ref()) { 37 + return (StatusCode::FORBIDDEN, "You are not authorized to access this page.").into_response(); 38 + } 39 + 40 + let current_day = get_global_unlock_day(&pool).await.unwrap_or(1); 41 + 42 + HtmlTemplate(AdminTemplate { 43 + title: "Admin - Global Unlock", 44 + current_unlock_day: current_day, 45 + is_logged_in: session.logged_in(), 46 + message: None, 47 + }) 48 + .into_response() 49 + } 50 + 51 + #[derive(Debug, serde::Deserialize)] 52 + pub struct AdminForm { 53 + pub action: String, 54 + } 55 + 56 + pub async fn admin_post_handler( 57 + State(pool): State<PgPool>, 58 + session: AxumSessionStore, 59 + Form(form): Form<AdminForm>, 60 + ) -> impl IntoResponse { 61 + let did = session.get_did(); 62 + 63 + if !is_admin(did.as_ref()) { 64 + return (StatusCode::FORBIDDEN, "You are not authorized to access this page.").into_response(); 65 + } 66 + 67 + let current_day = get_global_unlock_day(&pool).await.unwrap_or(1); 68 + 69 + let new_day: i32 = match form.action.as_str() { 70 + "up" => (current_day as i32 + 1).min(25), 71 + "down" => (current_day as i32 - 1).max(1), 72 + _ => current_day as i32, 73 + }; 74 + 75 + let result = sqlx::query("UPDATE settings SET unlocked_up_to_day = $1") 76 + .bind(new_day) 77 + .execute(&pool) 78 + .await; 79 + 80 + match result { 81 + Ok(_) => Redirect::to("/admin").into_response(), 82 + Err(e) => { 83 + log::error!("Failed to update global unlock day: {}", e); 84 + HtmlTemplate(AdminTemplate { 85 + title: "Admin - Global Unlock", 86 + current_unlock_day: current_day, 87 + is_logged_in: session.logged_in(), 88 + message: Some("Failed to update the unlock day.".to_string()), 89 + }) 90 + .into_response() 91 + } 92 + } 93 + }
+1
web/src/handlers/mod.rs
··· 1 + pub mod admin; 1 2 pub mod auth; 2 3 pub mod custom; 3 4 pub mod day;
+2
web/src/main.rs
··· 302 302 "/leaderboard", 303 303 get(handlers::leaderboard::leaderboard_handler), 304 304 ) 305 + .route("/admin", get(handlers::admin::admin_page_handler)) 306 + .route("/admin", post(handlers::admin::admin_post_handler)) 305 307 .route("/login", get(handlers::auth::login_page_handler)) 306 308 .route("/logout", get(handlers::auth::logout_handler)) 307 309 .route("/redirect/login", get(handlers::auth::login_handle))
+10
web/src/templates/admin.rs
··· 1 + use askama::Template; 2 + 3 + #[derive(Template)] 4 + #[template(path = "admin.askama.html")] 5 + pub struct AdminTemplate<'a> { 6 + pub title: &'a str, 7 + pub current_unlock_day: u8, 8 + pub is_logged_in: bool, 9 + pub message: Option<String>, 10 + }
+1
web/src/templates/mod.rs
··· 2 2 use axum::http::StatusCode; 3 3 use axum::response::{Html, IntoResponse, Response}; 4 4 5 + pub mod admin; 5 6 pub mod day; 6 7 pub mod error; 7 8 pub mod home;
+35
web/templates/admin.askama.html
··· 1 + {% extends "layout.askama.html" %} 2 + 3 + {% block content %} 4 + <div class="flex flex-col items-center justify-center min-h-[60vh]"> 5 + <div class="card w-full max-w-md shadow-2xl bg-base-100"> 6 + <div class="card-body items-center text-center"> 7 + <h2 class="card-title text-3xl mb-4">Global Unlock</h2> 8 + 9 + {% if let Some(msg) = message %} 10 + <div class="alert alert-error mb-4"> 11 + <span>{{ msg }}</span> 12 + </div> 13 + {% endif %} 14 + 15 + <p class="text-lg mb-2">Currently unlocked up to day:</p> 16 + <p class="text-6xl font-bold mb-6">{{ current_unlock_day }}</p> 17 + 18 + <div class="flex gap-4"> 19 + <form method="POST" action="/admin"> 20 + <input type="hidden" name="action" value="down"/> 21 + <button type="submit" class="btn btn-lg btn-outline" {% if current_unlock_day <= 1 %}disabled{% endif %}> 22 + - Day 23 + </button> 24 + </form> 25 + <form method="POST" action="/admin"> 26 + <input type="hidden" name="action" value="up"/> 27 + <button type="submit" class="btn btn-lg btn-primary" {% if current_unlock_day >= 25 %}disabled{% endif %}> 28 + + Day 29 + </button> 30 + </form> 31 + </div> 32 + </div> 33 + </div> 34 + </div> 35 + {% endblock %}