johnandersen777.bsky.social/knotless
git that lives in your Atmosphere account
Forked from chadtmiller.com/knotless
0
0
//! Invites to private repositories, left for their runners: nothing
//! written in a space reaches the firehose, so an owner says here that a
//! repository names a runner, and the runner asks what it was told
//! ([`knotless::invites`]). Each signs for itself with a service token. An
//! invite is only a hint, which anyone can leave: the runner reads the
//! repository's record, as a member of its space, before running anything.
use crate::gateway::caller;
use crate::moderation::{bad, index};
use crate::{App, AppError};
use axum::Json;
use axum::extract::{Query, State};
use axum::http::HeaderMap;
use knotless::invites::{INVITE, LIST};
use serde::Deserialize;
use serde_json::{Value, json};
use std::sync::Arc;
#[derive(Deserialize)]
pub struct Invite {
runner: String,
name: String,
}
pub async fn invite(
State(app): State<Arc<App>>,
headers: HeaderMap,
Json(body): Json<Invite>,
) -> Result<Json<Value>, AppError> {
let index = index(&app)?;
let did = caller(&app, &headers, INVITE).await?;
if !body.runner.starts_with("did:") {
return Err(bad("a runner is named by its DID"));
}
if !knotless::keys::valid_name(&body.name) {
return Err(bad("a repository's name is letters, digits and ._~-"));
}
let left = index.invite(&body.runner, &did, &body.name);
left.map_err(anyhow::Error::from)
.map_err(AppError::internal)?;
Ok(Json(json!({})))
}
#[derive(Deserialize)]
pub struct ListParams {
limit: Option<usize>,
cursor: Option<String>,
}
pub async fn list(
State(app): State<Arc<App>>,
headers: HeaderMap,
Query(p): Query<ListParams>,
) -> Result<Json<Value>, AppError> {
let index = index(&app)?;
let runner = caller(&app, &headers, LIST).await?;
let limit = p.limit.unwrap_or(100).clamp(1, 500);
let after = match p.cursor.as_deref().map(str::parse::<i64>) {
None => 0,
Some(Ok(id)) => id,
Some(Err(_)) => return Err(bad("a cursor is one this method gave")),
};
let page = index
.invites(&runner, after, limit)
.map_err(anyhow::Error::from)
.map_err(AppError::internal)?;
let last = page.last().map_or(after, |(id, _)| *id);
let invites: Vec<Value> = page.into_iter().map(|(_, invite)| invite).collect();
Ok(Json(
json!({ "invites": invites, "cursor": last.to_string() }),
))
}