add store info display // all blob listing

feat/vaults
Tomáš Mládek 2022-02-21 21:27:46 +01:00
parent f653ed0cf9
commit 583fea153c
8 changed files with 182 additions and 24 deletions

View File

@ -252,17 +252,6 @@ impl UpEndConnection {
Ok(matches)
}
pub fn get_latest_files(&self, count: i64) -> Result<Vec<models::File>> {
use crate::database::inner::schema::files::dsl::*;
let matches = files
.order_by(added.desc())
.limit(count)
.load::<models::File>(&self.conn)?;
Ok(matches)
}
pub fn file_update_mtime(&self, file_id: i32, m_time: Option<NaiveDateTime>) -> Result<usize> {
use crate::database::inner::schema::files::dsl::*;

View File

@ -594,7 +594,48 @@ pub async fn api_refresh(
Ok(HttpResponse::Ok().finish())
}
#[get("/api/files/{hash}")]
#[get("/api/store")]
pub async fn latest_files(state: web::Data<State>) -> Result<HttpResponse, Error> {
let connection = state.upend.connection().map_err(ErrorInternalServerError)?;
let files = connection
.retrieve_all_files()
.map_err(ErrorInternalServerError)?;
let mut files_by_hash = HashMap::new();
for file in &files {
if !files_by_hash.contains_key(&file.hash) {
files_by_hash.insert(&file.hash, vec![]);
}
files_by_hash.get_mut(&file.hash).unwrap().push(file);
}
let mut blobs = files_by_hash
.iter()
.map(|(hash, files)| {
json!({
"hash": hash,
"size": files[0].size,
"paths": files.iter().map(|f| json!({
"added": f.added,
"valid": f.valid,
"path": f.path
})).collect::<serde_json::Value>()
})
})
.collect::<Vec<serde_json::Value>>();
blobs.sort_unstable_by_key(|f| f["size"].as_u64().unwrap());
blobs.reverse();
Ok(HttpResponse::Ok().json(json!({
"totals": {
"count": files_by_hash.len(),
"size": files_by_hash.iter().map(|(_, f)| f[0].size as u64).sum::<u64>()
},
"blobs": blobs
})))
}
#[get("/api/store/{hash}")]
pub async fn get_file(
state: web::Data<State>,
hash: web::Path<String>,
@ -615,15 +656,6 @@ pub async fn get_file(
}
}
#[get("/api/files/latest")]
pub async fn latest_files(state: web::Data<State>) -> Result<HttpResponse, Error> {
let connection = state.upend.connection().map_err(ErrorInternalServerError)?;
let files = connection
.get_latest_files(100)
.map_err(ErrorInternalServerError)?;
Ok(HttpResponse::Ok().json(&files))
}
#[derive(Deserialize)]
pub struct JobsRequest {
full: Option<String>,

View File

@ -74,3 +74,12 @@ export interface VaultInfo {
version: string;
desktop: boolean;
}
export interface StoreInfo {
totals: { count: number; size: number };
blobs: {
hash: string;
size: number;
paths: { added: number; valid: boolean; path: string }[];
}[];
}

View File

@ -8,6 +8,7 @@
import Search from "./views/Search.svelte";
import DropPasteHandler from "./components/DropPasteHandler.svelte";
import AddModal from "./components/AddModal.svelte";
import Store from "./views/Store.svelte";
const history = createHistory(createHashSource());
@ -32,6 +33,10 @@
<Search query={decodeURIComponent(params.query)} />
</Route>
<Route path="/store">
<Store />
</Route>
<Footer on:resize={setBottomMargin} />
<AddModal />

View File

@ -7,7 +7,8 @@ import type {
IValue,
ListingResult,
PutResult,
VaultInfo
VaultInfo,
StoreInfo,
} from "upend/types";
import type { EntityListing } from "./entity";
@ -125,3 +126,8 @@ export async function fetchInfo(): Promise<VaultInfo> {
const response = await fetch("api/info");
return await response.json();
}
export async function fetchStoreInfo(): Promise<StoreInfo> {
const response = await fetch("api/store");
return await response.json();
}

View File

@ -1,4 +1,5 @@
<script lang="ts">
import { Link } from "svelte-navigator";
import { UpListing } from "upend";
import AttributeView from "../components/AttributeView.svelte";
import UpObjectCard from "../components/display/UpObjectCard.svelte";
@ -88,6 +89,10 @@
</section>
{/if}
<div class="button store-button">
<Link to="/store">View store statistics</Link>
</div>
<footer>
<div>
<strong>UpEnd</strong> - a database for the complex, the changing, and the
@ -102,6 +107,11 @@
</div>
<style lang="scss">
.home {
display: flex;
flex-direction: column;
}
h1,
h2 {
text-align: center;
@ -136,4 +146,10 @@
margin: 0.5em;
}
}
.store-button {
display: inline-block;
padding: 1em;
margin: auto;
}
</style>

View File

@ -0,0 +1,101 @@
<script lang="ts">
import filesize from "filesize";
import UpObject from "../components/display/UpObject.svelte";
import Icon from "../components/utils/Icon.svelte";
import Spinner from "../components/utils/Spinner.svelte";
import { fetchStoreInfo } from "../lib/api";
const store = fetchStoreInfo();
</script>
<div class="store">
<h1>Store</h1>
{#await store}
<Spinner />
{:then store}
<div class="totals">
<strong>{store.totals.count}</strong> blobs,
<strong>{filesize(store.totals.size)}</strong>
</div>
<table>
<tr>
<th>Hash</th>
<th>Size</th>
<th>Path</th>
<th>Added</th>
<th>Valid</th>
</tr>
{#each store.blobs as blob}
<tbody>
<tr>
<td class="hash"
><UpObject link address={blob.hash} labels={[]} /></td
>
<td class="size">{filesize(blob.size)}</td>
<td class="path">{blob.paths[0].path}</td>
<td class="added">{blob.paths[0].added}</td>
<td class="valid">
{#if blob.paths[0].valid}
<Icon name="check" />
{:else}
<Icon name="x" />
{/if}
</td>
</tr>
{#each blob.paths.slice(1) as path}
<tr>
<td />
<td />
<td class="path">{path.path}</td>
<td class="added">{path.added}</td>
<td class="valid">
{#if path.valid}
<Icon name="check" />
{:else}
<Icon name="x" />
{/if}
</td>
</tr>
{/each}
</tbody>
{/each}
</table>
{:catch error}
<div class="error">
{error}
</div>
{/await}
</div>
<style lang="scss">
.store {
text-align: center;
}
.totals,
th {
font-size: larger;
}
table {
border-spacing: 1em 0.25em;
margin: 1em;
text-align: initial;
.size,
.valid {
text-align: center;
}
.size {
white-space: nowrap;
}
tbody:nth-child(odd) {
font-weight: 300;
}
tbody:nth-child(even) {
font-weight: 500;
}
}
</style>

View File

@ -4669,8 +4669,8 @@ __metadata:
"upend@file:../tools/upend_js::locator=svelte-app%40workspace%3A.":
version: 0.0.1
resolution: "upend@file:../tools/upend_js#../tools/upend_js::hash=23ae93&locator=svelte-app%40workspace%3A."
checksum: dadd19aa2ae0275289b011bbb46d3881515cef8b064441791f846abb80ce8fe33f7b9f4d51bb9dd164bf010f93f9de9cd1f12e8fa881544b5e2cccce125feb16
resolution: "upend@file:../tools/upend_js#../tools/upend_js::hash=a988d5&locator=svelte-app%40workspace%3A."
checksum: 11b26f7703c0c8e750b7c4667dd025d8eee1964eee1ae9a11e1b3038bda34bc28053dc5b6c31a780857b4f884d872a425a2b19c7a20ce8dbd87997b51c94fd60
languageName: node
linkType: hard