Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prototype admin console #6353

Closed
wants to merge 11 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .ember-cli
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@

Setting `disableAnalytics` to true will prevent any data from being sent.
*/
"disableAnalytics": false
"disableAnalytics": false,
"port": 4201
}
4 changes: 4 additions & 0 deletions .env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,7 @@ export GH_CLIENT_SECRET=
# Credentials for connecting to the Sentry error reporting service.
# export SENTRY_DSN_API=
export SENTRY_ENV_API=local

# GitHub users that are admins on this instance, separated by commas. Whitespace
# will be ignored.
export GH_ADMIN_USERS=
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Haven't taken a look at the whole PR, but please use GitHub IDs rather than usernames. Usernames can change and the old username can be taken over by another user.

8 changes: 8 additions & 0 deletions Caddyfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
http://localhost:4200, http://127.0.0.1:4200 {
@backend {
path /api/* /admin/* /git/*
}

reverse_proxy @backend 127.0.0.1:8888
reverse_proxy 127.0.0.1:4201
}
66 changes: 66 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ anyhow = "=1.0.70"
aws-sigv4 = "=0.55.1"
axum = { version = "=0.6.16", features = ["headers", "macros", "matched-path"] }
axum-extra = { version = "=0.7.4", features = ["cookie-signed"] }
axum-template = { version = "0.15.1", features = ["handlebars"] }
base64 = "=0.13.1"
cargo-registry-index = { path = "cargo-registry-index" }
cargo-registry-markdown = { path = "cargo-registry-markdown" }
Expand All @@ -43,14 +44,15 @@ dotenv = "=0.15.0"
flate2 = "=1.0.25"
futures-channel = { version = "=0.3.28", default-features = false }
futures-util = "=0.3.28"
handlebars = { version = "4.3.6", features = ["dir_source", "rust-embed"] }
hex = "=0.4.3"
http = "=0.2.9"
http-body = "=0.4.5"
hyper = { version = "=0.14.26", features = ["backports", "client", "deprecated", "http1"] }
indexmap = { version = "=1.9.3", features = ["serde-1"] }
indicatif = "=0.17.3"
ipnetwork = "=0.20.0"
tikv-jemallocator = { version = "=0.5.0", features = ['unprefixed_malloc_on_supported_platforms', 'profiling'] }
lazy_static = "1.4.0"
lettre = { version = "=0.10.4", default-features = false, features = ["file-transport", "smtp-transport", "native-tls", "hostname", "builder"] }
minijinja = "=0.31.1"
moka = { version = "=0.10.2", features = ["future"] }
Expand All @@ -62,6 +64,7 @@ rand = "=0.8.5"
reqwest = { version = "=0.11.16", features = ["blocking", "gzip", "json"] }
retry = "=2.0.0"
ring = "=0.16.20"
rust-embed = { version = "6.6.1" }
scheduled-thread-pool = "=0.2.7"
semver = { version = "=1.0.17", features = ["serde"] }
sentry = { version = "=0.30.0", features = ["tracing", "tower", "tower-http"] }
Expand All @@ -73,6 +76,7 @@ tar = "=0.4.38"
tempfile = "=3.5.0"
thiserror = "=1.0.40"
threadpool = "=1.8.1"
tikv-jemallocator = { version = "=0.5.0", features = ['unprefixed_malloc_on_supported_platforms', 'profiling'] }
tokio = { version = "=1.27.0", features = ["net", "signal", "io-std", "io-util", "rt-multi-thread", "macros"]}
toml = "=0.7.3"
tower = "=0.4.13"
Expand Down
1 change: 1 addition & 0 deletions admin/templates/components/datetime.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<span title='{{this.absolute}}'>{{this.human}}</span>
27 changes: 27 additions & 0 deletions admin/templates/components/page.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{{#if this.paginated}}
<nav>
<ul class="pagination">
{{#if this.previous}}
<li class="page-item">
<a class="page-link" href="{{ this.previous }}">
<span>&laquo;</span>
</a>
</li>
{{/if}}
{{#each this.pages}}
<li class="page-item">
<a class="page-link {{#if this.active}}active{{/if}}" href="{{ this.url }}">
<span>{{ this.number }}</span>
</a>
</li>
{{/each}}
{{#if this.next}}
<li class="page-item">
<a class="page-link" href="{{ this.next }}">
<span>&raquo;</span>
</a>
</li>
{{/if}}
</ul>
</nav>
{{/if}}
13 changes: 13 additions & 0 deletions admin/templates/components/user.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<a href='/users/{{this.username}}'>
{{#if this.avatar}}
<img
src='{{this.avatar}}'
alt='{{this.username}}'
aria-hidden='true'
width='32'
height='32'
class='d-inline-block me-1'
/>
{{~/if~}}
{{~this.username}}
</a>
97 changes: 97 additions & 0 deletions admin/templates/crates.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
{{#*inline "body"}}
<div class="d-flex align-items-end mt-3 mb-3">
<h1 class="me-auto">{{> title}}</h1>
<form class="d-flex align-items-center" method="GET">
<input class="form-control" name="q" type="search" placeholder="crate name" spellcheck="false" value="{{this.q}}">
</form>
</div>

<table class="table align-middle">
<thead>
<tr>
<th>Crate</th>
<th>Version</th>
<th>Published</th>
<th/>
<tr>
</thead>
<tbody>
{{#each this.versions}}
<tr>
<td>
<div>
<a href="/crates/{{this.name}}">
{{#if this.yanked}}
<s>{{this.name}}</s>
{{~else}}
{{this.name}}
{{/if~}}
</a>
{{#if this.yanked}}<i title="Yanked" class="bi bi-trash-fill"></i>{{/if}}
</div>
<div style="font-size: 80%"><a href="https://index.crates.io/{{crate-index-path this.name}}">View in sparse index</a></div>
</td>
<td>{{this.num}}</td>
<td>
<em>{{> components/datetime this.created_at}}</em>
by
{{> components/user this.publisher }}
</td>
<td class="text-end">
{{#if this.yanked}}
<button type="button" class="btn btn-outline-primary unyank" data-unyank-name="{{this.name}}" data-unyank-version="{{this.num}}">Unyank</button>
{{else}}
<button type="button" class="btn btn-danger yank" data-yank-name="{{this.name}}" data-yank-version="{{this.num}}">Yank</button>
{{/if}}
</td>
</tr>
{{/each}}
</tbody>
</table>

{{> components/page this.page}}

<script>
window.addEventListener("DOMContentLoaded", () => {
document.querySelectorAll("[data-yank-name]").forEach(btn => {
const name = btn.getAttribute("data-yank-name");
const version = btn.getAttribute("data-yank-version");

btn.addEventListener("click", async event => {
event.preventDefault();
if (confirm(`Are you sure you want to yank ${name} ${version}?`)) {
btn.disabled = true;
btn.innerHTML = "<div class='spinner-border' role='status'></div>";

try {
const response = await fetch(`/api/v1/crates/${name}/${version}/yank`, {
method: "DELETE",
});

if (response.status >= 400){
throw `Unexpected status code: ${response.status}`;
}

// Errors can also be returned as a top level errors field.
const data = await response.json();
if ("errors" in data) {
throw data.errors.map(error => error.detail).join("; ");
}

window.location.reload();
} catch (e) {
btn.innerHTML = `<span data-bs-toggle="tooltip"><i class="bi bi-exclamation-octagon-fill"></i> Error</span>`;
const error = document.createElement("div");
error.innerText = e.toString();
btn.parentElement.appendChild(error);
}
}
});
});
});
</script>
{{/inline}}

{{#*inline "title"}}Crates{{/inline}}

{{> page}}
31 changes: 31 additions & 0 deletions admin/templates/page.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{> title}} :: crates.io</title>
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-KK94CHFLLe+nY2dmCWGMq91rCGa5gtU4mk92HdvYe+M/SXH301p5ILy+dN9+nJOZ" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/font/bootstrap-icons.css" integrity="sha384-LrVLJJYk9OiJmjNDakUBU7kS9qCT8wk1j2OU7ncpsfB3QS37UPdkCuq3ZD1MugNY" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-ENjdO4Dr2bkBIFxQpeoTz1HIcje39Wm4jDKdf19U8gI4ddQ3GYNS7NTKfAdVQSZe" crossorigin="anonymous"></script>
</head>
<body>
<nav class="navbar sticky-top navbar-expand-lg bg-dark" data-bs-theme="dark">
<div class="container-fluid">
<a class="navbar-brand" href="/admin/">crates.io admin</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link" href="/admin/crates/">Recent uploads</a>
</li>
</ul>
</div>
</div>
</nav>
<div class="container">
{{> body}}
</div>
</body>
</html>
3 changes: 3 additions & 0 deletions app/components/header.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@
<menu.Item><LinkTo @route="dashboard">Dashboard</LinkTo></menu.Item>
<menu.Item><LinkTo @route="settings">Account Settings</LinkTo></menu.Item>
<menu.Item><LinkTo @route="me.pending-invites">Owner Invites</LinkTo></menu.Item>
{{#if this.session.currentUser.admin }}
<menu.Item><a href="/admin/">Site Admin</a></menu.Item>
{{/if}}
<menu.Item local-class="menu-item-with-separator">
<button
type="button"
Expand Down
1 change: 1 addition & 0 deletions app/models/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export default class User extends Model {
@attr avatar;
@attr url;
@attr kind;
@attr admin;

async stats() {
return await apiAction(this, { method: 'GET', path: 'stats' });
Expand Down
4 changes: 4 additions & 0 deletions config/nginx.conf.erb
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,10 @@ http {
<% if ENV['USE_FASTBOOT'] == "staging-experimental" %>
# Experimentally send all non-backend requests to FastBoot

location /admin/ {
proxy_pass http://app_server;
}

location /api/ {
proxy_pass http://app_server;
}
Expand Down
Loading