Add flake
Some checks failed
continuous-integration/drone/push Build is failing

This commit is contained in:
asonix 2023-03-09 21:49:22 -06:00
parent 9dcb5114f3
commit 32aaa1c464
14 changed files with 465 additions and 323 deletions

3
.gitignore vendored
View file

@ -1,2 +1,5 @@
/target
/sled
/.envrc
/.direnv
/result

436
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,7 @@
[package]
name = "pict-rs-aggregator"
description = "A simple image aggregation service for pict-rs"
version = "0.2.0-beta.2"
version = "0.2.0-beta.1"
authors = ["asonix <asonix@asonix.dog>"]
license = "AGPL-3.0"
readme = "README.md"
@ -18,7 +18,7 @@ default = []
actix-rt = "2.7.0"
actix-web = { version = "4.0.0", default-features = false }
awc = { version = "3.0.0", default-features = false }
bcrypt = "0.13"
bcrypt = "0.14"
clap = { version = "4.0.2", features = ["derive", "env"] }
console-subscriber = "0.1"
mime = "0.3"
@ -46,7 +46,7 @@ uuid = { version = "1", features = ["serde", "v4"] }
[dependencies.tracing-actix-web]
version = "0.7.0"
version = "0.7.2"
default-features = false
features = ["emit_event_on_error", "opentelemetry_0_18"]

43
flake.lock Normal file
View file

@ -0,0 +1,43 @@
{
"nodes": {
"flake-utils": {
"locked": {
"lastModified": 1676283394,
"narHash": "sha256-XX2f9c3iySLCw54rJ/CZs+ZK6IQy7GXNY4nSOyu2QG4=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "3db36a8b464d0c4532ba1c7dda728f4576d6d073",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1678293141,
"narHash": "sha256-lLlQHaR0y+q6nd6kfpydPTGHhl1rS9nU9OQmztzKOYs=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "c90c4025bb6e0c4eaf438128a3b2640314b1c58d",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}

34
flake.nix Normal file
View file

@ -0,0 +1,34 @@
{
description = "pict-rs-aggregator";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, flake-utils }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = import nixpkgs {
inherit system;
};
in
{
packages = rec {
pict-rs-aggregator = pkgs.callPackage ./pict-rs-aggregator.nix { };
default = pict-rs-aggregator;
};
apps = rec {
dev = flake-utils.lib.mkApp { drv = self.packages.${system}.pict-rs-aggregator; };
default = dev;
};
devShell = with pkgs; mkShell {
nativeBuildInputs = [ cargo cargo-outdated cargo-zigbuild clippy gcc protobuf rust-analyzer rustc rustfmt ];
RUST_SRC_PATH = "${pkgs.rust.packages.stable.rustPlatform.rustLibSrc}";
};
});
}

27
pict-rs-aggregator.nix Normal file
View file

@ -0,0 +1,27 @@
{ lib
, makeWrapper
, nixosTests
, protobuf
, rustPlatform
, stdenv
}:
rustPlatform.buildRustPackage {
pname = "pict-rs-aggregator";
version = "0.2.0-beta.1";
src = ./.;
cargoSha256 = "UHTjI4UAVFP4KnkyWuubiVkFev+/z3PvCvDZVOy+Kxs=";
PROTOC = "${protobuf}/bin/protoc";
PROTOC_INCLUDE = "${protobuf}/include";
nativeBuildInputs = [ ];
passthru.tests = { inherit (nixosTests) pict-rs-aggregator; };
meta = with lib; {
description = "A simple image hosting service";
homepage = "https://git.asonix.dog/asonix/pict-rs-aggregator";
license = with licenses; [ agpl3Plus ];
};
}

View file

@ -163,9 +163,9 @@ impl State {
} else if s.is_empty() {
self.scope.clone()
} else if self.scope.is_empty() {
format!("/{s}")
format!("/{}", s)
} else {
format!("{}/{s}", self.scope)
format!("{}/{}", self.scope, s)
}
}
@ -174,31 +174,37 @@ impl State {
}
fn edit_collection_path(&self, id: Uuid, token: &ValidToken) -> String {
self.scoped(&format!("{id}?token={}", token.token))
self.scoped(&format!("{}?token={}", id, token.token))
}
fn update_collection_path(&self, id: Uuid, token: &ValidToken) -> String {
self.scoped(&format!("{id}?token={}", token.token))
self.scoped(&format!("{}?token={}", id, token.token))
}
fn delete_collection_path(&self, id: Uuid, token: &ValidToken, confirmed: bool) -> String {
if confirmed {
self.scoped(&format!("{id}/delete?token={}&confirmed=true", token.token))
self.scoped(&format!(
"{}/delete?token={}&confirmed=true",
id, token.token
))
} else {
self.scoped(&format!("{id}/delete?token={}", token.token))
self.scoped(&format!("{}/delete?token={}", id, token.token))
}
}
fn public_collection_path(&self, id: Uuid) -> String {
self.scoped(&format!("{id}"))
self.scoped(&format!("{}", id))
}
fn create_entry_path(&self, collection_id: Uuid, token: &ValidToken) -> String {
self.scoped(&format!("{collection_id}/entry?token={}", token.token))
self.scoped(&format!("{}/entry?token={}", collection_id, token.token))
}
fn update_entry_path(&self, collection_id: Uuid, id: Uuid, token: &ValidToken) -> String {
self.scoped(&format!("{collection_id}/entry/{id}?token={}", token.token))
self.scoped(&format!(
"{}/entry/{}?token={}",
collection_id, id, token.token
))
}
fn move_entry_path(
@ -209,8 +215,8 @@ impl State {
direction: Direction,
) -> String {
self.scoped(&format!(
"{collection_id}/entry/{id}/move/{direction}?token={}",
token.token
"{}/entry/{}/move/{}?token={}",
collection_id, id, direction, token.token
))
}
@ -223,24 +229,25 @@ impl State {
) -> String {
if confirmed {
self.scoped(&format!(
"{collection_id}/entry/{id}/delete?token={}&confirmed=true",
token.token
"{}/entry/{}/delete?token={}&confirmed=true",
collection_id, id, token.token
))
} else {
self.scoped(&format!(
"{collection_id}/entry/{id}/delete?token={}",
token.token
"{}/entry/{}/delete?token={}",
collection_id, id, token.token
))
}
}
fn statics_path(&self, file: &str) -> String {
self.scoped(&format!("static/{file}"))
self.scoped(&format!("static/{}", file))
}
fn thumbnail_path(&self, filename: &str, size: u16, extension: pict::Extension) -> String {
self.scoped(&format!(
"image/thumbnail.{extension}?src={filename}&size={size}",
"image/thumbnail.{}?src={}&size={}",
extension, filename, size
))
}
@ -249,8 +256,9 @@ impl State {
for size in connection::VALID_SIZES {
sizes.push(format!(
"{} {size}w",
"{} {}w",
self.thumbnail_path(filename, *size, extension),
size,
))
}
@ -258,7 +266,7 @@ impl State {
}
fn image_path(&self, filename: &str) -> String {
self.scoped(&format!("image/full/{filename}"))
self.scoped(&format!("image/full/{}", filename))
}
}
@ -277,7 +285,6 @@ pub fn configure(cfg: &mut web::ServiceConfig, state: State, client: Client) {
client,
)))
.app_data(web::Data::new(state))
.route("/healthz", web::get().to(healthz))
.service(web::resource("/static/{filename}").route(web::get().to(static_files)))
.service(web::resource("/404").route(web::get().to(not_found)))
.service(
@ -348,11 +355,6 @@ where
}
}
async fn healthz(state: web::Data<State>) -> Result<HttpResponse, StateError> {
state.store.check_health().await.stateful(&state)?;
Ok(HttpResponse::Ok().finish())
}
#[tracing::instrument(name = "Static files")]
async fn static_files(filename: web::Path<String>, state: web::Data<State>) -> HttpResponse {
let filename = filename.into_inner();
@ -678,7 +680,7 @@ async fn upload(
}
}
Err(e) => {
tracing::warn!("{e}");
tracing::warn!("{}", e);
let _ = store::DeleteEntry {
entry_path: &entry_path2,
}
@ -775,13 +777,7 @@ async fn view_collection(
rendered(
|cursor| {
self::templates::view_collection_html(
cursor,
path.collection,
&collection,
&entries,
&state,
)
self::templates::view_collection_html(cursor, path.collection, &collection, &entries, &state)
},
HttpResponse::Ok(),
)
@ -980,7 +976,7 @@ fn to_svg_string(qr: &qrcodegen::QrCode, border: i32) -> String {
.checked_add(border.checked_mul(2).unwrap())
.unwrap();
result += &format!(
"<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" viewBox=\"0 0 {dimension} {dimension}\" stroke=\"none\">\n");
"<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" viewBox=\"0 0 {0} {0}\" stroke=\"none\">\n", dimension);
result += "\t<rect width=\"100%\" height=\"100%\" fill=\"#FFFFFF\"/>\n";
result += "\t<path d=\"";
for y in 0..qr.size() {

View file

@ -3,22 +3,22 @@
@(text: &str, kind: ButtonKind)
@match kind {
ButtonKind::Submit => {
<div class="button submit">
<span>@text</span>
<button class="action" type="submit">@text</button>
</div>
}
ButtonKind::Plain => {
<div class="button plain">
<span>@text</span>
<button class="action">@text</button>
</div>
}
ButtonKind::Outline => {
<div class="button outline">
<span>@text</span>
<button class="action">@text</button>
</div>
}
ButtonKind::Submit => {
<div class="button submit">
<span>@text</span>
<button class="action" type="submit">@text</button>
</div>
}
ButtonKind::Plain => {
<div class="button plain">
<span>@text</span>
<button class="action">@text</button>
</div>
}
ButtonKind::Outline => {
<div class="button outline">
<span>@text</span>
<button class="action">@text</button>
</div>
}
}

View file

@ -3,22 +3,23 @@
@(text: &str, href: &str, kind: ButtonKind)
@match kind {
ButtonKind::Submit => {
<div class="button submit">
<span>@text</span>
<a class="action" href="@href">@text</a>
</div>
}
ButtonKind::Plain => {
<div class="button plain">
<span>@text</span>
<a class="action" href="@href">@text</a>
</div>
}
ButtonKind::Outline => {
<div class="button outline">
<span>@text</span>
<a class="action" href="@href">@text</a>
</div>
}
ButtonKind::Submit => {
<div class="button submit">
<span>@text</span>
<a class="action" href="@href">@text</a>
</div>
}
ButtonKind::Plain => {
<div class="button plain">
<span>@text</span>
<a class="action" href="@href">@text</a>
</div>
}
ButtonKind::Outline => {
<div class="button outline">
<span>@text</span>
<a class="action" href="@href">@text</a>
</div>
}
}

View file

@ -4,25 +4,24 @@
@(id: Uuid, collection: &Collection, token: &ValidToken, state: &State)
@:layout_html(state, &format!("Delete: {}", collection.title), Some(&format!("Are you sure you want to delete {}",
collection.title)), {
<meta property="og:url" content="@state.delete_collection_path(id, token, false)" />
@:layout_html(state, &format!("Delete: {}", collection.title), Some(&format!("Are you sure you want to delete {}", collection.title)), {
<meta property="og:url" content="@state.delete_collection_path(id, token, false)" />
}, {
<section>
<article>
<div class="content-group">
<h3>Delete @collection.title</h3>
</div>
<div class="content-group">
<p class="subtitle">Are you sure you want to delete @collection.title</p>
</div>
<div class="content-group">
<div class="button-group">
@:button_link_html("Delete Collection", &state.delete_collection_path(id, token, true), ButtonKind::Submit)
@:button_link_html("Cancel", &state.edit_collection_path(id, token), ButtonKind::Outline)
</div>
</div>
</article>
<article>
<div class="content-group">
<h3>Delete @collection.title</h3>
</div>
<div class="content-group">
<p class="subtitle">Are you sure you want to delete @collection.title</p>
</div>
<div class="content-group">
<div class="button-group">
@:button_link_html("Delete Collection", &state.delete_collection_path(id, token, true), ButtonKind::Submit)
@:button_link_html("Cancel", &state.edit_collection_path(id, token), ButtonKind::Outline)
</div>
</div>
</article>
</section>
@:return_home_html(state)
})

View file

@ -5,29 +5,29 @@
@(collection_id: Uuid, id: Uuid, entry: &Entry, token: &ValidToken, state: &State)
@:layout_html(state, "Delete Image", Some("Are you sure you want to delete this image?"), {
<meta property="og:url" content="@state.delete_entry_path(collection_id, id, token, false)" />
<meta property="og:url" content="@state.delete_entry_path(collection_id, id, token, false)" />
}, {
<section>
<article>
<div class="content-group">
<h3>Delete Image</h3>
</div>
<div class="content-group">
<div class="edit-row">
<div class="edit-item">
@:image_html(entry, state)
<article>
<div class="content-group">
<h3>Delete Image</h3>
</div>
<div class="edit-item">
<p class="delete-confirmation">Are you sure you want to delete this image?</p>
<div class="button-group button-space">
@:button_link_html("Delete Image", &state.delete_entry_path(collection_id, id, token, true),
ButtonKind::Submit)
@:button_link_html("Cancel", &state.edit_collection_path(collection_id, token), ButtonKind::Outline)
</div>
<div class="content-group">
<div class="edit-row">
<div class="edit-item">
@:image_html(entry, state)
</div>
<div class="edit-item">
<p class="delete-confirmation">Are you sure you want to delete this image?</p>
<div class="button-group button-space">
@:button_link_html("Delete Image", &state.delete_entry_path(collection_id, id, token, true), ButtonKind::Submit)
@:button_link_html("Cancel", &state.edit_collection_path(collection_id, token), ButtonKind::Outline)
</div>
</div>
</div>
</div>
</div>
</div>
</article>
</article>
</section>
@:return_home_html(state)
})

View file

@ -1,6 +1,5 @@
@use crate::{ui::ButtonKind, Collection, Direction, Entry, State, ValidToken};
@use super::{button_html, button_link_html, image_html, file_input_html, layout_html, return_home_html, text_area_html,
text_input_html,
@use super::{button_html, button_link_html, image_html, file_input_html, layout_html, return_home_html, text_area_html, text_input_html,
statics::file_upload_js};
@use uuid::Uuid;

View file

@ -5,14 +5,14 @@
@:layout_html(state, "Error", Some(error), {}, {
<section>
<article>
<div class="content-group">
<h3>Error</h3>
</div>
<div class="content-group">
<p class="subtitle">@error</p>
</div>
</article>
<article>
<div class="content-group">
<h3>Error</h3>
</div>
<div class="content-group">
<p class="subtitle">@error</p>
</div>
</article>
</section>
@:return_home_html(state)
})

View file

@ -5,11 +5,11 @@
@:layout_html(state, "Not Found", None, {}, {
<section>
<article class="content-group">
<h3>Not Found</h3>
</article>
<article class="content-group">
<p><a href="@state.create_collection_path()">Return Home</a></p>
</article>
<article class="content-group">
<h3>Not Found</h3>
</article>
<article class="content-group">
<p><a href="@state.create_collection_path()">Return Home</a></p>
</article>
</section>
})