chadtmiller.com/knotless
Git hosted in your atproto account, on any stock PDS, fast enough for agents.
git clone knotless://chadtmiller.com/knotlessneeds the knotless git helper. set it up
//! What the appview answers from its index.
use crate::{App, AppError};
use axum::Json;
use axum::extract::{Query, State};
use axum::http::StatusCode;
use serde::Deserialize;
use serde_json::{Value, json};
use std::sync::Arc;
fn index(app: &App) -> Result<&crate::index::Index, AppError> {
app.index
.as_deref()
.ok_or_else(|| AppError(StatusCode::NOT_FOUND, "this server keeps no index".into()))
}
#[derive(Deserialize)]
pub struct ForgeParams {
repo: String,
key: String,
limit: Option<usize>,
cursor: Option<String>,
}
/// The pulls, issues, comments and state changes about a repository: the
/// one its owner made with `key`, whatever it has been named. A page at a
/// time, by URI.
pub async fn get_forge(
State(app): State<Arc<App>>,
Query(p): Query<ForgeParams>,
) -> Result<Json<Value>, AppError> {
let did = knotless::forge::parse_uri(&p.repo)
.map_err(|e| AppError(StatusCode::BAD_REQUEST, e.to_string()))?
.0;
let mut entries = index(&app)?
.forge(did, &p.key)
.map_err(anyhow::Error::from)
.map_err(AppError::internal)?;
entries.sort_by(|a, b| a.uri.cmp(&b.uri));
let after = p.cursor.unwrap_or_default();
let limit = p.limit.unwrap_or(500).clamp(1, 1000);
let mut page: Vec<_> = entries.into_iter().filter(|e| e.uri > after).collect();
let more = page.len() > limit;
page.truncate(limit);
let mut out = json!({ "entries": page });
if more {
out["cursor"] = page[limit - 1].uri.clone().into();
}
Ok(Json(out))
}
#[derive(Deserialize)]
pub struct ListParams {
q: Option<String>,
limit: Option<usize>,
cursor: Option<String>,
}
/// Repositories, newest first, matching `q` by name or description, a
/// page at a time.
pub async fn list_repos(
State(app): State<Arc<App>>,
Query(p): Query<ListParams>,
) -> Result<Json<Value>, AppError> {
let limit = p.limit.unwrap_or(50).clamp(1, 500);
let (repos, cursor) = index(&app)?
.repos_page(
p.q.as_deref().unwrap_or_default(),
limit,
p.cursor.as_deref(),
)
.map_err(anyhow::Error::from)
.map_err(AppError::internal)?;
let mut out = json!({ "repos": repos });
if let Some(cursor) = cursor {
out["cursor"] = cursor.into();
}
Ok(Json(out))
}