mirror of
https://github.com/aljazceru/pubky-core.git
synced 2025-12-31 21:04:34 +01:00
feat(homeserver): list when path ends with '/' and some refactoring
This commit is contained in:
@@ -5,7 +5,7 @@ use std::{borrow::Cow, fmt::Result, time::SystemTime};
|
||||
|
||||
use heed::{
|
||||
types::{Bytes, Str},
|
||||
BoxedError, BytesDecode, BytesEncode, Database,
|
||||
BoxedError, BytesDecode, BytesEncode, Database, RoTxn,
|
||||
};
|
||||
|
||||
use pubky_common::{
|
||||
@@ -82,32 +82,31 @@ impl DB {
|
||||
Ok(deleted)
|
||||
}
|
||||
|
||||
pub fn contains_directory(&self, txn: &RoTxn, path: &str) -> anyhow::Result<bool> {
|
||||
Ok(self.tables.entries.get_greater_than(txn, path)?.is_some())
|
||||
}
|
||||
|
||||
/// Return a list of pubky urls.
|
||||
///
|
||||
/// - limit defaults to and capped by [MAX_LIST_LIMIT]
|
||||
pub fn list(
|
||||
&self,
|
||||
public_key: &PublicKey,
|
||||
prefix: &str,
|
||||
txn: &RoTxn,
|
||||
path: &str,
|
||||
reverse: bool,
|
||||
limit: Option<u16>,
|
||||
cursor: Option<String>,
|
||||
shallow: bool,
|
||||
) -> anyhow::Result<Vec<String>> {
|
||||
let db = self.tables.entries;
|
||||
let txn = self.env.read_txn()?;
|
||||
// Remove directories from the cursor;
|
||||
let cursor = cursor
|
||||
.as_deref()
|
||||
.and_then(|mut cursor| cursor.rsplit('/').next())
|
||||
.unwrap_or(if reverse { "~" } else { "" });
|
||||
|
||||
let cursor = format!("{path}{cursor}");
|
||||
let mut cursor = cursor.as_str();
|
||||
|
||||
let prefix = format!("{public_key}/{prefix}");
|
||||
// Normalized cursor
|
||||
let cursor = cursor.map(|mut cursor| {
|
||||
if cursor.starts_with("pubky://") {
|
||||
cursor = cursor[8..].into();
|
||||
}
|
||||
if cursor.starts_with(&prefix) {
|
||||
cursor
|
||||
} else {
|
||||
format!("{prefix}{cursor}")
|
||||
}
|
||||
});
|
||||
let limit = limit.unwrap_or(MAX_LIST_LIMIT).min(MAX_LIST_LIMIT);
|
||||
|
||||
// Vector to store results
|
||||
@@ -115,37 +114,19 @@ impl DB {
|
||||
|
||||
// Fetch data based on direction
|
||||
if reverse {
|
||||
if let Some(x) = &cursor {
|
||||
let mut cursor = cursor.unwrap_or(prefix.to_string());
|
||||
let mut cursor = cursor.as_str();
|
||||
|
||||
for _ in 0..limit {
|
||||
if let Some((key, _)) = self.tables.entries.get_lower_than(&txn, cursor)? {
|
||||
if !key.starts_with(&prefix) {
|
||||
break;
|
||||
}
|
||||
cursor = key;
|
||||
results.push(format!("pubky://{}", key))
|
||||
};
|
||||
}
|
||||
} else {
|
||||
// TODO: find a way to avoid this special case.
|
||||
|
||||
let mut iter = self.tables.entries.rev_prefix_iter(&txn, &prefix)?;
|
||||
|
||||
for _ in 0..limit {
|
||||
if let Some((key, _)) = iter.next().transpose()? {
|
||||
results.push(format!("pubky://{}", key))
|
||||
};
|
||||
}
|
||||
for _ in 0..limit {
|
||||
if let Some((key, _)) = self.tables.entries.get_lower_than(txn, cursor)? {
|
||||
if !key.starts_with(path) {
|
||||
break;
|
||||
}
|
||||
cursor = key;
|
||||
results.push(format!("pubky://{}", key))
|
||||
};
|
||||
}
|
||||
} else {
|
||||
let mut cursor = cursor.unwrap_or(prefix.to_string());
|
||||
let mut cursor = cursor.as_str();
|
||||
|
||||
for _ in 0..limit {
|
||||
if let Some((key, _)) = self.tables.entries.get_greater_than(&txn, cursor)? {
|
||||
if !key.starts_with(&prefix) {
|
||||
if let Some((key, _)) = self.tables.entries.get_greater_than(txn, cursor)? {
|
||||
if !key.starts_with(path) {
|
||||
break;
|
||||
}
|
||||
cursor = key;
|
||||
|
||||
@@ -82,14 +82,28 @@ pub async fn get(
|
||||
verify(path.as_str());
|
||||
let public_key = pubky.public_key();
|
||||
|
||||
if params.contains_key("list") {
|
||||
let path = path.as_str();
|
||||
|
||||
if path.ends_with('/') {
|
||||
let txn = state.db.env.read_txn()?;
|
||||
|
||||
let path = format!("{public_key}/{path}");
|
||||
|
||||
if !state.db.contains_directory(&txn, &path)? {
|
||||
return Err(Error::new(
|
||||
StatusCode::NOT_FOUND,
|
||||
"Directory Not Found".into(),
|
||||
));
|
||||
}
|
||||
|
||||
// Handle listing
|
||||
let vec = state.db.list(
|
||||
public_key,
|
||||
path.as_str(),
|
||||
&txn,
|
||||
&path,
|
||||
params.contains_key("reverse"),
|
||||
params.get("limit").and_then(|l| l.parse::<u16>().ok()),
|
||||
params.get("cursor").map(|cursor| cursor.into()),
|
||||
params.contains_key("shallow"),
|
||||
)?;
|
||||
|
||||
return Ok(Response::builder()
|
||||
@@ -101,10 +115,10 @@ pub async fn get(
|
||||
|
||||
// TODO: Enable streaming
|
||||
|
||||
match state.db.get_blob(public_key, path.as_str()) {
|
||||
match state.db.get_blob(public_key, path) {
|
||||
Err(error) => Err(error)?,
|
||||
Ok(Some(bytes)) => Ok(Response::builder().body(Body::from(bytes)).unwrap()),
|
||||
Ok(None) => Err(Error::with_status(StatusCode::NOT_FOUND)),
|
||||
Ok(None) => Err(Error::new(StatusCode::NOT_FOUND, "File Not Found".into())),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -52,8 +52,17 @@ impl<'a> ListBuilder<'a> {
|
||||
pub async fn send(self) -> Result<Vec<String>> {
|
||||
let mut url = self.client.pubky_to_http(self.url).await?;
|
||||
|
||||
if !url.path().ends_with('/') {
|
||||
let path = url.path().to_string();
|
||||
let mut parts = path.split('/').collect::<Vec<&str>>();
|
||||
parts.pop();
|
||||
|
||||
let path = format!("{}/", parts.join("/"));
|
||||
|
||||
url.set_path(&path)
|
||||
}
|
||||
|
||||
let mut query = url.query_pairs_mut();
|
||||
query.append_key_only("list");
|
||||
|
||||
if self.reverse {
|
||||
query.append_key_only("reverse");
|
||||
|
||||
@@ -236,7 +236,7 @@ mod tests {
|
||||
client.put(url.as_str(), &[0]).await.unwrap();
|
||||
}
|
||||
|
||||
let url = format!("pubky://{}/pub/example.com/", keypair.public_key());
|
||||
let url = format!("pubky://{}/pub/example.com/extra", keypair.public_key());
|
||||
|
||||
{
|
||||
let list = client.list(url.as_str()).unwrap().send().await.unwrap();
|
||||
|
||||
Reference in New Issue
Block a user