Compare commits

..

2 commits

Author SHA1 Message Date
096d44ae59 add remove 2023-11-25 01:38:56 +01:00
ddb4ec4a5c css refactor 2023-11-25 01:29:19 +01:00
10 changed files with 248 additions and 141 deletions

View file

@ -1,5 +1,5 @@
use anyhow::{anyhow, Result};
use git2::{ErrorCode, IndexAddOption, Repository, Signature};
use git2::{ErrorCode, Index, IndexAddOption, Oid, Repository, Signature};
use std::path::{Path, PathBuf};
use tracing::info;
@ -40,9 +40,6 @@ impl ThreadSafeRepository {
index.add_all(["*/*.nix"].iter(), IndexAddOption::DEFAULT, None)?;
let oid = index.write_tree()?;
let tree = repo.find_tree(oid)?;
// This prevents a nasty condition where the index goes all wack,
// but it's probably a mistake somewhere else
index.write()?;
let signature = Signature::now(&config.author_name, &config.author_email)?;
@ -71,24 +68,15 @@ impl ThreadSafeRepository {
Repository::open(&self.path)
}
pub fn commit_files(&self, paths: &[&Path], message: &str) -> Result<()> {
let repository = self.repository()?;
fn commit(&self, repository: Repository, mut index: Index, message: &str) -> Result<Oid> {
index.write()?;
// Commit file
let mut index = repository.index()?;
for path in paths {
index.add_path(path)?;
}
let oid = index.write_tree()?;
let tree = repository.find_tree(oid)?;
let head = repository.head()?;
// This prevents a nasty condition where the index goes all wack,
// but it's probably a mistake somewhere else
index.write()?;
let signature = Signature::now(&self.config.author_name, &self.config.author_email)?;
repository.commit(
let oid = repository.commit(
Some("HEAD"),
&signature,
&signature,
@ -98,6 +86,32 @@ impl ThreadSafeRepository {
)?;
drop(tree);
Ok(oid)
}
pub fn commit_files(&self, paths: &[&Path], message: &str) -> Result<()> {
let repository = self.repository()?;
// Commit file
let mut index = repository.index()?;
for path in paths {
index.add_path(path)?;
}
self.commit(repository, index, message)?;
Ok(())
}
pub fn remove_folder(&self, path: &Path, message: &str) -> Result<()> {
let repository = self.repository()?;
// Commit file
let mut index = repository.index()?;
index.remove_dir(path, 0)?;
self.commit(repository, index, message)?;
Ok(())
}
}

View file

@ -186,6 +186,27 @@ pub async fn create_new(
Ok(())
}
pub async fn remove(
base_dir: &Path,
arion_bin: &Path,
repository: ThreadSafeRepository,
stack_name: &str,
) -> Result<()> {
// Remove all containers and resources
command(base_dir, stack_name, arion_bin, StackCommand::Down).await?;
// Remove from repository
repository.remove_folder(
&PathBuf::from(stack_name),
format!("Removed stack {}", stack_name).as_str(),
)?;
// Remove from disk
fs::remove_dir_all(repository.path.join(stack_name)).await?;
Ok(())
}
pub async fn check_compose(arion_bin: &Path, source: &str) -> Result<()> {
// Check that it's a valid nix tree
rnix::Root::parse(source)

View file

@ -7,7 +7,7 @@ use crate::{
container::ContainerInfo,
stack::{
check_compose, command, commit_compose, create_new, get_compose, get_containers,
write_compose, StackCommand,
remove, write_compose, StackCommand,
},
},
AppState,
@ -37,6 +37,12 @@ struct GetOneTemplate {
#[template(path = "stack/new-form.html")]
struct CreateTemplate {}
#[derive(Template)]
#[template(path = "stack/delete-one.html")]
struct ConfirmDeleteTemplate {
stack_name: String,
}
async fn get_one(Path(stack_name): Path<String>, State(state): State<AppState>) -> HandlerResponse {
let (file_contents_res, containers_res) = join!(
get_compose(&state.stack_dir, &stack_name),
@ -139,6 +145,24 @@ async fn check_stack_file(
Ok(StatusCode::NO_CONTENT)
}
async fn confirm_deletion_page(Path(stack_name): Path<String>) -> impl IntoResponse {
ConfirmDeleteTemplate { stack_name }
}
async fn delete_stack(
Path(stack_name): Path<String>,
State(state): State<AppState>,
) -> Result<Redirect, AppError> {
remove(
&state.stack_dir,
&state.arion_bin,
state.repository,
&stack_name,
)
.await?;
Ok(Redirect::to("/"))
}
macro_rules! stack_command {
($cmd: expr) => {
move |Path(stack_name): Path<String>, State(state): State<AppState>| async move {
@ -167,4 +191,8 @@ pub(super) fn router() -> Router<AppState> {
.route("/:stack/stop", post(stack_command!(StackCommand::Stop)))
.route("/:stack/down", post(stack_command!(StackCommand::Down)))
.route("/:stack/edit", post(edit_stack))
.route(
"/:stack/delete",
get(confirm_deletion_page).post(delete_stack),
)
}

View file

@ -21,8 +21,8 @@
& .error {
border-radius: 3px;
background-color: #500F1C;
color: #FF9592;
background-color: var(--danger-bg);
color: var(--danger-text);
padding: 4px 8px;
display: none;
}
@ -35,14 +35,14 @@
.ace_editor {
min-height: 50vh;
border: 3px solid #5958B1;
border: var(--table-border);
border-radius: 3px;
&.checked {
border-color: #46A758;
border-color: var(--success-bright);
}
&.err {
border-color: #E5484D;
border-color: var(--danger-bright);
}
}

107
static/css/resource.css Normal file
View file

@ -0,0 +1,107 @@
.actions {
display: flex;
gap: 0.5rem;
& form {
display: flex;
min-width: 80px;
}
& button {
flex: 1;
&.danger {
background-color: var(--danger-bg);
border-color: var(--danger-bright);
color: var(--danger-bright);
&:hover {
border-color: var(--link);
color: var(--link);
background-color: var(--danger-bright);
}
}
}
&.big button {
font-size: 14pt;
padding: 8px 20px;
}
}
dd~dt,
dd~dd {
border-top: 1px solid #262A65;
}
dd[data-state] {
background-color: var(--table-bg);
}
dd[data-state="running"] {
background-color: var(--success-bg);
color: var(--success-text);
}
dd[data-state="stopped"],
dd[data-state="exited"],
dd[data-state="dead"] {
background-color: var(--danger-bg);
color: var(--danger-text);
}
dt,
dd {
box-sizing: border-box;
padding: 4px;
}
dt {
padding-bottom: 0;
}
dd {
background-color: #171625;
padding: 4px 8px;
word-break: break-all;
}
dl.table {
border: 1px solid #3D3E82;
background-color: var(--table-bg);
margin: 0;
& dt {
float: left;
width: 25%;
}
& dd {
margin-left: 25%;
border-left: 1px dotted #3D3E82;
}
& dd:after {
content: "";
display: block;
clear: both;
}
}
dl.list {
border: 1px solid #3D3E82;
background-color: var(--table-bg);
margin: 0;
border-top: 0;
& dd {
margin: 0;
}
& dt {
padding: 4px 8px;
}
}

View file

@ -2,6 +2,11 @@
--background: #13131E;
--bg-raised: #171625;
--text: #E0DFFE;
--text-accent: #E796F3;
--table-border-color: #5958B1;
--table-border: 3px solid var(--table-border-color);
--table-bg: #202248;
--link: #FFC53D;
--link-hover: #e28d0e;
@ -11,6 +16,14 @@
--button-border: #5B5BD6;
--button-bg-hover: #7E451D;
--success-bright: #46A758;
--success-bg: #113B29;
--success-text: #3DD68C;
--danger-bright: #E5484D;
--danger-bg: #500F1C;
--danger-text: #FF9592;
background-color: var(--background);
color: var(--text);
font-family: Inter, sans-serif;
@ -64,7 +77,6 @@ nav {
}
}
main {
padding: 10px;
}
@ -92,7 +104,7 @@ button {
table.table {
background-color: var(--bg-raised);
border: 3px solid #5958B1;
border: var(--table-border);
border-radius: 3px;
& th,
@ -105,7 +117,7 @@ table.table {
}
& thead {
background-color: #202248;
background-color: var(--table-bg);
}
}
@ -116,7 +128,7 @@ table.table {
input,
textarea {
border: 1px solid #5958B1;
border: 1px solid var(--table-border-color);
border-radius: 3px;
background-color: var(--bg-raised);
color: var(--text);

View file

@ -71,6 +71,7 @@
</section>
</main>
<link rel="stylesheet" href="/static/css/resource.css" />
<style scoped>
a.stack-name {
text-decoration: none;
@ -95,13 +96,13 @@
}
.container-name {
color: #E796F3;
color: var(--text-accent);
}
.label {
& .key {
color: #E796F3;
color: var(--text-accent);
}
}
@ -115,8 +116,8 @@
& .error {
padding: 4px 8px;
background-color: #3B1219;
color: #FF9592;
background-color: var(--danger-bg);
color: var(--danger-text);
}
& p {
@ -135,81 +136,6 @@
}
}
dd~dt,
dd~dd {
border-top: 1px solid #262A65;
}
dd[data-state] {
background-color: #202248;
}
dd[data-state="running"] {
background-color: #113B29;
color: #3DD68C;
}
dd[data-state="stopped"],
dd[data-state="exited"],
dd[data-state="dead"] {
background-color: #500F1C;
color: #FF9592;
}
dt,
dd {
box-sizing: border-box;
padding: 4px;
}
dt {
padding-bottom: 0;
}
dd {
background-color: #171625;
padding: 4px 8px;
word-break: break-all;
}
dl.table {
border: 1px solid #3D3E82;
background-color: #202248;
margin: 0;
& dt {
float: left;
width: 25%;
}
& dd {
margin-left: 25%;
border-left: 1px dotted #3D3E82;
}
& dd:after {
content: "";
display: block;
clear: both;
}
}
dl.list {
border: 1px solid #3D3E82;
background-color: #202248;
margin: 0;
border-top: 0;
& dd {
margin: 0;
}
& dt {
padding: 4px 8px;
}
}
main>header {
@ -244,20 +170,6 @@
overflow: auto;
}
}
.actions {
display: flex;
gap: 0.5rem;
& form {
display: flex;
min-width: 80px;
}
& button {
flex: 1;
}
}
</style>
<script type="module">

View file

@ -158,11 +158,11 @@
padding: 0 10px;
&.exited {
background-color: #E5484D;
background-color: var(--danger-bright);
}
&.running {
background-color: #46A758;
background-color: var(--success-bright);
}
}
}
@ -180,7 +180,7 @@
&.status {
width: 6px;
padding: 0;
background-color: #E5484D;
background-color: var(--danger-bright);
&.active {
background-color: #33B074;
@ -210,11 +210,11 @@
}
&.running::before {
color: #46A758;
color: var(--success-bright);
}
&.stopped::before {
color: #E5484D;
color: var(--danger-bright);
}
}

View file

@ -0,0 +1,25 @@
{% extends "base.html" %}
{% block title %}Confirm deletion of stack {{stack_name}}?{% endblock %}
{% block content %}
<main>
<header>
<h1>Confirm deletion of stack <span class="stack-name">{{stack_name}}</span></h1>
<div class="actions big">
<form action="./delete" method="POST">
<button class="danger" type="submit">Delete</button>
</form>
<form action="." method="GET">
<button type="submit">Cancel</button>
</form>
</div>
</header>
</main>
<link rel="stylesheet" href="/static/css/resource.css" />
<style scoped>
.stack-name {
color: var(--text-accent);
}
</style>
{% endblock %}

View file

@ -11,6 +11,7 @@
<form action="./restart" method="POST"><button type="submit">Restart</button></form>
<form action="./stop" method="POST"><button type="submit">Stop</button></form>
<form action="./down" method="POST"><button type="submit">Down</button></form>
<form action="./delete" method="GET"><button type="submit">Delete</button></form>
</div>
</header>
<section class="container-list">
@ -50,6 +51,7 @@
</main>
<link rel="stylesheet" href="/static/css/editor.css" />
<link rel="stylesheet" href="/static/css/resource.css" />
<style scoped>
main {
display: flex;
@ -69,31 +71,17 @@
padding: 0 10px;
&.exited {
background-color: #E5484D;
background-color: var(--danger-bright);
}
&.running {
background-color: #46A758;
background-color: var(--success-bright);
}
}
}
.stack-name {
color: #E796F3;
}
.actions {
display: flex;
gap: 0.5rem;
& form {
display: flex;
min-width: 80px;
}
& button {
flex: 1;
}
color: var(--text-accent);
}
</style>