Compare commits
2 commits
df1babccd4
...
0352989083
Author | SHA1 | Date | |
---|---|---|---|
0352989083 | |||
f9c82e5601 |
14 changed files with 1849 additions and 5593 deletions
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
**static site management system**
|
**static site management system**
|
||||||
|
|
||||||
![CI status](https://ci.thetadev.de/api/badges/ThetaDev/Talon/status.svg)
|
[![CI status](https://ci.thetadev.de/api/badges/ThetaDev/Talon/status.svg)](https://ci.thetadev.de/ThetaDev/Talon)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
89
src/api.rs
89
src/api.rs
|
@ -211,58 +211,66 @@ impl TalonApi {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get version
|
/// Get version
|
||||||
#[oai(path = "/website/:subdomain/version/:id", method = "get")]
|
#[oai(path = "/website/:subdomain/version/:version", method = "get")]
|
||||||
async fn version_get(
|
async fn version_get(
|
||||||
&self,
|
&self,
|
||||||
talon: Data<&Talon>,
|
talon: Data<&Talon>,
|
||||||
subdomain: Path<String>,
|
subdomain: Path<String>,
|
||||||
id: Path<u32>,
|
version: Path<u32>,
|
||||||
) -> Result<Json<Version>> {
|
) -> Result<Json<Version>> {
|
||||||
talon
|
talon
|
||||||
.db
|
.db
|
||||||
.get_version(&subdomain, *id)
|
.get_version(&subdomain, *version)
|
||||||
.map(|v| Json(Version::from((*id, v))))
|
.map(|v| Json(Version::from((*version, v))))
|
||||||
|
.map_err(Error::from)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get version files
|
||||||
|
#[oai(path = "/website/:subdomain/version/:version/files", method = "get")]
|
||||||
|
async fn version_files(
|
||||||
|
&self,
|
||||||
|
talon: Data<&Talon>,
|
||||||
|
subdomain: Path<String>,
|
||||||
|
version: Path<u32>,
|
||||||
|
) -> Result<Json<Vec<String>>> {
|
||||||
|
talon.db.version_exists(&subdomain, *version)?;
|
||||||
|
talon
|
||||||
|
.db
|
||||||
|
.get_version_files(&subdomain, *version)
|
||||||
|
.map(|r| r.map(|f| f.0))
|
||||||
|
.collect::<Result<Vec<_>, _>>()
|
||||||
|
.map(Json)
|
||||||
.map_err(Error::from)
|
.map_err(Error::from)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Delete version
|
/// Delete version
|
||||||
#[oai(path = "/website/:subdomain/version/:id", method = "delete")]
|
#[oai(path = "/website/:subdomain/version/:version", method = "delete")]
|
||||||
async fn version_delete(
|
async fn version_delete(
|
||||||
&self,
|
&self,
|
||||||
auth: ApiKeyAuthorization,
|
auth: ApiKeyAuthorization,
|
||||||
talon: Data<&Talon>,
|
talon: Data<&Talon>,
|
||||||
subdomain: Path<String>,
|
subdomain: Path<String>,
|
||||||
id: Path<u32>,
|
version: Path<u32>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
auth.check_subdomain(&subdomain, Access::Modify)?;
|
auth.check_subdomain(&subdomain, Access::Modify)?;
|
||||||
|
|
||||||
talon.db.delete_version(&subdomain, *id, true)?;
|
talon.db.delete_version(&subdomain, *version, true)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Insert a new version into the database
|
||||||
fn insert_version(
|
fn insert_version(
|
||||||
talon: &Talon,
|
talon: &Talon,
|
||||||
subdomain: &str,
|
subdomain: &str,
|
||||||
id: u32,
|
|
||||||
fallback: Option<String>,
|
fallback: Option<String>,
|
||||||
spa: bool,
|
spa: bool,
|
||||||
mut version_data: BTreeMap<String, String>,
|
mut version_data: BTreeMap<String, String>,
|
||||||
) -> Result<()> {
|
) -> Result<u32> {
|
||||||
version_data.remove("fallback");
|
version_data.remove("fallback");
|
||||||
version_data.remove("spa");
|
version_data.remove("spa");
|
||||||
|
|
||||||
// Validata fallback path
|
let id = talon.db.insert_version(
|
||||||
if let Some(fallback) = &fallback {
|
|
||||||
if let Err(e) = talon.storage.get_file(id, fallback, &Default::default()) {
|
|
||||||
// Remove the uploaded files of the bad version
|
|
||||||
let _ = talon.db.delete_version(subdomain, id, false);
|
|
||||||
return Err(ApiError::InvalidFallback(e.to_string()).into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
talon.db.insert_version(
|
|
||||||
subdomain,
|
subdomain,
|
||||||
id,
|
|
||||||
&db::model::Version {
|
&db::model::Version {
|
||||||
data: version_data,
|
data: version_data,
|
||||||
fallback,
|
fallback,
|
||||||
|
@ -270,10 +278,33 @@ impl TalonApi {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
Ok(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the given version as the most recent one
|
||||||
|
fn finalize_version(
|
||||||
|
talon: &Talon,
|
||||||
|
subdomain: &str,
|
||||||
|
version: u32,
|
||||||
|
fallback: Option<&str>,
|
||||||
|
) -> Result<()> {
|
||||||
|
// Validata fallback path
|
||||||
|
if let Some(fallback) = fallback {
|
||||||
|
if let Err(e) =
|
||||||
|
talon
|
||||||
|
.storage
|
||||||
|
.get_file(subdomain, version, fallback, &Default::default())
|
||||||
|
{
|
||||||
|
// Remove the bad version
|
||||||
|
let _ = talon.db.delete_version(subdomain, version, false);
|
||||||
|
return Err(ApiError::InvalidFallback(e.to_string()).into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
talon.db.update_website(
|
talon.db.update_website(
|
||||||
subdomain,
|
subdomain,
|
||||||
db::model::WebsiteUpdate {
|
db::model::WebsiteUpdate {
|
||||||
latest_version: Some(Some(id)),
|
latest_version: Some(Some(version)),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
@ -303,11 +334,12 @@ impl TalonApi {
|
||||||
data: Binary<Vec<u8>>,
|
data: Binary<Vec<u8>>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
auth.check_subdomain(&subdomain, Access::Upload)?;
|
auth.check_subdomain(&subdomain, Access::Upload)?;
|
||||||
let vid = talon.db.new_version_id()?;
|
let version =
|
||||||
|
Self::insert_version(&talon, &subdomain, fallback.clone(), spa.0, version_data.0)?;
|
||||||
talon
|
talon
|
||||||
.storage
|
.storage
|
||||||
.insert_zip_archive(Cursor::new(data.as_slice()), vid)?;
|
.insert_zip_archive(Cursor::new(data.as_slice()), &subdomain, version)?;
|
||||||
Self::insert_version(&talon, &subdomain, vid, fallback.0, spa.0, version_data.0)
|
Self::finalize_version(&talon, &subdomain, version, fallback.as_deref())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Upload a new version (.tar.gz archive)
|
/// Upload a new version (.tar.gz archive)
|
||||||
|
@ -333,8 +365,11 @@ impl TalonApi {
|
||||||
data: Binary<Vec<u8>>,
|
data: Binary<Vec<u8>>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
auth.check_subdomain(&subdomain, Access::Upload)?;
|
auth.check_subdomain(&subdomain, Access::Upload)?;
|
||||||
let vid = talon.db.new_version_id()?;
|
let version =
|
||||||
talon.storage.insert_tgz_archive(data.as_slice(), vid)?;
|
Self::insert_version(&talon, &subdomain, fallback.clone(), spa.0, version_data.0)?;
|
||||||
Self::insert_version(&talon, &subdomain, vid, fallback.0, spa.0, version_data.0)
|
talon
|
||||||
|
.storage
|
||||||
|
.insert_tgz_archive(data.as_slice(), &subdomain, version)?;
|
||||||
|
Self::finalize_version(&talon, &subdomain, version, fallback.as_deref())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
129
src/db/mod.rs
129
src/db/mod.rs
|
@ -259,14 +259,6 @@ impl Db {
|
||||||
String::from_utf8(key).map_err(|e| DbError::Other(format!("could not parse key: {e}")))
|
String::from_utf8(key).map_err(|e| DbError::Other(format!("could not parse key: {e}")))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn split_key(key: Vec<u8>) -> Result<(String, String)> {
|
|
||||||
let key_str = Self::key_to_string(key)?;
|
|
||||||
key_str
|
|
||||||
.split_once(':')
|
|
||||||
.map(|(id, p)| (id.to_owned(), p.to_owned()))
|
|
||||||
.ok_or_else(|| DbError::Other(format!("invalid key: {key_str}")))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn split_version_key(key: Vec<u8>) -> Result<(String, u32)> {
|
fn split_version_key(key: Vec<u8>) -> Result<(String, u32)> {
|
||||||
let key_str = Self::key_to_string(key)?;
|
let key_str = Self::key_to_string(key)?;
|
||||||
key_str
|
key_str
|
||||||
|
@ -275,47 +267,68 @@ impl Db {
|
||||||
.ok_or_else(|| DbError::Other(format!("invalid key: {key_str}")))
|
.ok_or_else(|| DbError::Other(format!("invalid key: {key_str}")))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn increment(old: Option<&[u8]>) -> Option<Vec<u8>> {
|
fn split_file_key(key: Vec<u8>) -> Result<(String, String)> {
|
||||||
let number = match old {
|
let key_str = Self::key_to_string(key)?;
|
||||||
Some(bytes) => {
|
let mut parts = key_str.split(':');
|
||||||
let array: [u8; 4] = bytes.try_into().unwrap();
|
parts.next(); // Skip subdomain part
|
||||||
let number = u32::from_be_bytes(array);
|
|
||||||
number + 1
|
match (parts.next(), parts.next()) {
|
||||||
|
(Some(id), Some(p)) => Ok((id.to_owned(), p.to_owned())),
|
||||||
|
_ => Err(DbError::Other(format!("invalid key: {key_str}"))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns an error if the website does not exist
|
||||||
|
pub fn version_exists(&self, subdomain: &str, id: u32) -> Result<()> {
|
||||||
|
let key = Self::version_key(subdomain, id);
|
||||||
|
|
||||||
|
if !self.i.versions.contains_key(&key)? {
|
||||||
|
Err(DbError::NotExists("version", key))
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
None => 1,
|
|
||||||
};
|
|
||||||
Some(number.to_be_bytes().to_vec())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a version from the database
|
/// Get a version from the database
|
||||||
pub fn get_version(&self, subdomain: &str, id: u32) -> Result<Version> {
|
pub fn get_version(&self, subdomain: &str, id: u32) -> Result<Version> {
|
||||||
let data = self.i.versions.get(Self::version_key(subdomain, id))?;
|
let key = Self::version_key(subdomain, id);
|
||||||
data.and_then(|data| rmp_serde::from_slice::<Version>(data.as_ref()).ok())
|
|
||||||
.ok_or_else(|| DbError::NotExists("version", subdomain.to_owned()))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get a new unique version id
|
let data = self.i.versions.get(&key)?;
|
||||||
pub fn new_version_id(&self) -> Result<u32> {
|
data.and_then(|data| rmp_serde::from_slice::<Version>(data.as_ref()).ok())
|
||||||
Ok(u32::from_be_bytes(
|
.ok_or_else(|| DbError::NotExists("version", key))
|
||||||
self.i
|
|
||||||
.db
|
|
||||||
.update_and_fetch("vid_count", Self::increment)?
|
|
||||||
.unwrap()
|
|
||||||
.as_ref()
|
|
||||||
.try_into()
|
|
||||||
.unwrap(),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Insert a new version into the database
|
/// Insert a new version into the database
|
||||||
pub fn insert_version(&self, subdomain: &str, id: u32, version: &Version) -> Result<()> {
|
///
|
||||||
|
/// Returns the ID of the new version
|
||||||
|
pub fn insert_version(&self, subdomain: &str, version: &Version) -> Result<u32> {
|
||||||
|
let ws = self
|
||||||
|
.i
|
||||||
|
.websites
|
||||||
|
.update_and_fetch(subdomain, |data| match data {
|
||||||
|
Some(data) => match rmp_serde::from_slice::<Website>(data) {
|
||||||
|
Ok(mut w) => {
|
||||||
|
w.vid_count += 1;
|
||||||
|
rmp_serde::to_vec(&w).ok()
|
||||||
|
}
|
||||||
|
Err(_) => None,
|
||||||
|
},
|
||||||
|
None => todo!(),
|
||||||
|
})?
|
||||||
|
.and_then(|data| rmp_serde::from_slice::<Website>(&data).ok());
|
||||||
|
|
||||||
|
let id = match ws {
|
||||||
|
Some(ws) => ws.vid_count,
|
||||||
|
None => return Err(DbError::NotExists("website", subdomain.to_owned())),
|
||||||
|
};
|
||||||
|
|
||||||
let key = Self::version_key(subdomain, id);
|
let key = Self::version_key(subdomain, id);
|
||||||
let data = rmp_serde::to_vec(version)?;
|
let data = rmp_serde::to_vec(version)?;
|
||||||
self.i
|
self.i
|
||||||
.versions
|
.versions
|
||||||
.compare_and_swap(&key, None::<&[u8]>, Some(data))?
|
.compare_and_swap(&key, None::<&[u8]>, Some(data))?
|
||||||
.map_err(|_| DbError::Exists("version", key))?;
|
.map_err(|_| DbError::Exists("version", key))?;
|
||||||
Ok(())
|
Ok(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// internal method for deleting a version from the database
|
/// internal method for deleting a version from the database
|
||||||
|
@ -323,10 +336,10 @@ impl Db {
|
||||||
/// this method does not lock the db or update the associated website
|
/// this method does not lock the db or update the associated website
|
||||||
fn _delete_version(&self, subdomain: &str, id: u32, should_exist: bool) -> Result<()> {
|
fn _delete_version(&self, subdomain: &str, id: u32, should_exist: bool) -> Result<()> {
|
||||||
// Remove all files associated with the version
|
// Remove all files associated with the version
|
||||||
for f in self.get_version_files(id) {
|
for f in self.get_version_files(subdomain, id) {
|
||||||
match f {
|
match f {
|
||||||
Ok((path, _)) => {
|
Ok((path, _)) => {
|
||||||
self.delete_file(id, &path, false)?;
|
self.delete_file(subdomain, id, &path, false)?;
|
||||||
}
|
}
|
||||||
Err(DbError::Sled(e)) => return Err(DbError::Sled(e)),
|
Err(DbError::Sled(e)) => return Err(DbError::Sled(e)),
|
||||||
Err(_) => {}
|
Err(_) => {}
|
||||||
|
@ -397,19 +410,24 @@ impl Db {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn file_key(version: u32, path: &str) -> String {
|
fn file_key(subdomain: &str, version: u32, path: &str) -> String {
|
||||||
format!("{version}:{path}")
|
format!("{subdomain}:{version}:{path}")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the hash of a file in the database
|
/// Get the hash of a file in the database
|
||||||
pub fn get_file_opt(&self, version: u32, path: &str) -> Result<Option<Vec<u8>>> {
|
pub fn get_file_opt(
|
||||||
let key = Self::file_key(version, path);
|
&self,
|
||||||
|
subdomain: &str,
|
||||||
|
version: u32,
|
||||||
|
path: &str,
|
||||||
|
) -> Result<Option<Vec<u8>>> {
|
||||||
|
let key = Self::file_key(subdomain, version, path);
|
||||||
Ok(self.i.files.get(key)?.map(|hash| hash.to_vec()))
|
Ok(self.i.files.get(key)?.map(|hash| hash.to_vec()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the hash of a file in the database
|
/// Get the hash of a file in the database
|
||||||
pub fn get_file(&self, version: u32, path: &str) -> Result<Vec<u8>> {
|
pub fn get_file(&self, subdomain: &str, version: u32, path: &str) -> Result<Vec<u8>> {
|
||||||
let key = Self::file_key(version, path);
|
let key = Self::file_key(subdomain, version, path);
|
||||||
match self.i.files.get(&key)? {
|
match self.i.files.get(&key)? {
|
||||||
Some(hash) => Ok(hash.to_vec()),
|
Some(hash) => Ok(hash.to_vec()),
|
||||||
None => Err(DbError::NotExists("file", key)),
|
None => Err(DbError::NotExists("file", key)),
|
||||||
|
@ -417,8 +435,14 @@ impl Db {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Insert a file into the database
|
/// Insert a file into the database
|
||||||
pub fn insert_file(&self, version: u32, path: &str, hash: &[u8]) -> Result<()> {
|
pub fn insert_file(
|
||||||
let key = Self::file_key(version, path);
|
&self,
|
||||||
|
subdomain: &str,
|
||||||
|
version: u32,
|
||||||
|
path: &str,
|
||||||
|
hash: &[u8],
|
||||||
|
) -> Result<()> {
|
||||||
|
let key = Self::file_key(subdomain, version, path);
|
||||||
self.i
|
self.i
|
||||||
.files
|
.files
|
||||||
.compare_and_swap(&key, None::<&[u8]>, Some(hash))?
|
.compare_and_swap(&key, None::<&[u8]>, Some(hash))?
|
||||||
|
@ -426,8 +450,14 @@ impl Db {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Delete a file in the database
|
/// Delete a file in the database
|
||||||
pub fn delete_file(&self, version: u32, path: &str, should_exist: bool) -> Result<()> {
|
pub fn delete_file(
|
||||||
let key = Self::file_key(version, path);
|
&self,
|
||||||
|
subdomain: &str,
|
||||||
|
version: u32,
|
||||||
|
path: &str,
|
||||||
|
should_exist: bool,
|
||||||
|
) -> Result<()> {
|
||||||
|
let key = Self::file_key(subdomain, version, path);
|
||||||
let res = self.i.files.remove(&key)?;
|
let res = self.i.files.remove(&key)?;
|
||||||
|
|
||||||
if should_exist && res.is_none() {
|
if should_exist && res.is_none() {
|
||||||
|
@ -442,13 +472,14 @@ impl Db {
|
||||||
/// Result: Tuples of file path and hash
|
/// Result: Tuples of file path and hash
|
||||||
pub fn get_version_files(
|
pub fn get_version_files(
|
||||||
&self,
|
&self,
|
||||||
id: u32,
|
subdomain: &str,
|
||||||
|
version: u32,
|
||||||
) -> impl DoubleEndedIterator<Item = Result<(String, Vec<u8>)>> {
|
) -> impl DoubleEndedIterator<Item = Result<(String, Vec<u8>)>> {
|
||||||
let key = Self::file_key(id, "");
|
let key = Self::file_key(subdomain, version, "");
|
||||||
|
|
||||||
self.i.files.scan_prefix(key).map(|r| {
|
self.i.files.scan_prefix(key).map(|r| {
|
||||||
r.map_err(DbError::from).and_then(|(k, v)| {
|
r.map_err(DbError::from).and_then(|(k, v)| {
|
||||||
let (_, path) = Self::split_key(k.to_vec())?;
|
let (_, path) = Self::split_file_key(k.to_vec())?;
|
||||||
Ok((path, v.to_vec()))
|
Ok((path, v.to_vec()))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -22,6 +22,10 @@ pub struct Website {
|
||||||
pub source_url: Option<String>,
|
pub source_url: Option<String>,
|
||||||
/// Icon for the source link
|
/// Icon for the source link
|
||||||
pub source_icon: Option<SourceIcon>,
|
pub source_icon: Option<SourceIcon>,
|
||||||
|
/// Version ID counter
|
||||||
|
///
|
||||||
|
/// value + 1 will be the next version ID
|
||||||
|
pub vid_count: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Website {
|
impl Default for Website {
|
||||||
|
@ -34,6 +38,7 @@ impl Default for Website {
|
||||||
visibility: Default::default(),
|
visibility: Default::default(),
|
||||||
source_url: Default::default(),
|
source_url: Default::default(),
|
||||||
source_icon: Default::default(),
|
source_icon: Default::default(),
|
||||||
|
vid_count: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
16
src/page.rs
16
src/page.rs
|
@ -36,20 +36,28 @@ pub async fn page(request: &Request, talon: Data<&Talon>) -> Result<Response> {
|
||||||
|
|
||||||
let ws = talon.db.get_website(subdomain)?;
|
let ws = talon.db.get_website(subdomain)?;
|
||||||
let vid = ws.latest_version.ok_or(PageError::NoVersion)?;
|
let vid = ws.latest_version.ok_or(PageError::NoVersion)?;
|
||||||
let (file, ok) = match talon
|
let (file, ok) =
|
||||||
|
match talon
|
||||||
.storage
|
.storage
|
||||||
.get_file(vid, request.uri().path(), request.headers())
|
.get_file(subdomain, vid, request.uri().path(), request.headers())
|
||||||
{
|
{
|
||||||
Ok(file) => (file, true),
|
Ok(file) => (file, true),
|
||||||
Err(StorageError::NotFound(f)) => {
|
Err(StorageError::NotFound(f)) => {
|
||||||
let version = talon.db.get_version(subdomain, vid)?;
|
let version = talon.db.get_version(subdomain, vid)?;
|
||||||
if let Some(fallback) = &version.fallback {
|
if let Some(fallback) = &version.fallback {
|
||||||
(
|
(
|
||||||
talon.storage.get_file(vid, fallback, request.headers())?,
|
talon
|
||||||
|
.storage
|
||||||
|
.get_file(subdomain, vid, fallback, request.headers())?,
|
||||||
version.spa,
|
version.spa,
|
||||||
)
|
)
|
||||||
} else if version.spa {
|
} else if version.spa {
|
||||||
(talon.storage.get_file(vid, "", request.headers())?, true)
|
(
|
||||||
|
talon
|
||||||
|
.storage
|
||||||
|
.get_file(subdomain, vid, "", request.headers())?,
|
||||||
|
true,
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
return Err(StorageError::NotFound(f).into());
|
return Err(StorageError::NotFound(f).into());
|
||||||
}
|
}
|
||||||
|
|
|
@ -114,6 +114,7 @@ impl Storage {
|
||||||
pub fn insert_file<P: AsRef<Path>>(
|
pub fn insert_file<P: AsRef<Path>>(
|
||||||
&self,
|
&self,
|
||||||
file_path: P,
|
file_path: P,
|
||||||
|
subdomain: &str,
|
||||||
version: u32,
|
version: u32,
|
||||||
site_path: &str,
|
site_path: &str,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
|
@ -153,7 +154,7 @@ impl Storage {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.db.insert_file(version, site_path, &hash)?;
|
self.db.insert_file(subdomain, version, site_path, &hash)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -210,30 +211,40 @@ impl Storage {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Insert a directory of files into the store
|
/// Insert a directory of files into the store
|
||||||
pub fn insert_dir<P: AsRef<Path>>(&self, dir: P, version: u32) -> Result<()> {
|
pub fn insert_dir<P: AsRef<Path>>(&self, dir: P, subdomain: &str, version: u32) -> Result<()> {
|
||||||
Self::visit_files(dir, "", &|file_path, site_path| {
|
Self::visit_files(dir, "", &|file_path, site_path| {
|
||||||
self.insert_file(file_path, version, site_path)
|
self.insert_file(file_path, subdomain, version, site_path)
|
||||||
})?;
|
})?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Insert the contents of a zip archive into the store
|
/// Insert the contents of a zip archive into the store
|
||||||
pub fn insert_zip_archive(&self, reader: impl Read + Seek, version: u32) -> Result<()> {
|
pub fn insert_zip_archive(
|
||||||
|
&self,
|
||||||
|
reader: impl Read + Seek,
|
||||||
|
subdomain: &str,
|
||||||
|
version: u32,
|
||||||
|
) -> Result<()> {
|
||||||
let temp = TempDir::with_prefix(TMPDIR_PREFIX)?;
|
let temp = TempDir::with_prefix(TMPDIR_PREFIX)?;
|
||||||
let mut zip = ZipArchive::new(reader)?;
|
let mut zip = ZipArchive::new(reader)?;
|
||||||
zip.extract(temp.path())?;
|
zip.extract(temp.path())?;
|
||||||
let import_path = Self::fix_archive_path(temp.path())?;
|
let import_path = Self::fix_archive_path(temp.path())?;
|
||||||
self.insert_dir(import_path, version)
|
self.insert_dir(import_path, subdomain, version)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Insert the contents of a tar.gz archive into the store
|
/// Insert the contents of a tar.gz archive into the store
|
||||||
pub fn insert_tgz_archive(&self, reader: impl Read, version: u32) -> Result<()> {
|
pub fn insert_tgz_archive(
|
||||||
|
&self,
|
||||||
|
reader: impl Read,
|
||||||
|
subdomain: &str,
|
||||||
|
version: u32,
|
||||||
|
) -> Result<()> {
|
||||||
let temp = TempDir::with_prefix(TMPDIR_PREFIX)?;
|
let temp = TempDir::with_prefix(TMPDIR_PREFIX)?;
|
||||||
let decoder = GzDecoder::new(reader);
|
let decoder = GzDecoder::new(reader);
|
||||||
let mut archive = tar::Archive::new(decoder);
|
let mut archive = tar::Archive::new(decoder);
|
||||||
archive.unpack(temp.path())?;
|
archive.unpack(temp.path())?;
|
||||||
let import_path = Self::fix_archive_path(temp.path())?;
|
let import_path = Self::fix_archive_path(temp.path())?;
|
||||||
self.insert_dir(import_path, version)
|
self.insert_dir(import_path, subdomain, version)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the path of a file with the given hash while creating the subdirectory
|
/// Get the path of a file with the given hash while creating the subdirectory
|
||||||
|
@ -302,7 +313,13 @@ impl Storage {
|
||||||
/// Get a file using the raw site path and the website version
|
/// Get a file using the raw site path and the website version
|
||||||
///
|
///
|
||||||
/// HTTP headers are used to determine if the compressed version of a file should be returned.
|
/// HTTP headers are used to determine if the compressed version of a file should be returned.
|
||||||
pub fn get_file(&self, version: u32, site_path: &str, headers: &HeaderMap) -> Result<GotFile> {
|
pub fn get_file(
|
||||||
|
&self,
|
||||||
|
subdomain: &str,
|
||||||
|
version: u32,
|
||||||
|
site_path: &str,
|
||||||
|
headers: &HeaderMap,
|
||||||
|
) -> Result<GotFile> {
|
||||||
let sp = util::trim_site_path(site_path);
|
let sp = util::trim_site_path(site_path);
|
||||||
let mut new_path: Cow<str> = sp.into();
|
let mut new_path: Cow<str> = sp.into();
|
||||||
let mut rd_path = None;
|
let mut rd_path = None;
|
||||||
|
@ -315,7 +332,7 @@ impl Storage {
|
||||||
// Attempt to access the following pages
|
// Attempt to access the following pages
|
||||||
// 1. Site path directly
|
// 1. Site path directly
|
||||||
// 2. Site path + `/index.html`
|
// 2. Site path + `/index.html`
|
||||||
match self.db.get_file_opt(version, sp)? {
|
match self.db.get_file_opt(subdomain, version, sp)? {
|
||||||
Some(h) => {
|
Some(h) => {
|
||||||
hash = Some(h);
|
hash = Some(h);
|
||||||
}
|
}
|
||||||
|
@ -334,7 +351,7 @@ impl Storage {
|
||||||
Some(hash) => hash,
|
Some(hash) => hash,
|
||||||
None => self
|
None => self
|
||||||
.db
|
.db
|
||||||
.get_file_opt(version, &new_path)?
|
.get_file_opt(subdomain, version, &new_path)?
|
||||||
.ok_or_else(|| StorageError::NotFound(sp.to_owned()))?,
|
.ok_or_else(|| StorageError::NotFound(sp.to_owned()))?,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
88
tests/fixtures/mod.rs
vendored
88
tests/fixtures/mod.rs
vendored
|
@ -19,12 +19,6 @@ pub const SUBDOMAIN_2: &str = "spotify-gender-ex";
|
||||||
pub const SUBDOMAIN_3: &str = "rustypipe";
|
pub const SUBDOMAIN_3: &str = "rustypipe";
|
||||||
pub const SUBDOMAIN_4: &str = "spa";
|
pub const SUBDOMAIN_4: &str = "spa";
|
||||||
|
|
||||||
pub const VERSION_1_1: u32 = 1;
|
|
||||||
pub const VERSION_1_2: u32 = 2;
|
|
||||||
pub const VERSION_2_1: u32 = 3;
|
|
||||||
pub const VERSION_3_1: u32 = 4;
|
|
||||||
pub const VERSION_4_1: u32 = 5;
|
|
||||||
|
|
||||||
pub const HASH_1_1_INDEX: [u8; 32] =
|
pub const HASH_1_1_INDEX: [u8; 32] =
|
||||||
hex!("3b5f6bad5376897435def176d0fe77e5b9b4f0deafc7491fc27262650744ad68");
|
hex!("3b5f6bad5376897435def176d0fe77e5b9b4f0deafc7491fc27262650744ad68");
|
||||||
pub const HASH_1_1_STYLE: [u8; 32] =
|
pub const HASH_1_1_STYLE: [u8; 32] =
|
||||||
|
@ -76,7 +70,7 @@ fn insert_websites(db: &Db) {
|
||||||
&Website {
|
&Website {
|
||||||
name: "ThetaDev".to_owned(),
|
name: "ThetaDev".to_owned(),
|
||||||
created_at: datetime!(2023-02-18 16:30 +0),
|
created_at: datetime!(2023-02-18 16:30 +0),
|
||||||
latest_version: Some(VERSION_1_2),
|
latest_version: Some(2),
|
||||||
color: Some(2068974),
|
color: Some(2068974),
|
||||||
visibility: talon::model::Visibility::Featured,
|
visibility: talon::model::Visibility::Featured,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
@ -88,7 +82,7 @@ fn insert_websites(db: &Db) {
|
||||||
&Website {
|
&Website {
|
||||||
name: "Spotify-Gender-Ex".to_owned(),
|
name: "Spotify-Gender-Ex".to_owned(),
|
||||||
created_at: datetime!(2023-02-18 16:30 +0),
|
created_at: datetime!(2023-02-18 16:30 +0),
|
||||||
latest_version: Some(VERSION_2_1),
|
latest_version: Some(1),
|
||||||
color: Some(1947988),
|
color: Some(1947988),
|
||||||
visibility: talon::model::Visibility::Featured,
|
visibility: talon::model::Visibility::Featured,
|
||||||
source_url: Some("https://github.com/Theta-Dev/Spotify-Gender-Ex".to_owned()),
|
source_url: Some("https://github.com/Theta-Dev/Spotify-Gender-Ex".to_owned()),
|
||||||
|
@ -102,7 +96,7 @@ fn insert_websites(db: &Db) {
|
||||||
&Website {
|
&Website {
|
||||||
name: "RustyPipe".to_owned(),
|
name: "RustyPipe".to_owned(),
|
||||||
created_at: datetime!(2023-02-20 18:30 +0),
|
created_at: datetime!(2023-02-20 18:30 +0),
|
||||||
latest_version: Some(VERSION_3_1),
|
latest_version: Some(1),
|
||||||
color: Some(7943647),
|
color: Some(7943647),
|
||||||
visibility: talon::model::Visibility::Featured,
|
visibility: talon::model::Visibility::Featured,
|
||||||
source_url: Some("https://code.thetadev.de/ThetaDev/rustypipe".to_owned()),
|
source_url: Some("https://code.thetadev.de/ThetaDev/rustypipe".to_owned()),
|
||||||
|
@ -116,7 +110,7 @@ fn insert_websites(db: &Db) {
|
||||||
&Website {
|
&Website {
|
||||||
name: "SvelteKit SPA".to_owned(),
|
name: "SvelteKit SPA".to_owned(),
|
||||||
created_at: datetime!(2023-03-03 22:00 +0),
|
created_at: datetime!(2023-03-03 22:00 +0),
|
||||||
latest_version: Some(VERSION_4_1),
|
latest_version: Some(1),
|
||||||
color: Some(16727552),
|
color: Some(16727552),
|
||||||
visibility: talon::model::Visibility::Hidden,
|
visibility: talon::model::Visibility::Hidden,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
@ -130,17 +124,18 @@ fn insert_websites(db: &Db) {
|
||||||
"Deployed by".to_owned(),
|
"Deployed by".to_owned(),
|
||||||
"https://github.com/Theta-Dev/Talon/actions/runs/1352014628".to_owned(),
|
"https://github.com/Theta-Dev/Talon/actions/runs/1352014628".to_owned(),
|
||||||
);
|
);
|
||||||
assert_eq!(db.new_version_id().unwrap(), VERSION_1_1);
|
assert_eq!(
|
||||||
db.insert_version(
|
db.insert_version(
|
||||||
SUBDOMAIN_1,
|
SUBDOMAIN_1,
|
||||||
VERSION_1_1,
|
|
||||||
&Version {
|
&Version {
|
||||||
created_at: datetime!(2023-02-18 16:30 +0),
|
created_at: datetime!(2023-02-18 16:30 +0),
|
||||||
data: v1_data,
|
data: v1_data,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap(),
|
||||||
|
1
|
||||||
|
);
|
||||||
|
|
||||||
let mut v2_data = BTreeMap::new();
|
let mut v2_data = BTreeMap::new();
|
||||||
v2_data.insert("Version".to_owned(), "v0.1.1".to_owned());
|
v2_data.insert("Version".to_owned(), "v0.1.1".to_owned());
|
||||||
|
@ -148,43 +143,44 @@ fn insert_websites(db: &Db) {
|
||||||
"Deployed by".to_owned(),
|
"Deployed by".to_owned(),
|
||||||
"https://github.com/Theta-Dev/Talon/actions/runs/1354755231".to_owned(),
|
"https://github.com/Theta-Dev/Talon/actions/runs/1354755231".to_owned(),
|
||||||
);
|
);
|
||||||
assert_eq!(db.new_version_id().unwrap(), VERSION_1_2);
|
assert_eq!(
|
||||||
db.insert_version(
|
db.insert_version(
|
||||||
SUBDOMAIN_1,
|
SUBDOMAIN_1,
|
||||||
VERSION_1_2,
|
|
||||||
&Version {
|
&Version {
|
||||||
created_at: datetime!(2023-02-18 16:52 +0),
|
created_at: datetime!(2023-02-18 16:52 +0),
|
||||||
data: v2_data,
|
data: v2_data,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap(),
|
||||||
|
2
|
||||||
|
);
|
||||||
|
|
||||||
assert_eq!(db.new_version_id().unwrap(), VERSION_2_1);
|
assert_eq!(
|
||||||
db.insert_version(
|
db.insert_version(
|
||||||
SUBDOMAIN_2,
|
SUBDOMAIN_2,
|
||||||
VERSION_2_1,
|
|
||||||
&Version {
|
&Version {
|
||||||
created_at: datetime!(2023-02-18 16:30 +0),
|
created_at: datetime!(2023-02-18 16:30 +0),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap(),
|
||||||
assert_eq!(db.new_version_id().unwrap(), VERSION_3_1);
|
1
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
db.insert_version(
|
db.insert_version(
|
||||||
SUBDOMAIN_3,
|
SUBDOMAIN_3,
|
||||||
VERSION_3_1,
|
|
||||||
&Version {
|
&Version {
|
||||||
created_at: datetime!(2023-02-20 18:30 +0),
|
created_at: datetime!(2023-02-20 18:30 +0),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap(),
|
||||||
|
1
|
||||||
assert_eq!(db.new_version_id().unwrap(), VERSION_4_1);
|
);
|
||||||
|
assert_eq!(
|
||||||
db.insert_version(
|
db.insert_version(
|
||||||
SUBDOMAIN_4,
|
SUBDOMAIN_4,
|
||||||
VERSION_4_1,
|
|
||||||
&Version {
|
&Version {
|
||||||
created_at: datetime!(2023-03-03 22:00 +0),
|
created_at: datetime!(2023-03-03 22:00 +0),
|
||||||
fallback: Some("200.html".to_owned()),
|
fallback: Some("200.html".to_owned()),
|
||||||
|
@ -192,7 +188,9 @@ fn insert_websites(db: &Db) {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap(),
|
||||||
|
1
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[fixture]
|
#[fixture]
|
||||||
|
@ -201,44 +199,47 @@ pub fn db() -> DbTest {
|
||||||
let db = Db::new(&temp).unwrap();
|
let db = Db::new(&temp).unwrap();
|
||||||
insert_websites(&db);
|
insert_websites(&db);
|
||||||
|
|
||||||
db.insert_file(VERSION_1_1, "index.html", &HASH_1_1_INDEX)
|
db.insert_file(SUBDOMAIN_1, 1, "index.html", &HASH_1_1_INDEX)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
db.insert_file(VERSION_1_1, "style.css", &HASH_1_1_STYLE)
|
db.insert_file(SUBDOMAIN_1, 1, "style.css", &HASH_1_1_STYLE)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
db.insert_file(VERSION_1_2, "index.html", &HASH_1_2_INDEX)
|
db.insert_file(SUBDOMAIN_1, 2, "index.html", &HASH_1_2_INDEX)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
db.insert_file(VERSION_1_2, "assets/style.css", &HASH_1_2_STYLE)
|
db.insert_file(SUBDOMAIN_1, 2, "assets/style.css", &HASH_1_2_STYLE)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
db.insert_file(
|
db.insert_file(
|
||||||
VERSION_1_2,
|
SUBDOMAIN_1,
|
||||||
|
2,
|
||||||
"assets/image.jpg",
|
"assets/image.jpg",
|
||||||
&hex!("901d291a47a8a9b55c06f84e5e5f82fd2dcee65cac1406d6e878b805d45c1e93"),
|
&hex!("901d291a47a8a9b55c06f84e5e5f82fd2dcee65cac1406d6e878b805d45c1e93"),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
db.insert_file(
|
db.insert_file(
|
||||||
VERSION_1_2,
|
SUBDOMAIN_1,
|
||||||
|
2,
|
||||||
"assets/test.js",
|
"assets/test.js",
|
||||||
&hex!("b6ed35f5ae339a35a8babb11a91ff90c1a62ef250d30fa98e59500e8dbb896fa"),
|
&hex!("b6ed35f5ae339a35a8babb11a91ff90c1a62ef250d30fa98e59500e8dbb896fa"),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
db.insert_file(
|
db.insert_file(
|
||||||
VERSION_1_2,
|
SUBDOMAIN_1,
|
||||||
|
2,
|
||||||
"data/example.txt",
|
"data/example.txt",
|
||||||
&hex!("bae6bdae8097c24f9a99028e04bfc8d5e0a0c318955316db0e7b955def9c1dbb"),
|
&hex!("bae6bdae8097c24f9a99028e04bfc8d5e0a0c318955316db0e7b955def9c1dbb"),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
db.insert_file(VERSION_2_1, "index.html", &HASH_2_1_INDEX)
|
db.insert_file(SUBDOMAIN_2, 1, "index.html", &HASH_2_1_INDEX)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
db.insert_file(VERSION_2_1, "gex_style.css", &HASH_2_1_STYLE)
|
db.insert_file(SUBDOMAIN_2, 1, "gex_style.css", &HASH_2_1_STYLE)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
db.insert_file(VERSION_3_1, "index.html", &HASH_3_1_INDEX)
|
db.insert_file(SUBDOMAIN_3, 1, "index.html", &HASH_3_1_INDEX)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
db.insert_file(VERSION_3_1, "rp_style.css", &HASH_3_1_STYLE)
|
db.insert_file(SUBDOMAIN_3, 1, "rp_style.css", &HASH_3_1_STYLE)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
db.insert_file(VERSION_3_1, "page2/index.html", &HASH_3_1_PAGE2)
|
db.insert_file(SUBDOMAIN_3, 1, "page2/index.html", &HASH_3_1_PAGE2)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
DbTest { db, _temp: temp }
|
DbTest { db, _temp: temp }
|
||||||
|
@ -266,25 +267,26 @@ pub fn tln() -> TalonTest {
|
||||||
|
|
||||||
talon
|
talon
|
||||||
.storage
|
.storage
|
||||||
.insert_dir(path!("tests" / "testfiles" / "ThetaDev0"), VERSION_1_1)
|
.insert_dir(path!("tests" / "testfiles" / "ThetaDev0"), SUBDOMAIN_1, 1)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
talon
|
talon
|
||||||
.storage
|
.storage
|
||||||
.insert_dir(path!("tests" / "testfiles" / "ThetaDev1"), VERSION_1_2)
|
.insert_dir(path!("tests" / "testfiles" / "ThetaDev1"), SUBDOMAIN_1, 2)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
talon
|
talon
|
||||||
.storage
|
.storage
|
||||||
.insert_dir(path!("tests" / "testfiles" / "GenderEx"), VERSION_2_1)
|
.insert_dir(path!("tests" / "testfiles" / "GenderEx"), SUBDOMAIN_2, 1)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
talon
|
talon
|
||||||
.storage
|
.storage
|
||||||
.insert_dir(path!("tests" / "testfiles" / "RustyPipe"), VERSION_3_1)
|
.insert_dir(path!("tests" / "testfiles" / "RustyPipe"), SUBDOMAIN_3, 1)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
talon
|
talon
|
||||||
.storage
|
.storage
|
||||||
.insert_tgz_archive(
|
.insert_tgz_archive(
|
||||||
File::open(path!("tests" / "testfiles" / "archive" / "spa.tar.gz")).unwrap(),
|
File::open(path!("tests" / "testfiles" / "archive" / "spa.tar.gz")).unwrap(),
|
||||||
VERSION_4_1,
|
SUBDOMAIN_4,
|
||||||
|
1,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
|
|
@ -2,14 +2,14 @@
|
||||||
source: tests/tests.rs
|
source: tests/tests.rs
|
||||||
expression: data
|
expression: data
|
||||||
---
|
---
|
||||||
{"type":"website","key":"rustypipe","value":{"name":"RustyPipe","created_at":[2023,51,18,30,0,0,0,0,0],"latest_version":4,"color":7943647,"visibility":"featured","source_url":"https://code.thetadev.de/ThetaDev/rustypipe","source_icon":"gitea"}}
|
{"type":"website","key":"rustypipe","value":{"name":"RustyPipe","created_at":[2023,51,18,30,0,0,0,0,0],"latest_version":1,"color":7943647,"visibility":"featured","source_url":"https://code.thetadev.de/ThetaDev/rustypipe","source_icon":"gitea","vid_count":1}}
|
||||||
{"type":"website","key":"spa","value":{"name":"SvelteKit SPA","created_at":[2023,62,22,0,0,0,0,0,0],"latest_version":5,"color":16727552,"visibility":"hidden","source_url":null,"source_icon":null}}
|
{"type":"website","key":"spa","value":{"name":"SvelteKit SPA","created_at":[2023,62,22,0,0,0,0,0,0],"latest_version":1,"color":16727552,"visibility":"hidden","source_url":null,"source_icon":null,"vid_count":1}}
|
||||||
{"type":"website","key":"spotify-gender-ex","value":{"name":"Spotify-Gender-Ex","created_at":[2023,49,16,30,0,0,0,0,0],"latest_version":3,"color":1947988,"visibility":"featured","source_url":"https://github.com/Theta-Dev/Spotify-Gender-Ex","source_icon":"github"}}
|
{"type":"website","key":"spotify-gender-ex","value":{"name":"Spotify-Gender-Ex","created_at":[2023,49,16,30,0,0,0,0,0],"latest_version":1,"color":1947988,"visibility":"featured","source_url":"https://github.com/Theta-Dev/Spotify-Gender-Ex","source_icon":"github","vid_count":1}}
|
||||||
{"type":"version","key":"rustypipe:4","value":{"created_at":[2023,51,18,30,0,0,0,0,0],"data":{},"fallback":null,"spa":false}}
|
{"type":"version","key":"rustypipe:1","value":{"created_at":[2023,51,18,30,0,0,0,0,0],"data":{},"fallback":null,"spa":false}}
|
||||||
{"type":"version","key":"spa:5","value":{"created_at":[2023,62,22,0,0,0,0,0,0],"data":{},"fallback":"200.html","spa":true}}
|
{"type":"version","key":"spa:1","value":{"created_at":[2023,62,22,0,0,0,0,0,0],"data":{},"fallback":"200.html","spa":true}}
|
||||||
{"type":"version","key":"spotify-gender-ex:3","value":{"created_at":[2023,49,16,30,0,0,0,0,0],"data":{},"fallback":null,"spa":false}}
|
{"type":"version","key":"spotify-gender-ex:1","value":{"created_at":[2023,49,16,30,0,0,0,0,0],"data":{},"fallback":null,"spa":false}}
|
||||||
{"type":"file","key":"3:gex_style.css","value":"fc825b409a49724af8f5b3c4ad15e175e68095ea746237a7b46152d3f383f541"}
|
{"type":"file","key":"rustypipe:1:index.html","value":"cc31423924cf1f124750825861ab1ccc675e755921fc2fa111c0a98e8c346a5e"}
|
||||||
{"type":"file","key":"3:index.html","value":"6c5d37546616519e8973be51515b8a90898b4675f7b6d01f2d891edb686408a2"}
|
{"type":"file","key":"rustypipe:1:page2/index.html","value":"be4f409ca0adcb21cdc7130cde63031718406726f889ef97ac8870c90b330a75"}
|
||||||
{"type":"file","key":"4:index.html","value":"cc31423924cf1f124750825861ab1ccc675e755921fc2fa111c0a98e8c346a5e"}
|
{"type":"file","key":"rustypipe:1:rp_style.css","value":"ee4fc4911a56e627c047a29ba3085131939d8d487759b9149d42aaab89ce8993"}
|
||||||
{"type":"file","key":"4:page2/index.html","value":"be4f409ca0adcb21cdc7130cde63031718406726f889ef97ac8870c90b330a75"}
|
{"type":"file","key":"spotify-gender-ex:1:gex_style.css","value":"fc825b409a49724af8f5b3c4ad15e175e68095ea746237a7b46152d3f383f541"}
|
||||||
{"type":"file","key":"4:rp_style.css","value":"ee4fc4911a56e627c047a29ba3085131939d8d487759b9149d42aaab89ce8993"}
|
{"type":"file","key":"spotify-gender-ex:1:index.html","value":"6c5d37546616519e8973be51515b8a90898b4675f7b6d01f2d891edb686408a2"}
|
||||||
|
|
|
@ -2,24 +2,24 @@
|
||||||
source: tests/tests.rs
|
source: tests/tests.rs
|
||||||
expression: data
|
expression: data
|
||||||
---
|
---
|
||||||
{"type":"website","key":"-","value":{"name":"ThetaDev","created_at":[2023,49,16,30,0,0,0,0,0],"latest_version":2,"color":2068974,"visibility":"featured","source_url":null,"source_icon":null}}
|
{"type":"website","key":"-","value":{"name":"ThetaDev","created_at":[2023,49,16,30,0,0,0,0,0],"latest_version":2,"color":2068974,"visibility":"featured","source_url":null,"source_icon":null,"vid_count":2}}
|
||||||
{"type":"website","key":"rustypipe","value":{"name":"RustyPipe","created_at":[2023,51,18,30,0,0,0,0,0],"latest_version":4,"color":7943647,"visibility":"featured","source_url":"https://code.thetadev.de/ThetaDev/rustypipe","source_icon":"gitea"}}
|
{"type":"website","key":"rustypipe","value":{"name":"RustyPipe","created_at":[2023,51,18,30,0,0,0,0,0],"latest_version":1,"color":7943647,"visibility":"featured","source_url":"https://code.thetadev.de/ThetaDev/rustypipe","source_icon":"gitea","vid_count":1}}
|
||||||
{"type":"website","key":"spa","value":{"name":"SvelteKit SPA","created_at":[2023,62,22,0,0,0,0,0,0],"latest_version":5,"color":16727552,"visibility":"hidden","source_url":null,"source_icon":null}}
|
{"type":"website","key":"spa","value":{"name":"SvelteKit SPA","created_at":[2023,62,22,0,0,0,0,0,0],"latest_version":1,"color":16727552,"visibility":"hidden","source_url":null,"source_icon":null,"vid_count":1}}
|
||||||
{"type":"website","key":"spotify-gender-ex","value":{"name":"Spotify-Gender-Ex","created_at":[2023,49,16,30,0,0,0,0,0],"latest_version":3,"color":1947988,"visibility":"featured","source_url":"https://github.com/Theta-Dev/Spotify-Gender-Ex","source_icon":"github"}}
|
{"type":"website","key":"spotify-gender-ex","value":{"name":"Spotify-Gender-Ex","created_at":[2023,49,16,30,0,0,0,0,0],"latest_version":1,"color":1947988,"visibility":"featured","source_url":"https://github.com/Theta-Dev/Spotify-Gender-Ex","source_icon":"github","vid_count":1}}
|
||||||
{"type":"version","key":"-:1","value":{"created_at":[2023,49,16,30,0,0,0,0,0],"data":{"Deployed by":"https://github.com/Theta-Dev/Talon/actions/runs/1352014628","Version":"v0.1.0"},"fallback":null,"spa":false}}
|
{"type":"version","key":"-:1","value":{"created_at":[2023,49,16,30,0,0,0,0,0],"data":{"Deployed by":"https://github.com/Theta-Dev/Talon/actions/runs/1352014628","Version":"v0.1.0"},"fallback":null,"spa":false}}
|
||||||
{"type":"version","key":"-:2","value":{"created_at":[2023,49,16,52,0,0,0,0,0],"data":{"Deployed by":"https://github.com/Theta-Dev/Talon/actions/runs/1354755231","Version":"v0.1.1"},"fallback":null,"spa":false}}
|
{"type":"version","key":"-:2","value":{"created_at":[2023,49,16,52,0,0,0,0,0],"data":{"Deployed by":"https://github.com/Theta-Dev/Talon/actions/runs/1354755231","Version":"v0.1.1"},"fallback":null,"spa":false}}
|
||||||
{"type":"version","key":"rustypipe:4","value":{"created_at":[2023,51,18,30,0,0,0,0,0],"data":{},"fallback":null,"spa":false}}
|
{"type":"version","key":"rustypipe:1","value":{"created_at":[2023,51,18,30,0,0,0,0,0],"data":{},"fallback":null,"spa":false}}
|
||||||
{"type":"version","key":"spa:5","value":{"created_at":[2023,62,22,0,0,0,0,0,0],"data":{},"fallback":"200.html","spa":true}}
|
{"type":"version","key":"spa:1","value":{"created_at":[2023,62,22,0,0,0,0,0,0],"data":{},"fallback":"200.html","spa":true}}
|
||||||
{"type":"version","key":"spotify-gender-ex:3","value":{"created_at":[2023,49,16,30,0,0,0,0,0],"data":{},"fallback":null,"spa":false}}
|
{"type":"version","key":"spotify-gender-ex:1","value":{"created_at":[2023,49,16,30,0,0,0,0,0],"data":{},"fallback":null,"spa":false}}
|
||||||
{"type":"file","key":"1:index.html","value":"3b5f6bad5376897435def176d0fe77e5b9b4f0deafc7491fc27262650744ad68"}
|
{"type":"file","key":"-:1:index.html","value":"3b5f6bad5376897435def176d0fe77e5b9b4f0deafc7491fc27262650744ad68"}
|
||||||
{"type":"file","key":"1:style.css","value":"356f131c825fbf604797c7e9c85352549d81db8af91fee834016d075110af026"}
|
{"type":"file","key":"-:1:style.css","value":"356f131c825fbf604797c7e9c85352549d81db8af91fee834016d075110af026"}
|
||||||
{"type":"file","key":"2:assets/image.jpg","value":"901d291a47a8a9b55c06f84e5e5f82fd2dcee65cac1406d6e878b805d45c1e93"}
|
{"type":"file","key":"-:2:assets/image.jpg","value":"901d291a47a8a9b55c06f84e5e5f82fd2dcee65cac1406d6e878b805d45c1e93"}
|
||||||
{"type":"file","key":"2:assets/style.css","value":"356f131c825fbf604797c7e9c85352549d81db8af91fee834016d075110af026"}
|
{"type":"file","key":"-:2:assets/style.css","value":"356f131c825fbf604797c7e9c85352549d81db8af91fee834016d075110af026"}
|
||||||
{"type":"file","key":"2:assets/test.js","value":"b6ed35f5ae339a35a8babb11a91ff90c1a62ef250d30fa98e59500e8dbb896fa"}
|
{"type":"file","key":"-:2:assets/test.js","value":"b6ed35f5ae339a35a8babb11a91ff90c1a62ef250d30fa98e59500e8dbb896fa"}
|
||||||
{"type":"file","key":"2:data/example.txt","value":"bae6bdae8097c24f9a99028e04bfc8d5e0a0c318955316db0e7b955def9c1dbb"}
|
{"type":"file","key":"-:2:data/example.txt","value":"bae6bdae8097c24f9a99028e04bfc8d5e0a0c318955316db0e7b955def9c1dbb"}
|
||||||
{"type":"file","key":"2:index.html","value":"a44816e6c3b650bdf88e6532659ba07ef187c2113ae311da9709e056aec8eadb"}
|
{"type":"file","key":"-:2:index.html","value":"a44816e6c3b650bdf88e6532659ba07ef187c2113ae311da9709e056aec8eadb"}
|
||||||
{"type":"file","key":"3:gex_style.css","value":"fc825b409a49724af8f5b3c4ad15e175e68095ea746237a7b46152d3f383f541"}
|
{"type":"file","key":"rustypipe:1:index.html","value":"cc31423924cf1f124750825861ab1ccc675e755921fc2fa111c0a98e8c346a5e"}
|
||||||
{"type":"file","key":"3:index.html","value":"6c5d37546616519e8973be51515b8a90898b4675f7b6d01f2d891edb686408a2"}
|
{"type":"file","key":"rustypipe:1:page2/index.html","value":"be4f409ca0adcb21cdc7130cde63031718406726f889ef97ac8870c90b330a75"}
|
||||||
{"type":"file","key":"4:index.html","value":"cc31423924cf1f124750825861ab1ccc675e755921fc2fa111c0a98e8c346a5e"}
|
{"type":"file","key":"rustypipe:1:rp_style.css","value":"ee4fc4911a56e627c047a29ba3085131939d8d487759b9149d42aaab89ce8993"}
|
||||||
{"type":"file","key":"4:page2/index.html","value":"be4f409ca0adcb21cdc7130cde63031718406726f889ef97ac8870c90b330a75"}
|
{"type":"file","key":"spotify-gender-ex:1:gex_style.css","value":"fc825b409a49724af8f5b3c4ad15e175e68095ea746237a7b46152d3f383f541"}
|
||||||
{"type":"file","key":"4:rp_style.css","value":"ee4fc4911a56e627c047a29ba3085131939d8d487759b9149d42aaab89ce8993"}
|
{"type":"file","key":"spotify-gender-ex:1:index.html","value":"6c5d37546616519e8973be51515b8a90898b4675f7b6d01f2d891edb686408a2"}
|
||||||
|
|
|
@ -11,23 +11,26 @@ expression: "vec![ws1, ws2, ws3]"
|
||||||
visibility: featured,
|
visibility: featured,
|
||||||
source_url: None,
|
source_url: None,
|
||||||
source_icon: None,
|
source_icon: None,
|
||||||
|
vid_count: 2,
|
||||||
),
|
),
|
||||||
Website(
|
Website(
|
||||||
name: "Spotify-Gender-Ex",
|
name: "Spotify-Gender-Ex",
|
||||||
created_at: (2023, 49, 16, 30, 0, 0, 0, 0, 0),
|
created_at: (2023, 49, 16, 30, 0, 0, 0, 0, 0),
|
||||||
latest_version: Some(3),
|
latest_version: Some(1),
|
||||||
color: Some(1947988),
|
color: Some(1947988),
|
||||||
visibility: featured,
|
visibility: featured,
|
||||||
source_url: Some("https://github.com/Theta-Dev/Spotify-Gender-Ex"),
|
source_url: Some("https://github.com/Theta-Dev/Spotify-Gender-Ex"),
|
||||||
source_icon: Some(github),
|
source_icon: Some(github),
|
||||||
|
vid_count: 1,
|
||||||
),
|
),
|
||||||
Website(
|
Website(
|
||||||
name: "RustyPipe",
|
name: "RustyPipe",
|
||||||
created_at: (2023, 51, 18, 30, 0, 0, 0, 0, 0),
|
created_at: (2023, 51, 18, 30, 0, 0, 0, 0, 0),
|
||||||
latest_version: Some(4),
|
latest_version: Some(1),
|
||||||
color: Some(7943647),
|
color: Some(7943647),
|
||||||
visibility: featured,
|
visibility: featured,
|
||||||
source_url: Some("https://code.thetadev.de/ThetaDev/rustypipe"),
|
source_url: Some("https://code.thetadev.de/ThetaDev/rustypipe"),
|
||||||
source_icon: Some(gitea),
|
source_icon: Some(gitea),
|
||||||
|
vid_count: 1,
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -11,32 +11,36 @@ expression: websites
|
||||||
visibility: featured,
|
visibility: featured,
|
||||||
source_url: None,
|
source_url: None,
|
||||||
source_icon: None,
|
source_icon: None,
|
||||||
|
vid_count: 2,
|
||||||
)),
|
)),
|
||||||
("rustypipe", Website(
|
("rustypipe", Website(
|
||||||
name: "RustyPipe",
|
name: "RustyPipe",
|
||||||
created_at: (2023, 51, 18, 30, 0, 0, 0, 0, 0),
|
created_at: (2023, 51, 18, 30, 0, 0, 0, 0, 0),
|
||||||
latest_version: Some(4),
|
latest_version: Some(1),
|
||||||
color: Some(7943647),
|
color: Some(7943647),
|
||||||
visibility: featured,
|
visibility: featured,
|
||||||
source_url: Some("https://code.thetadev.de/ThetaDev/rustypipe"),
|
source_url: Some("https://code.thetadev.de/ThetaDev/rustypipe"),
|
||||||
source_icon: Some(gitea),
|
source_icon: Some(gitea),
|
||||||
|
vid_count: 1,
|
||||||
)),
|
)),
|
||||||
("spa", Website(
|
("spa", Website(
|
||||||
name: "SvelteKit SPA",
|
name: "SvelteKit SPA",
|
||||||
created_at: (2023, 62, 22, 0, 0, 0, 0, 0, 0),
|
created_at: (2023, 62, 22, 0, 0, 0, 0, 0, 0),
|
||||||
latest_version: Some(5),
|
latest_version: Some(1),
|
||||||
color: Some(16727552),
|
color: Some(16727552),
|
||||||
visibility: hidden,
|
visibility: hidden,
|
||||||
source_url: None,
|
source_url: None,
|
||||||
source_icon: None,
|
source_icon: None,
|
||||||
|
vid_count: 1,
|
||||||
)),
|
)),
|
||||||
("spotify-gender-ex", Website(
|
("spotify-gender-ex", Website(
|
||||||
name: "Spotify-Gender-Ex",
|
name: "Spotify-Gender-Ex",
|
||||||
created_at: (2023, 49, 16, 30, 0, 0, 0, 0, 0),
|
created_at: (2023, 49, 16, 30, 0, 0, 0, 0, 0),
|
||||||
latest_version: Some(3),
|
latest_version: Some(1),
|
||||||
color: Some(1947988),
|
color: Some(1947988),
|
||||||
visibility: featured,
|
visibility: featured,
|
||||||
source_url: Some("https://github.com/Theta-Dev/Spotify-Gender-Ex"),
|
source_url: Some("https://github.com/Theta-Dev/Spotify-Gender-Ex"),
|
||||||
source_icon: Some(github),
|
source_icon: Some(github),
|
||||||
|
vid_count: 1,
|
||||||
)),
|
)),
|
||||||
]
|
]
|
||||||
|
|
|
@ -10,4 +10,5 @@ Website(
|
||||||
visibility: hidden,
|
visibility: hidden,
|
||||||
source_url: Some("https://example.com"),
|
source_url: Some("https://example.com"),
|
||||||
source_icon: Some(link),
|
source_icon: Some(link),
|
||||||
|
vid_count: 2,
|
||||||
)
|
)
|
||||||
|
|
|
@ -94,30 +94,29 @@ mod database {
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
fn get_version(db: DbTest) {
|
fn get_version(db: DbTest) {
|
||||||
let version = db.get_version(SUBDOMAIN_1, VERSION_1_1).unwrap();
|
let version = db.get_version(SUBDOMAIN_1, 1).unwrap();
|
||||||
insta::assert_ron_snapshot!(version);
|
insta::assert_ron_snapshot!(version);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
fn delete_version(db: DbTest) {
|
fn delete_version(db: DbTest) {
|
||||||
db.delete_version(SUBDOMAIN_1, VERSION_1_2, true).unwrap();
|
db.delete_version(SUBDOMAIN_1, 2, true).unwrap();
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
db.get_version(SUBDOMAIN_1, VERSION_1_2).unwrap_err(),
|
db.get_version(SUBDOMAIN_1, 2).unwrap_err(),
|
||||||
DbError::NotExists(_, _)
|
DbError::NotExists(_, _)
|
||||||
));
|
));
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
db.delete_version(SUBDOMAIN_1, VERSION_1_2, true)
|
db.delete_version(SUBDOMAIN_1, 2, true).unwrap_err(),
|
||||||
.unwrap_err(),
|
|
||||||
DbError::NotExists(_, _)
|
DbError::NotExists(_, _)
|
||||||
));
|
));
|
||||||
db.delete_version(SUBDOMAIN_1, VERSION_1_2, false).unwrap();
|
db.delete_version(SUBDOMAIN_1, 2, false).unwrap();
|
||||||
|
|
||||||
// Check if files were deleted
|
// Check if files were deleted
|
||||||
assert!(db.get_version_files(VERSION_1_2).next().is_none());
|
assert!(db.get_version_files(SUBDOMAIN_1, 2).next().is_none());
|
||||||
|
|
||||||
// Check if latest version was updated
|
// Check if latest version was updated
|
||||||
let ws = db.get_website(SUBDOMAIN_1).unwrap();
|
let ws = db.get_website(SUBDOMAIN_1).unwrap();
|
||||||
assert_eq!(ws.latest_version, Some(VERSION_1_1));
|
assert_eq!(ws.latest_version, Some(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
|
@ -135,33 +134,34 @@ mod database {
|
||||||
.get_website_version_ids(SUBDOMAIN_1)
|
.get_website_version_ids(SUBDOMAIN_1)
|
||||||
.map(|v| v.unwrap())
|
.map(|v| v.unwrap())
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
assert_eq!(ids, vec![VERSION_1_1, VERSION_1_2]);
|
assert_eq!(ids, vec![1, 2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
fn get_file(db: DbTest) {
|
fn get_file(db: DbTest) {
|
||||||
let hash = db.get_file(VERSION_1_1, "index.html").unwrap();
|
let hash = db.get_file(SUBDOMAIN_1, 1, "index.html").unwrap();
|
||||||
assert_eq!(hash, HASH_1_1_INDEX);
|
assert_eq!(hash, HASH_1_1_INDEX);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
fn delete_file(db: DbTest) {
|
fn delete_file(db: DbTest) {
|
||||||
db.delete_file(VERSION_1_1, "index.html", true).unwrap();
|
db.delete_file(SUBDOMAIN_1, 1, "index.html", true).unwrap();
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
db.get_file(VERSION_1_1, "index.html").unwrap_err(),
|
db.get_file(SUBDOMAIN_1, 1, "index.html").unwrap_err(),
|
||||||
DbError::NotExists(_, _)
|
DbError::NotExists(_, _)
|
||||||
));
|
));
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
db.delete_file(VERSION_1_1, "index.html", true).unwrap_err(),
|
db.delete_file(SUBDOMAIN_1, 1, "index.html", true)
|
||||||
|
.unwrap_err(),
|
||||||
DbError::NotExists(_, _)
|
DbError::NotExists(_, _)
|
||||||
));
|
));
|
||||||
db.delete_file(VERSION_1_1, "index.html", false).unwrap();
|
db.delete_file(SUBDOMAIN_1, 1, "index.html", false).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
fn get_version_files(db: DbTest) {
|
fn get_version_files(db: DbTest) {
|
||||||
let files = db
|
let files = db
|
||||||
.get_version_files(VERSION_1_1)
|
.get_version_files(SUBDOMAIN_1, 1)
|
||||||
.map(|f| f.unwrap())
|
.map(|f| f.unwrap())
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -201,10 +201,10 @@ mod storage {
|
||||||
let temp = temp_testdir::TempDir::default();
|
let temp = temp_testdir::TempDir::default();
|
||||||
let store = Storage::new(temp.to_path_buf(), db_empty.clone(), Default::default());
|
let store = Storage::new(temp.to_path_buf(), db_empty.clone(), Default::default());
|
||||||
|
|
||||||
store.insert_dir(dir, 1).unwrap();
|
store.insert_dir(dir, SUBDOMAIN_1, 1).unwrap();
|
||||||
|
|
||||||
let files = db_empty
|
let files = db_empty
|
||||||
.get_version_files(1)
|
.get_version_files(SUBDOMAIN_1, 1)
|
||||||
.map(|f| f.unwrap())
|
.map(|f| f.unwrap())
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
insta::assert_ron_snapshot!("insert_files", files);
|
insta::assert_ron_snapshot!("insert_files", files);
|
||||||
|
@ -223,11 +223,11 @@ mod storage {
|
||||||
let store = Storage::new(temp.to_path_buf(), db_empty.clone(), Default::default());
|
let store = Storage::new(temp.to_path_buf(), db_empty.clone(), Default::default());
|
||||||
|
|
||||||
store
|
store
|
||||||
.insert_zip_archive(File::open(archive).unwrap(), 1)
|
.insert_zip_archive(File::open(archive).unwrap(), SUBDOMAIN_1, 1)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let files = db_empty
|
let files = db_empty
|
||||||
.get_version_files(1)
|
.get_version_files(SUBDOMAIN_1, 1)
|
||||||
.map(|f| f.unwrap())
|
.map(|f| f.unwrap())
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
insta::assert_ron_snapshot!("insert_files", files);
|
insta::assert_ron_snapshot!("insert_files", files);
|
||||||
|
@ -246,11 +246,11 @@ mod storage {
|
||||||
let store = Storage::new(temp.to_path_buf(), db_empty.clone(), Default::default());
|
let store = Storage::new(temp.to_path_buf(), db_empty.clone(), Default::default());
|
||||||
|
|
||||||
store
|
store
|
||||||
.insert_tgz_archive(File::open(archive).unwrap(), 1)
|
.insert_tgz_archive(File::open(archive).unwrap(), SUBDOMAIN_1, 1)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let files = db_empty
|
let files = db_empty
|
||||||
.get_version_files(1)
|
.get_version_files(SUBDOMAIN_1, 1)
|
||||||
.map(|f| f.unwrap())
|
.map(|f| f.unwrap())
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
insta::assert_ron_snapshot!("insert_files", files);
|
insta::assert_ron_snapshot!("insert_files", files);
|
||||||
|
@ -278,9 +278,9 @@ mod storage {
|
||||||
});
|
});
|
||||||
|
|
||||||
let store = Storage::new(temp.to_path_buf(), db_empty.clone(), cfg);
|
let store = Storage::new(temp.to_path_buf(), db_empty.clone(), cfg);
|
||||||
store.insert_dir(dir, 1).unwrap();
|
store.insert_dir(dir, SUBDOMAIN_1, 1).unwrap();
|
||||||
|
|
||||||
for f in db_empty.get_version_files(1) {
|
for f in db_empty.get_version_files(SUBDOMAIN_1, 1) {
|
||||||
let hash = f.unwrap().1;
|
let hash = f.unwrap().1;
|
||||||
let hash_str = hash.encode_hex::<String>();
|
let hash_str = hash.encode_hex::<String>();
|
||||||
let path = temp.join(&hash_str[..2]).join(&hash_str);
|
let path = temp.join(&hash_str[..2]).join(&hash_str);
|
||||||
|
@ -297,15 +297,16 @@ mod storage {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
#[case::index("br", VERSION_1_2, "", false, "text/html", None)]
|
#[case::index("br", SUBDOMAIN_1, 2, "", false, "text/html", None)]
|
||||||
#[case::nocmp("", VERSION_1_2, "assets/style.css", true, "text/css", None)]
|
#[case::nocmp("", SUBDOMAIN_1, 2, "assets/style.css", true, "text/css", None)]
|
||||||
#[case::gzip("gzip", VERSION_1_2, "assets/style.css", true, "text/css", None)]
|
#[case::gzip("gzip", SUBDOMAIN_1, 2, "assets/style.css", true, "text/css", None)]
|
||||||
#[case::br("br", VERSION_1_2, "assets/style.css", true, "text/css", None)]
|
#[case::br("br", SUBDOMAIN_1, 2, "assets/style.css", true, "text/css", None)]
|
||||||
#[case::image("br", VERSION_1_2, "assets/image.jpg", false, "image/jpeg", None)]
|
#[case::image("br", SUBDOMAIN_1, 2, "assets/image.jpg", false, "image/jpeg", None)]
|
||||||
#[case::subdir("br", VERSION_3_1, "page2", false, "text/html", Some("/page2/"))]
|
#[case::subdir("br", SUBDOMAIN_3, 1, "page2", false, "text/html", Some("/page2/"))]
|
||||||
fn get_file(
|
fn get_file(
|
||||||
tln: TalonTest,
|
tln: TalonTest,
|
||||||
#[case] encoding: &str,
|
#[case] encoding: &str,
|
||||||
|
#[case] subdomain: &str,
|
||||||
#[case] version: u32,
|
#[case] version: u32,
|
||||||
#[case] path: &str,
|
#[case] path: &str,
|
||||||
#[case] compressible: bool,
|
#[case] compressible: bool,
|
||||||
|
@ -325,7 +326,10 @@ mod storage {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let index_file = tln.storage.get_file(version, path, &headers).unwrap();
|
let index_file = tln
|
||||||
|
.storage
|
||||||
|
.get_file(subdomain, version, path, &headers)
|
||||||
|
.unwrap();
|
||||||
dbg!(&index_file);
|
dbg!(&index_file);
|
||||||
assert!(index_file.file_path.is_file());
|
assert!(index_file.file_path.is_file());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -620,38 +624,43 @@ mod page {
|
||||||
#[case] ok: bool,
|
#[case] ok: bool,
|
||||||
#[case] hash: &[u8],
|
#[case] hash: &[u8],
|
||||||
) {
|
) {
|
||||||
let vid = tln.db.new_version_id().unwrap();
|
const SUBDOMAIN: &str = "fallback";
|
||||||
|
|
||||||
tln.db
|
tln.db
|
||||||
.insert_website(
|
.insert_website(
|
||||||
"fallback",
|
SUBDOMAIN,
|
||||||
&talon::db::model::Website {
|
&talon::db::model::Website {
|
||||||
latest_version: Some(vid),
|
latest_version: Some(1),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
tln.db
|
tln.db
|
||||||
.insert_version(
|
.insert_version(
|
||||||
"fallback",
|
SUBDOMAIN,
|
||||||
vid,
|
|
||||||
&talon::db::model::Version {
|
&talon::db::model::Version {
|
||||||
fallback,
|
fallback,
|
||||||
spa,
|
spa,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap(),
|
||||||
|
1
|
||||||
|
);
|
||||||
tln.storage
|
tln.storage
|
||||||
.insert_file(
|
.insert_file(
|
||||||
path!("tests" / "testfiles" / "ThetaDev0" / "index.html"),
|
path!("tests" / "testfiles" / "ThetaDev0" / "index.html"),
|
||||||
vid,
|
SUBDOMAIN,
|
||||||
|
1,
|
||||||
"index.html",
|
"index.html",
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
tln.storage
|
tln.storage
|
||||||
.insert_file(
|
.insert_file(
|
||||||
path!("tests" / "testfiles" / "ThetaDev1" / "index.html"),
|
path!("tests" / "testfiles" / "ThetaDev1" / "index.html"),
|
||||||
vid,
|
SUBDOMAIN,
|
||||||
|
1,
|
||||||
"fallback.html",
|
"fallback.html",
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
6783
ui/menu/package-lock.json
generated
6783
ui/menu/package-lock.json
generated
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue