parent
8534407f14
commit
1d141740d5
@ -1,2 +1 @@
|
||||
# ADMIN_KEY=poop
|
||||
# DATABASE_URL=postgres://localhost/mango
|
||||
|
@ -1,67 +0,0 @@
|
||||
use crate::fairings::prelude::*;
|
||||
|
||||
#[rocket::post("/login", data = "<key>")]
|
||||
pub fn login(jar: &CookieJar<'_>, key: String) {
|
||||
jar.add(Cookie::new("nothing", key));
|
||||
}
|
||||
|
||||
#[derive(FromForm)]
|
||||
pub struct HighlightProject {
|
||||
id: String,
|
||||
highlight: String,
|
||||
}
|
||||
|
||||
#[rocket::post("/highlights", data = "<data>")]
|
||||
pub async fn highlight_project(_access: AdminAccess, db: Db, data: Form<Strict<HighlightProject>>) {
|
||||
let highlight = if &data.highlight == "true" {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
db.run(move |conn| {
|
||||
let id = schema::highlights::id.eq(&data.id);
|
||||
|
||||
if highlight {
|
||||
diesel::insert_into(schema::highlights::table)
|
||||
.values(id)
|
||||
.execute(conn)
|
||||
} else {
|
||||
diesel::delete(schema::highlights::table.filter(id))
|
||||
.execute(conn)
|
||||
}
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[rocket::post("/projects", data = "<data>")]
|
||||
pub async fn upsert_project(_access: AdminAccess, db: Db, data: Form<Strict<model::Project>>) {
|
||||
db.run(move |conn| {
|
||||
diesel::insert_into(schema::projects::table)
|
||||
.values(&**data)
|
||||
.on_conflict(schema::projects::id)
|
||||
.do_update()
|
||||
.set(&**data)
|
||||
.execute(conn)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[rocket::post("/config/<name>", data = "<data>")]
|
||||
pub async fn upsert_config(_access: AdminAccess, db: Db, name: String, data: Json<serde_json::Value>) {
|
||||
db.run(move |conn| {
|
||||
diesel::insert_into(schema::configs::table)
|
||||
.values(model::PageConfig {
|
||||
name: name.clone(),
|
||||
data: data.0.clone(),
|
||||
})
|
||||
.on_conflict(schema::configs::name)
|
||||
.do_update()
|
||||
.set(model::PageConfig { name, data: data.0 })
|
||||
.execute(conn)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
use crate::fairings::prelude::*;
|
||||
|
||||
#[rocket::get("/")]
|
||||
pub async fn index(_access: AdminAccess, db: Db) -> Template {
|
||||
let projects = db
|
||||
.run(|conn| schema::projects::table.load::<model::Project>(conn))
|
||||
.await
|
||||
.expect("failed to load projects");
|
||||
let configs: Vec<_> = db
|
||||
.run(|conn| schema::configs::table.load::<model::PageConfig>(conn))
|
||||
.await
|
||||
.expect("failed to load configs")
|
||||
.into_iter()
|
||||
.map(|it| json!({ "name": it.name, "data": serde_json::to_string_pretty(&it.data).unwrap() }))
|
||||
.collect();
|
||||
|
||||
Template::render(
|
||||
"admin/index",
|
||||
json!({ "projects": projects, "configs": configs }),
|
||||
)
|
||||
}
|
||||
|
||||
#[rocket::get("/ip-log?<page>&<search>")]
|
||||
pub async fn ip_log(
|
||||
_access: AdminAccess,
|
||||
db: Db,
|
||||
page: Option<u32>,
|
||||
search: Option<std::net::IpAddr>,
|
||||
) -> Template {
|
||||
let logs = db
|
||||
.run(move |conn| {
|
||||
let base = schema::ip_log::table
|
||||
.order(schema::ip_log::timestamp.desc())
|
||||
.offset(page.unwrap_or(0) as i64 * 50)
|
||||
.limit(50);
|
||||
|
||||
if let Some(x) = search {
|
||||
base.filter(schema::ip_log::addr.eq(ipnetwork::IpNetwork::from(x)))
|
||||
.load::<model::IpLogEntry>(conn)
|
||||
} else {
|
||||
base.load::<model::IpLogEntry>(conn)
|
||||
}
|
||||
})
|
||||
.await
|
||||
.expect("failed to load ip log");
|
||||
|
||||
Template::render(
|
||||
"admin/ip-log",
|
||||
json!({ "entries": logs, "nextPage": page.unwrap_or(0) + 1, "search": search }),
|
||||
)
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
use super::prelude::*;
|
||||
|
||||
mod api;
|
||||
mod frontend;
|
||||
|
||||
#[rocket::catch(401)]
|
||||
fn teapot() -> (Status, &'static str) {
|
||||
(Status { code: 418 }, "🫖")
|
||||
}
|
||||
|
||||
pub fn fairing() -> impl Fairing {
|
||||
AdHoc::on_ignite("Admin Frontend", |rocket| async {
|
||||
rocket
|
||||
.register("/admin", rocket::catchers![teapot])
|
||||
.mount(
|
||||
"/admin",
|
||||
rocket::routes![api::login, frontend::index, frontend::ip_log],
|
||||
)
|
||||
.mount(
|
||||
"/admin/api",
|
||||
rocket::routes![
|
||||
api::highlight_project,
|
||||
api::upsert_project,
|
||||
api::upsert_config
|
||||
],
|
||||
)
|
||||
})
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
use super::prelude::*;
|
||||
|
||||
pub struct AdminAccess;
|
||||
|
||||
#[async_trait]
|
||||
impl<'r> FromRequest<'r> for AdminAccess {
|
||||
type Error = ();
|
||||
|
||||
async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> {
|
||||
let cookie = request
|
||||
.cookies()
|
||||
.get("nothing")
|
||||
.map(|x| String::from(x.value()));
|
||||
|
||||
if let Ok(key) = std::env::var("ADMIN_KEY") {
|
||||
if cookie == Some(key) {
|
||||
Outcome::Success(AdminAccess)
|
||||
} else {
|
||||
Outcome::Failure((Status::Unauthorized, ()))
|
||||
}
|
||||
} else {
|
||||
Outcome::Failure((Status::Unauthorized, ()))
|
||||
}
|
||||
}
|
||||
}
|
@ -1,9 +1,7 @@
|
||||
mod prelude;
|
||||
|
||||
mod admin;
|
||||
mod db;
|
||||
mod page_configs;
|
||||
|
||||
pub use admin::*;
|
||||
pub use db::*;
|
||||
pub use page_configs::*;
|
||||
|
@ -1,2 +0,0 @@
|
||||
User-agent: *
|
||||
Disallow: /admin/
|
@ -1,75 +0,0 @@
|
||||
<html>
|
||||
good morning
|
||||
|
||||
<script>
|
||||
window.onload = () => {
|
||||
document.getElementById("highlight-project").onsubmit = async e => {
|
||||
e.preventDefault();
|
||||
await fetch("admin/api/highlights", { method: "post", body: new FormData(e.target) });
|
||||
window.location = window.location;
|
||||
};
|
||||
|
||||
document.getElementById("upsert-project").onsubmit = async e => {
|
||||
e.preventDefault();
|
||||
await fetch("admin/api/projects", { method: "post", body: new FormData(e.target) });
|
||||
window.location = window.location;
|
||||
};
|
||||
|
||||
document.getElementById("upsert-config").onsubmit = async e => {
|
||||
e.preventDefault();
|
||||
const name = document.getElementById("upsert-config-name").value;
|
||||
const data = document.getElementById("upsert-config-data").value;
|
||||
await fetch(`admin/api/config/${name}`, { method: "post", body: data });
|
||||
window.location = window.location;
|
||||
};
|
||||
};
|
||||
</script>
|
||||
|
||||
<body>
|
||||
<a href="/admin/ip-log?page=0">ip log</a>
|
||||
|
||||
<h1>page configs</h1>
|
||||
<h2>upsert</h2>
|
||||
<form id="upsert-config" autocomplete="off">
|
||||
<label>name</label>
|
||||
<input id="upsert-config-name" name="name">
|
||||
<label>data</label>
|
||||
<input id="upsert-config-data" name="data">
|
||||
<input type="submit" value="send">
|
||||
</form>
|
||||
|
||||
{{#each configs}}
|
||||
<h2>{{name}}</h2>
|
||||
<code>{{data}}</code>
|
||||
{{/each}}
|
||||
|
||||
<h1>projects</h1>
|
||||
<h2>highlight</h2>
|
||||
<form id="highlight-project" autocomplete="off">
|
||||
<label>id</label>
|
||||
<input name="id">
|
||||
<label>highlight ("true" | _)</label>
|
||||
<input name="highlight">
|
||||
<input type="submit" value="send">
|
||||
</form>
|
||||
|
||||
<h2>upsert</h2>
|
||||
<form id="upsert-project" autocomplete="off">
|
||||
<label>id</label>
|
||||
<input name="id">
|
||||
<label>display name</label>
|
||||
<input name="name">
|
||||
<label>desc</label>
|
||||
<input name="description">
|
||||
<label>image</label>
|
||||
<input name="image">
|
||||
<input type="submit" value="send">
|
||||
</form>
|
||||
{{#each projects}}
|
||||
<article>
|
||||
<h2>{{name}} (id {{id}})</h2>
|
||||
<span>{{description}}</span>
|
||||
</article>
|
||||
{{/each}}
|
||||
</body>
|
||||
</html>
|
@ -1,24 +0,0 @@
|
||||
<html>
|
||||
<a href="?page={{nextPage}}&search={{search}}">next page</a>
|
||||
<div>
|
||||
<form>
|
||||
<label>ip</label>
|
||||
<input name="search">
|
||||
<input type="submit" value="search"/>
|
||||
</form>
|
||||
</div>
|
||||
<table>
|
||||
<tr>
|
||||
<th>addr</th>
|
||||
<th>timestamp</th>
|
||||
<th>path</th>
|
||||
</tr>
|
||||
{{#each entries}}
|
||||
<tr>
|
||||
<td>{{addr}}</td>
|
||||
<td>{{timestamp}}</td>
|
||||
<td>{{path}}</td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
</table>
|
||||
</html>
|
Loading…
Reference in new issue