add store info display // all blob listing
parent
f653ed0cf9
commit
583fea153c
|
@ -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::*;
|
||||
|
||||
|
|
|
@ -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>,
|
||||
|
|
|
@ -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 }[];
|
||||
}[];
|
||||
}
|
||||
|
|
|
@ -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 />
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in New Issue