Clean up SSH keys page
Diff
Cargo.lock | 8 ++++++++
chartered-db/Cargo.toml | 2 ++
chartered-db/src/crates.rs | 14 ++++++++++----
chartered-db/src/users.rs | 23 ++++++++++++++++++++++-
chartered-frontend/src/index.sass | 4 ++++
chartered-web/src/main.rs | 1 -
chartered-frontend/src/pages/SingleCrate.tsx | 123 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------
chartered-frontend/src/pages/ssh-keys/ListSshKeys.tsx | 35 ++++++++++++++++++++++++++---------
chartered-web/src/endpoints/web_api/crate_info.rs | 5 ++++-
9 files changed, 142 insertions(+), 73 deletions(-)
@@ -92,6 +92,12 @@
]
[[package]]
name = "base64"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
[[package]]
name = "base64ct"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -186,6 +192,7 @@
name = "chartered-db"
version = "0.1.0"
dependencies = [
"base64",
"bitflags",
"chartered-fs",
"chartered-types",
@@ -193,6 +200,7 @@
"diesel",
"displaydoc",
"dotenv",
"hex",
"itertools",
"rand",
"serde",
@@ -9,10 +9,12 @@
chartered-fs = { path = "../chartered-fs" }
chartered-types = { path = "../chartered-types" }
base64 = "0.13"
bitflags = "1"
chrono = "0.4"
diesel = { version = "1", features = ["sqlite", "r2d2", "chrono"] }
displaydoc = "0.2"
hex = "0.4"
itertools = "0.10"
rand = "0.8"
serde = { version = "1", features = ["derive"] }
@@ -37,7 +37,13 @@
impl<'a> CrateVersion<'a> {
#[must_use]
pub fn into_cargo_format(self, crate_: &'a Crate) -> (chartered_types::cargo::CrateVersion<'a>, chartered_types::cargo::CrateVersionMetadata) {
pub fn into_cargo_format(
self,
crate_: &'a Crate,
) -> (
chartered_types::cargo::CrateVersion<'a>,
chartered_types::cargo::CrateVersionMetadata,
) {
(
chartered_types::cargo::CrateVersion {
name: crate_.name.as_str().into(),
@@ -52,7 +58,7 @@
repository: self.repository,
homepage: self.homepage,
documentation: self.documentation,
}
},
)
}
}
@@ -183,8 +189,8 @@
metadata: chartered_types::cargo::CrateVersionMetadata,
) -> Result<()> {
use crate::schema::crate_versions::dsl::{
checksum, crate_id, crate_versions, dependencies, features, filesystem_object, links,
version, description, readme, repository, homepage, documentation,
checksum, crate_id, crate_versions, dependencies, description, documentation, features,
filesystem_object, homepage, links, readme, repository, version,
};
tokio::task::spawn_blocking(move || {
@@ -84,8 +84,7 @@
let mut split = ssh_key.split_whitespace();
let key = match (split.next(), split.next()) {
(Some(_), Some(key)) => key,
(Some(key), None) => key,
(Some(_), Some(key)) | (Some(key), None) => key,
_ => return Err(thrussh_keys::Error::CouldNotReadKey.into()),
};
@@ -152,7 +151,25 @@
(
id,
thrussh_keys::key::parse_public_key(&key)
.map(|v| v.fingerprint())
.map_err(|e| e.into())
.and_then(|v| {
let raw_hex = hex::encode(
base64::decode(&v.fingerprint())
.map_err(|_| thrussh_keys::Error::CouldNotReadKey)?,
);
let mut hex =
String::with_capacity(raw_hex.len() + (raw_hex.len() / 2 - 1));
for (i, c) in raw_hex.chars().enumerate() {
if i != 0 && i % 2 == 0 {
hex.push(':');
}
hex.push(c);
}
Ok::<_, crate::Error>(hex)
})
.unwrap_or_else(|e| format!("INVALID: {}", e)),
)
})
@@ -15,3 +15,7 @@
background: #f5f2f0
padding: .1rem .2rem
border-radius: 3px
td.fit, th.fit
width: 0.1%
white-space: nowrap
@@ -12,7 +12,6 @@
use tower::ServiceBuilder;
use tower_http::cors::{Any, CorsLayer};
#[allow(clippy::unused_async)]
async fn hello_world() -> &'static str {
"hello, world!"
@@ -157,58 +157,77 @@
}
function Members(props: { crateInfo: any }) {
return <div className="grid" style={{ gridTemplateColumns: '1fr 1fr 1fr' }}>
<div className="g-col-4 g-col-lg-1 d-flex align-items-center">
<img src="http://placekitten.com/96/96" className="rounded-circle" />
<div className="ms-2">
<strong>Johnny Davidson</strong> <em>(that's you!)</em><br />
Owner
</div>
</div>
<div className="g-col-4 g-col-lg-1 d-flex align-items-center mt-2">
<img src="http://placekitten.com/96/96" className="rounded-circle" />
<div className="ms-2">
<strong>Will Woodwood</strong><br />
<select className="form-select form-select-sm" aria-label="Default select example">
<option value="1">Consumer</option>
<option value="2">Maintainer</option>
<option value="3">Owner</option>
</select>
</div>
</div>
<div className="g-col-4 g-col-lg-1 d-flex align-items-center mt-2">
<img src="http://placekitten.com/96/96" className="rounded-circle" />
<div className="ms-2">
<strong>Ben Dover</strong><br />
<select className="form-select form-select-sm" aria-label="Default select example">
<option value="1">Consumer</option>
<option value="2" selected>Maintainer</option>
<option value="3">Owner</option>
</select>
</div>
</div>
<div className="g-col-4 g-col-lg-1 d-flex align-items-center mt-2">
<img src="http://placekitten.com/96/96" className="rounded-circle" />
<div className="ms-2">
<strong>Eline Dover</strong><br />
<select className="form-select form-select-sm" aria-label="Default select example">
<option value="1">Consumer</option>
<option value="2">Maintainer</option>
<option value="3" selected>Owner</option>
</select>
</div>
</div>
<div className="g-col-4 g-col-lg-1 mt-2 d-flex align-items-center justify-content-center rounded-circle"
style={{ width: '96px', height: '96px', background: '#DEDEDE', fontSize: '2rem' }}>
<PersonPlus />
const x = ["John Paul", "David Davidson", "Andrew Smith"];
return <div className="container-fluid g-0">
<div className="table-responsive">
<table className="table table-striped">
<tbody>
{x.map(v =>
<tr key={v}>
<td className="align-middle fit">
<img src="http://placekitten.com/48/48" className="rounded-circle" />
</td>
<td className="align-middle">
<strong>{v}</strong><br />
<em>(that's you!)</em>
</td>
<td className="align-middle">
<div className="d-flex">
<div>
<div className="form-check">
<input className="form-check-input" type="checkbox" value="" id="visible" />
<label className="form-check-label" htmlFor="visible">
Visible
</label>
</div>
<div className="form-check">
<input className="form-check-input" type="checkbox" value="" id="publish_version" />
<label className="form-check-label" htmlFor="visible">
Publish Version
</label>
</div>
</div>
<div className="ms-3">
<div className="form-check">
<input className="form-check-input" type="checkbox" value="" id="visible" />
<label className="form-check-label" htmlFor="visible">
Yank Version
</label>
</div>
<div className="form-check">
<input className="form-check-input" type="checkbox" value="" id="publish_version" />
<label className="form-check-label" htmlFor="visible">
Manage Users
</label>
</div>
</div>
</div>
</td>
</tr>
)}
<tr>
<td className="align-middle fit">
<div
className="d-flex align-items-center justify-content-center rounded-circle"
style={{ width: '48px', height: '48px', background: '#DEDEDE', fontSize: '1rem' }}
>
<PersonPlus />
</div>
</td>
<td></td>
<td></td>
</tr>
</tbody>
</table>
</div>
</div>;
}
@@ -65,18 +65,29 @@
</div>
<div className="card border-0 shadow-sm text-black">
<ul className="list-group list-group-flush">
{sshKeys.keys.map(key => (
<li key={key.id} className="list-group-item">
<div className="d-flex">
<div className="flex-grow-1"><strong>{key.fingerprint}</strong></div>
<div>
<button type="button" className="btn text-danger" onClick={() => setDeleting(key)}><Trash /></button>
</div>
</div>
</li>
))}
</ul>
<div className="table-responsive">
<table className="table table-striped">
<tbody>
{sshKeys.keys.map(key => (
<tr key={key.id}>
<td className="align-middle">
<pre className="m-0">{key.fingerprint}</pre>
<div className="lh-sm" style={{ fontSize: '.75rem' }}>
<div>Added on 10 May 2016</div>
<div className="text-success">Last used within the last week</div>
</div>
</td>
<td className="align-middle fit">
<button type="button" className="btn text-danger" onClick={() => setDeleting(key)}>
<Trash />
</button>
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
<Link to="/ssh-keys/add" className="btn btn-outline-light mt-2 float-end"><Plus /> Add New</Link>
@@ -50,7 +50,10 @@
.into_iter()
.map(|v| {
let (inner, meta) = v.into_cargo_format(&crate_);
ResponseVersion { inner: inner.into_owned(), meta }
ResponseVersion {
inner: inner.into_owned(),
meta,
}
})
.collect(),
}))