Add dynamic dashboard

- Initial commit
- Create the frontend and backend
- Read the dashboard sections from config and return those the user has
  access to based on the Trusted SSO headers
This commit is contained in:
Nicolas Mémeint 2024-10-12 22:49:45 +02:00
commit b0dc4f7370
21 changed files with 8056 additions and 0 deletions

1
.envrc Normal file
View file

@ -0,0 +1 @@
use flake

5
.gitignore vendored Normal file
View file

@ -0,0 +1,5 @@
# direnv
.direnv
# nix output
result

77
flake.lock generated Normal file
View file

@ -0,0 +1,77 @@
{
"nodes": {
"crane": {
"locked": {
"lastModified": 1727974419,
"narHash": "sha256-WD0//20h+2/yPGkO88d2nYbb23WMWYvnRyDQ9Dx4UHg=",
"owner": "ipetkov",
"repo": "crane",
"rev": "37e4f9f0976cb9281cd3f0c70081e5e0ecaee93f",
"type": "github"
},
"original": {
"owner": "ipetkov",
"repo": "crane",
"type": "github"
}
},
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1726560853,
"narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1727634051,
"narHash": "sha256-S5kVU7U82LfpEukbn/ihcyNt2+EvG7Z5unsKW9H/yFA=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "06cf0e1da4208d3766d898b7fdab6513366d45b9",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"crane": "crane",
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

43
flake.nix Normal file
View file

@ -0,0 +1,43 @@
{
description = "Builder of the nicomem.com website";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
crane.url = "github:ipetkov/crane";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { nixpkgs, crane, flake-utils, ... }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = nixpkgs.legacyPackages.${system};
craneLib = crane.mkLib pkgs;
in
{
packages = rec {
backend = craneLib.buildPackage {
src = craneLib.cleanCargoSource (craneLib.path ./packages/backend);
};
frontend = pkgs.callPackage ./packages/frontend { };
all = pkgs.symlinkJoin {
name = "all";
paths = [ backend frontend ];
};
default = all;
};
devShells.default = craneLib.devShell {
# --- Environment variables ----
# Needed for rust-analyzer
RUST_SRC_PATH = "${pkgs.rust.packages.stable.rustPlatform.rustLibSrc}";
# --- Extra packages ---
packages = [
pkgs.bashInteractive
pkgs.nodejs
];
};
});
}

1
packages/backend/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
target/

851
packages/backend/Cargo.lock generated Normal file
View file

@ -0,0 +1,851 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "addr2line"
version = "0.24.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
dependencies = [
"gimli",
]
[[package]]
name = "adler2"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
[[package]]
name = "anyhow"
version = "1.0.90"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37bf3594c4c988a53154954629820791dde498571819ae4ca50ca811e060cc95"
[[package]]
name = "async-trait"
version = "0.1.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "atomic"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d818003e740b63afc82337e3160717f4f63078720a810b7b903e70a5d1d2994"
dependencies = [
"bytemuck",
]
[[package]]
name = "axum"
version = "0.7.7"
source = "git+https://github.com/tokio-rs/axum.git?branch=jplatte/generic-serve#26a37a4679a3ec4ce88168be825d2f6940a1dec2"
dependencies = [
"async-trait",
"axum-core",
"axum-macros",
"bytes",
"futures-util",
"http",
"http-body",
"http-body-util",
"hyper",
"hyper-util",
"itoa",
"matchit",
"memchr",
"mime",
"percent-encoding",
"pin-project-lite",
"rustversion",
"serde",
"serde_json",
"serde_path_to_error",
"serde_urlencoded",
"sync_wrapper 1.0.1",
"tokio",
"tower",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]
name = "axum-core"
version = "0.4.5"
source = "git+https://github.com/tokio-rs/axum.git?branch=jplatte/generic-serve#26a37a4679a3ec4ce88168be825d2f6940a1dec2"
dependencies = [
"async-trait",
"bytes",
"futures-util",
"http",
"http-body",
"http-body-util",
"mime",
"pin-project-lite",
"rustversion",
"sync_wrapper 1.0.1",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]
name = "axum-macros"
version = "0.4.2"
source = "git+https://github.com/tokio-rs/axum.git?branch=jplatte/generic-serve#26a37a4679a3ec4ce88168be825d2f6940a1dec2"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "backend"
version = "0.1.0"
dependencies = [
"anyhow",
"axum",
"figment",
"serde",
"service-binding",
"tokio",
"tower",
"tower-http",
"tracing",
"tracing-subscriber",
]
[[package]]
name = "backtrace"
version = "0.3.74"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a"
dependencies = [
"addr2line",
"cfg-if",
"libc",
"miniz_oxide",
"object",
"rustc-demangle",
"windows-targets",
]
[[package]]
name = "bitflags"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
[[package]]
name = "bytemuck"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8334215b81e418a0a7bdb8ef0849474f40bb10c8b71f1c4ed315cff49f32494d"
[[package]]
name = "bytes"
version = "1.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "figment"
version = "0.10.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8cb01cd46b0cf372153850f4c6c272d9cbea2da513e07538405148f95bd789f3"
dependencies = [
"atomic",
"serde",
"serde_json",
"uncased",
"version_check",
]
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "form_urlencoded"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
dependencies = [
"percent-encoding",
]
[[package]]
name = "futures-channel"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
dependencies = [
"futures-core",
]
[[package]]
name = "futures-core"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
[[package]]
name = "futures-task"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
[[package]]
name = "futures-util"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
dependencies = [
"futures-core",
"futures-task",
"pin-project-lite",
"pin-utils",
]
[[package]]
name = "gimli"
version = "0.31.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
[[package]]
name = "hermit-abi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
[[package]]
name = "http"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258"
dependencies = [
"bytes",
"fnv",
"itoa",
]
[[package]]
name = "http-body"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
dependencies = [
"bytes",
"http",
]
[[package]]
name = "http-body-util"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f"
dependencies = [
"bytes",
"futures-util",
"http",
"http-body",
"pin-project-lite",
]
[[package]]
name = "httparse"
version = "1.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946"
[[package]]
name = "httpdate"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
[[package]]
name = "hyper"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbbff0a806a4728c99295b254c8838933b5b082d75e3cb70c8dab21fdfbcfa9a"
dependencies = [
"bytes",
"futures-channel",
"futures-util",
"http",
"http-body",
"httparse",
"httpdate",
"itoa",
"pin-project-lite",
"smallvec",
"tokio",
]
[[package]]
name = "hyper-util"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41296eb09f183ac68eec06e03cdbea2e759633d4067b2f6552fc2e009bcad08b"
dependencies = [
"bytes",
"futures-util",
"http",
"http-body",
"hyper",
"pin-project-lite",
"tokio",
"tower-service",
]
[[package]]
name = "itoa"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
[[package]]
name = "lazy_static"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "libc"
version = "0.2.161"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1"
[[package]]
name = "log"
version = "0.4.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
[[package]]
name = "matchit"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94"
[[package]]
name = "memchr"
version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "mime"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
[[package]]
name = "miniz_oxide"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1"
dependencies = [
"adler2",
]
[[package]]
name = "mio"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec"
dependencies = [
"hermit-abi",
"libc",
"wasi",
"windows-sys",
]
[[package]]
name = "nu-ansi-term"
version = "0.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
dependencies = [
"overload",
"winapi",
]
[[package]]
name = "object"
version = "0.36.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e"
dependencies = [
"memchr",
]
[[package]]
name = "once_cell"
version = "1.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
[[package]]
name = "overload"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
[[package]]
name = "percent-encoding"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
[[package]]
name = "pin-project-lite"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
[[package]]
name = "pin-utils"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "proc-macro2"
version = "1.0.88"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
dependencies = [
"proc-macro2",
]
[[package]]
name = "raunch"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a67226789ccd419c55f5c826e04f7d58690b0593364f61bc8ed6e7dbbab49c5"
dependencies = [
"libc",
]
[[package]]
name = "rustc-demangle"
version = "0.1.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
[[package]]
name = "rustversion"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248"
[[package]]
name = "ryu"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
[[package]]
name = "serde"
version = "1.0.210"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.210"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.131"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67d42a0bd4ac281beff598909bb56a86acaf979b84483e1c79c10dcaf98f8cf3"
dependencies = [
"itoa",
"memchr",
"ryu",
"serde",
]
[[package]]
name = "serde_path_to_error"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6"
dependencies = [
"itoa",
"serde",
]
[[package]]
name = "serde_urlencoded"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
dependencies = [
"form_urlencoded",
"itoa",
"ryu",
"serde",
]
[[package]]
name = "service-binding"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25663b9d8b83193ef83183edb3fd01e5447adb3e5baa35466ff9a6cd2408d6e8"
dependencies = [
"raunch",
]
[[package]]
name = "sharded-slab"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
dependencies = [
"lazy_static",
]
[[package]]
name = "smallvec"
version = "1.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
[[package]]
name = "socket2"
version = "0.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c"
dependencies = [
"libc",
"windows-sys",
]
[[package]]
name = "syn"
version = "2.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "sync_wrapper"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
[[package]]
name = "sync_wrapper"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394"
[[package]]
name = "thread_local"
version = "1.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
dependencies = [
"cfg-if",
"once_cell",
]
[[package]]
name = "tokio"
version = "1.40.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998"
dependencies = [
"backtrace",
"libc",
"mio",
"pin-project-lite",
"socket2",
"tokio-macros",
"windows-sys",
]
[[package]]
name = "tokio-macros"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tower"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2873938d487c3cfb9aed7546dc9f2711d867c9f90c46b889989a2cb84eba6b4f"
dependencies = [
"futures-core",
"futures-util",
"pin-project-lite",
"sync_wrapper 0.1.2",
"tokio",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]
name = "tower-http"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8437150ab6bbc8c5f0f519e3d5ed4aa883a83dd4cdd3d1b21f9482936046cb97"
dependencies = [
"bitflags",
"bytes",
"http",
"http-body",
"pin-project-lite",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]
name = "tower-layer"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e"
[[package]]
name = "tower-service"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
[[package]]
name = "tracing"
version = "0.1.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
dependencies = [
"log",
"pin-project-lite",
"tracing-attributes",
"tracing-core",
]
[[package]]
name = "tracing-attributes"
version = "0.1.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tracing-core"
version = "0.1.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
dependencies = [
"once_cell",
"valuable",
]
[[package]]
name = "tracing-log"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
dependencies = [
"log",
"once_cell",
"tracing-core",
]
[[package]]
name = "tracing-subscriber"
version = "0.3.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b"
dependencies = [
"nu-ansi-term",
"sharded-slab",
"smallvec",
"thread_local",
"tracing-core",
"tracing-log",
]
[[package]]
name = "uncased"
version = "0.9.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1b88fcfe09e89d3866a5c11019378088af2d24c3fbd4f0543f96b479ec90697"
dependencies = [
"version_check",
]
[[package]]
name = "unicode-ident"
version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
[[package]]
name = "valuable"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
[[package]]
name = "version_check"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"

View file

@ -0,0 +1,28 @@
[package]
name = "backend"
version = "0.1.0"
edition = "2021"
[dependencies]
anyhow = "1.0.89"
axum = { version = "0.7.0", features = ["macros"] }
figment = { version = "0.10.19", features = ["json"] }
serde = { version = "1.0.210", features = ["derive"] }
service-binding = "3.0.0"
tokio = "1.40.0"
tower = "0.5.1"
tower-http = { version = "0.6.1", features = ["trace"] }
tracing = "0.1.40"
tracing-subscriber = "0.3.18"
# Axum patch to allow using other types than TcpListener with axum::serve
# This will most probably be included in version 0.8
# See: https://github.com/tokio-rs/axum/pull/2941
[patch.crates-io]
axum = { git = "https://github.com/tokio-rs/axum.git", branch = "jplatte/generic-serve" }
axum-core = { git = "https://github.com/tokio-rs/axum.git", branch = "jplatte/generic-serve" }
[profile.release]
opt-level = "z"
panic = "abort"
strip = true

View file

@ -0,0 +1,79 @@
use std::path::Path;
use anyhow::Context;
use figment::providers::Format;
use figment::providers::Json;
use figment::Figment;
use serde::Deserialize;
use serde::Deserializer;
use service_binding::Binding;
#[derive(Debug, Deserialize)]
pub struct Server {
/// The address to bind to. The syntax is described at:
/// https://github.com/wiktor-k/service-binding/blob/main/README.md#supported-schemes
#[serde(deserialize_with = "deserialize_binding")]
pub bind_address: Binding,
#[serde(deserialize_with = "deserialize_level", default = "default_level")]
pub log_level: tracing::Level,
}
#[derive(Debug, Deserialize)]
pub struct Section {
/// URL where the user can access the icon
pub icon: String,
/// URL where the user can be redirected to
pub href: String,
/// Title of the section
pub title: String,
/// The section description
pub descr: String,
/// If set, only users with the specified groups will be able to access the section
pub groups: Option<Vec<String>>,
}
#[derive(Debug, Deserialize)]
pub struct Config {
pub server: Server,
pub sections: Vec<Section>,
}
/// Parse the configuration.
///
/// Configuration may come from the following sources:
/// - A JSON file: The file is structured in the same way as the [Config] struct
///
/// Sources are read and merged in the above order, the latter sources
/// taking overriding the previous ones in case of conflict.
pub fn parse_config(config_json: &Path) -> anyhow::Result<Config> {
Figment::from(Json::file_exact(config_json))
.extract()
.context("Error while getting the configuration")
}
fn deserialize_binding<'de, D>(deserializer: D) -> Result<Binding, D::Error>
where
D: Deserializer<'de>,
{
let value: String = Deserialize::deserialize(deserializer)?;
value.parse().map_err(serde::de::Error::custom)
}
fn deserialize_level<'de, D>(deserializer: D) -> Result<tracing::Level, D::Error>
where
D: Deserializer<'de>,
{
let value: String = Deserialize::deserialize(deserializer)?;
value.parse().map_err(serde::de::Error::custom)
}
fn default_level() -> tracing::Level {
tracing::Level::INFO
}

View file

@ -0,0 +1,76 @@
use axum::routing::get;
use axum::Router;
use crate::state::SharedState;
pub fn router() -> Router<SharedState> {
Router::new().nest(
"/dashboard",
Router::new().route("/sections", get(get::sections)),
)
}
mod get {
use axum::extract::State;
use axum::http::HeaderMap;
use axum::Json;
use serde::Serialize;
use tracing::debug;
use crate::state::SharedState;
#[derive(Debug, Serialize)]
pub struct Section {
/// Path where the icon is served
icon: String,
/// URL where to link the section to
href: String,
/// Title of the section
title: String,
/// The section description
descr: String,
}
/// Get the sections the user has access to
pub async fn sections(state: State<SharedState>, headers: HeaderMap) -> Json<Vec<Section>> {
debug!("The request headers are: {:?}", headers);
// Get the user groups from the Trusted SSO header
let header_groups = headers.get("Remote-Groups");
debug!(
"Trusted SSO header found? {}",
if header_groups.is_some() { "Yes" } else { "No" }
);
let user_groups: Vec<&str> = header_groups
.and_then(|val| val.to_str().ok())
.map(|val| val.split(',').collect())
.unwrap_or_default();
debug!("User has the following groups: {:?}", user_groups);
// Get public sections and sections restricted to one of the user groups
let allowed_sections = state.sections.iter().filter(|sections| {
sections.groups.as_ref().map_or(true, |groups| {
groups
.iter()
.any(|group| user_groups.contains(&group.as_str()))
})
});
// Create the response from the allowed sections
Json(
allowed_sections
.map(|section| Section {
icon: section.icon.to_string(),
href: section.href.to_string(),
title: section.title.to_string(),
descr: section.descr.to_string(),
})
.collect(),
)
}
}

View file

@ -0,0 +1,60 @@
use std::path::PathBuf;
use anyhow::Context;
use axum::Router;
use state::SharedState;
use tokio::net::TcpListener;
use tokio::net::UnixListener;
use tower_http::trace::TraceLayer;
mod config;
mod dashboard;
mod state;
#[tokio::main(flavor = "current_thread")]
async fn main() -> anyhow::Result<()> {
let config_json: PathBuf = std::env::args()
.nth(1)
.context("Expected JSON config file as argument")?
.into();
let config = config::parse_config(&config_json)?;
tracing_subscriber::fmt()
.with_max_level(config.server.log_level)
.init();
let binding = config.server.bind_address.clone();
let state = SharedState::new(config);
let app = Router::new()
.merge(dashboard::router())
.layer(TraceLayer::new_for_http())
.with_state(state);
match binding
.try_into()
.context("Could not get a listener from the binding address")?
{
service_binding::Listener::Unix(listener) => {
axum::serve(
UnixListener::from_std(listener).unwrap(),
app.into_make_service(),
)
.await?
}
service_binding::Listener::Tcp(listener) => {
axum::serve(
TcpListener::from_std(listener).unwrap(),
app.into_make_service(),
)
.await?
}
service_binding::Listener::NamedPipe(_) => {
anyhow::bail!("Named pipes are not supported")
}
};
Ok(())
}

View file

@ -0,0 +1,5 @@
use std::sync::Arc;
use crate::config::Config;
pub type SharedState = Arc<Config>;

19
packages/frontend/.gitignore vendored Normal file
View file

@ -0,0 +1,19 @@
# build output
dist/
# generated types
.astro/
# dependencies
node_modules/
# logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# environment variables
.env
.env.production

View file

@ -0,0 +1,9 @@
// @ts-check
import { defineConfig } from 'astro/config';
import compress from "astro-compress";
import compressor from "astro-compressor";
// https://astro.build/config
export default defineConfig({
integrations: [compress(), compressor()]
});

View file

@ -0,0 +1,20 @@
{ buildNpmPackage, vips, nodePackages, pkg-config, ... }:
buildNpmPackage {
pname = "nicomem.com";
version = "0.1.0";
src = ./.;
nativeBuildInputs = [ pkg-config nodePackages.node-gyp ];
buildInputs = [ vips ];
npmDepsHash = "sha256-GHeKYnMVGzjvAMw7GTdAN4xScttRTrMSLrlCnmmxDPM=";
npmFlags = [ "--legacy-peer-deps" ];
installPhase = ''
mkdir $out
cp -r dist $out/static
'';
}

6597
packages/frontend/package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,23 @@
{
"name": "nicomem.com",
"version": "0.0.1",
"dependencies": {
"@astrojs/check": "^0.9.4",
"astro": "^5.0.0-beta.3",
"astro-compress": "^2.3.3",
"astro-compressor": "^0.4.1",
"sharp": "^0.33.5",
"typescript": "^5.6.2"
},
"scripts": {
"dev": "astro dev",
"start": "astro dev",
"build": "astro check && astro build",
"preview": "astro preview",
"astro": "astro"
},
"type": "module",
"trustedDependencies": [
"sharp"
]
}

View file

@ -0,0 +1,99 @@
---
---
<ul id="section"></ul>
<script>
interface Section {
icon: string;
href: string;
title: string;
descr: string;
}
const createCard = ({ icon, href, title, descr }: Section): HTMLLIElement => {
const img = document.createElement("img");
img.src = icon;
img.alt = "logo";
img.loading = "eager";
const h3 = document.createElement("h3");
const a = document.createElement("a");
a.href = href;
a.textContent = title;
h3.appendChild(a);
const p = document.createElement("p");
p.textContent = descr;
const li = document.createElement("li");
li.classList.add("card");
li.append(img, h3, p);
return li;
};
const response = await fetch("/api/dashboard/sections");
const sections: Section[] = await response.json();
const fragment = document.createDocumentFragment();
sections.map(createCard).forEach((e) => fragment.appendChild(e));
const ul = document.getElementById("section")!;
ul.appendChild(fragment);
</script>
<style is:global>
#section {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(17em, 1fr));
gap: 0.5em 0.5em;
}
li.card {
display: grid;
grid-template-columns: auto 1fr;
grid-template-rows: 1fr 1fr;
padding: 0.5em;
border: thin solid;
border-radius: 1em;
}
li.card > img {
grid-column: 1;
grid-row: 1 / 3;
/*
Instead of simply using `aspect-ratio: 1;`, we need to use fixed sizes...
See https://bugzilla.mozilla.org/show_bug.cgi?id=1719273
*/
height: 3em;
width: 3em;
align-self: center;
padding: 0.5em;
box-sizing: border-box;
border-radius: 1em;
background-color: color-mix(in srgb, CanvasText 10%, transparent);
}
li.card > h3 {
grid-row: 1;
grid-column: 2;
margin: 0;
justify-self: center;
}
li.card > p {
grid-row: 2;
grid-column: 2;
margin: 0;
justify-self: center;
}
</style>

11
packages/frontend/src/env.d.ts vendored Normal file
View file

@ -0,0 +1,11 @@
/// <reference path="../.astro/types.d.ts" />
declare namespace App {
import type { BaseClient } from "openid-client";
interface Locals {
auth: {
client: BaseClient;
};
}
}

View file

@ -0,0 +1,25 @@
---
interface Props {
title: string;
}
const { title } = Astro.props;
---
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="color-scheme" content="light dark" />
<title>{title}</title>
</head>
<body>
<slot />
</body>
</html>
<style is:global>
html {
font-family: system-ui, sans-serif;
}
</style>

View file

@ -0,0 +1,13 @@
---
import Layout from "@layouts/Layout.astro";
import Section from "@components/Section.astro";
---
<Layout title="Dashboard">
<main>
<h1>Dashboard</h1>
<Section />
</main>
</Layout>

View file

@ -0,0 +1,14 @@
{
"extends": "astro/tsconfigs/strict",
"include": [".astro/types.d.ts", "src/**/*", "public/**/*"],
"exclude": ["dist", ".direnv"],
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@components/*": ["src/components/*"],
"@icons/*": ["src/icons/*"],
"@layouts/*": ["src/layouts/*"]
},
"types": ["node"]
}
}