You've already forked authentication-service
mirror of
https://github.com/matrix-org/matrix-authentication-service.git
synced 2025-07-31 09:24:31 +03:00
Frontend/static files building & serving
This commit is contained in:
@ -1,3 +1,6 @@
|
||||
target/
|
||||
crates/*/target
|
||||
crates/*/node_modules
|
||||
.git/
|
||||
Dockerfile
|
||||
.dockerignore
|
||||
|
45
Cargo.lock
generated
45
Cargo.lock
generated
@ -1539,6 +1539,7 @@ dependencies = [
|
||||
"k256",
|
||||
"mas-config",
|
||||
"mas-data-model",
|
||||
"mas-static-files",
|
||||
"mas-templates",
|
||||
"mime",
|
||||
"oauth2-types",
|
||||
@ -1573,6 +1574,16 @@ dependencies = [
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mas-static-files"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"headers",
|
||||
"mime_guess",
|
||||
"rust-embed",
|
||||
"warp",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mas-templates"
|
||||
version = "0.1.0"
|
||||
@ -2528,6 +2539,40 @@ dependencies = [
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust-embed"
|
||||
version = "6.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d40377bff8cceee81e28ddb73ac97f5c2856ce5522f0b260b763f434cdfae602"
|
||||
dependencies = [
|
||||
"rust-embed-impl",
|
||||
"rust-embed-utils",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust-embed-impl"
|
||||
version = "6.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94e763e24ba2bf0c72bc6be883f967f794a019fafd1b86ba1daff9c91a7edd30"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rust-embed-utils",
|
||||
"syn",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust-embed-utils"
|
||||
version = "7.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ad22c7226e4829104deab21df575e995bfbc4adfad13a595e387477f238c1aec"
|
||||
dependencies = [
|
||||
"sha2",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.2.3"
|
||||
|
30
Dockerfile
30
Dockerfile
@ -7,12 +7,27 @@
|
||||
# there is a small script that translates those platforms to LLVM triples,
|
||||
# respectively x86-64-unknown-linux-gnu and aarch64-unknown-linux-gnu
|
||||
|
||||
ARG RUSTC_VERSION=1.56.1
|
||||
# The Debian version and version name must be in sync
|
||||
ARG DEBIAN_VERSION=11
|
||||
ARG DEBIAN_VERSION_NAME=bullseye
|
||||
ARG RUSTC_VERSION=1.57.0
|
||||
ARG NODEJS_VERSION=16
|
||||
|
||||
## Build stage that builds the static files/frontend ##
|
||||
FROM --platform=${BUILDPLATFORM} docker.io/library/node:${NODEJS_VERSION}-${DEBIAN_VERSION_NAME}-slim AS static-files
|
||||
|
||||
WORKDIR /app/crates/static-files
|
||||
COPY ./crates/static-files/package.json ./crates/static-files/package-lock.json /app/crates/static-files/
|
||||
RUN npm ci
|
||||
COPY . /app/
|
||||
RUN npm run build
|
||||
# Change the timestamp of built files for better caching
|
||||
RUN find public -type f -exec touch -t 197001010000.00 {} +
|
||||
|
||||
## Base image with cargo-chef and the right cross-compilation toolchain ##
|
||||
# cargo-chef helps with caching dependencies between builds
|
||||
# The image Debian base name (bullseye) must be in sync with the runtime variant (debian11)
|
||||
FROM --platform=${BUILDPLATFORM} docker.io/library/rust:${RUSTC_VERSION}-slim-bullseye AS chef
|
||||
FROM --platform=${BUILDPLATFORM} docker.io/library/rust:${RUSTC_VERSION}-slim-${DEBIAN_VERSION_NAME} AS chef
|
||||
|
||||
# Install x86_64 and aarch64 cross-compiling stack
|
||||
RUN apt update && apt install -y --no-install-recommends \
|
||||
@ -60,6 +75,7 @@ RUN cargo chef cook \
|
||||
|
||||
# Build the rest
|
||||
COPY . .
|
||||
COPY --from=static-files /app/crates/static-files/public /app/crates/static-files/public
|
||||
RUN cargo build \
|
||||
--release \
|
||||
--bin mas-cli \
|
||||
@ -68,8 +84,12 @@ RUN cargo build \
|
||||
# Move the binary to avoid having to guess its name in the next stage
|
||||
RUN mv target/$(/docker-arch-to-rust-target.sh "${TARGETPLATFORM}")/release/mas-cli /mas-cli
|
||||
|
||||
## Runtime stage ##
|
||||
# The image Debian base name (bullseye) must be in sync with the runtime variant (debian11)
|
||||
FROM --platform=${TARGETPLATFORM} gcr.io/distroless/cc-debian11:nonroot
|
||||
## Runtime stage, debug variant ##
|
||||
FROM --platform=${TARGETPLATFORM} gcr.io/distroless/cc-debian${DEBIAN_VERSION}:debug-nonroot AS debug
|
||||
COPY --from=builder /mas-cli /mas-cli
|
||||
ENTRYPOINT ["/mas-cli"]
|
||||
|
||||
## Runtime stage ##
|
||||
FROM --platform=${TARGETPLATFORM} gcr.io/distroless/cc-debian${DEBIAN_VERSION}:nonroot
|
||||
COPY --from=builder /mas-cli /mas-cli
|
||||
ENTRYPOINT ["/mas-cli"]
|
||||
|
@ -8,7 +8,15 @@ See the [Documentation](https://matrix-org.github.io/matrix-authentication-servi
|
||||
## Running
|
||||
|
||||
- [Install Rust and Cargo](https://www.rust-lang.org/learn/get-started)
|
||||
- [Install Node.js and npm](https://nodejs.org/)
|
||||
- Clone this repository
|
||||
- Generate the frontend:
|
||||
```sh
|
||||
cd crates/static-files
|
||||
npm ci
|
||||
npm run build
|
||||
cd ../..
|
||||
```
|
||||
- Generate the sample config via `cargo run -- config generate > config.yaml`
|
||||
- Run the database migrations via `cargo run -- database migrate`
|
||||
- Run the server via `cargo run -- server -c config.yaml`
|
||||
|
@ -41,7 +41,7 @@ indoc = "1.0.3"
|
||||
|
||||
[features]
|
||||
default = ["otlp", "jaeger", "zipkin"]
|
||||
dev = ["mas-templates/dev"]
|
||||
dev = ["mas-templates/dev", "mas-core/dev"]
|
||||
# Enable OpenTelemetry OTLP exporter. Requires "protoc"
|
||||
otlp = ["opentelemetry-otlp"]
|
||||
# Enable OpenTelemetry Jaeger exporter and propagator.
|
||||
|
@ -12,6 +12,8 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -26,12 +28,16 @@ fn default_http_address() -> String {
|
||||
pub struct HttpConfig {
|
||||
#[serde(default = "default_http_address")]
|
||||
pub address: String,
|
||||
|
||||
#[serde(default)]
|
||||
pub web_root: Option<PathBuf>,
|
||||
}
|
||||
|
||||
impl Default for HttpConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
address: default_http_address(),
|
||||
web_root: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,9 @@ authors = ["Quentin Gliech <quenting@element.io>"]
|
||||
edition = "2018"
|
||||
license = "Apache-2.0"
|
||||
|
||||
[features]
|
||||
dev = ["mas-static-files/dev", "mas-templates/dev"]
|
||||
|
||||
[dependencies]
|
||||
# Async runtime
|
||||
tokio = { version = "1.14.0", features = ["full"] }
|
||||
@ -63,6 +66,7 @@ oauth2-types = { path = "../oauth2-types", features = ["sqlx_type"] }
|
||||
mas-config = { path = "../config" }
|
||||
mas-data-model = { path = "../data-model" }
|
||||
mas-templates = { path = "../templates" }
|
||||
mas-static-files = { path = "../static-files" }
|
||||
|
||||
[dev-dependencies]
|
||||
indoc = "1.0.3"
|
||||
|
@ -15,6 +15,7 @@
|
||||
#![allow(clippy::unused_async)] // Some warp filters need that
|
||||
|
||||
use mas_config::RootConfig;
|
||||
use mas_static_files::filter as static_files;
|
||||
use mas_templates::Templates;
|
||||
use sqlx::PgPool;
|
||||
use warp::{filters::BoxedFilter, Filter, Reply};
|
||||
@ -31,7 +32,8 @@ pub fn root(
|
||||
templates: &Templates,
|
||||
config: &RootConfig,
|
||||
) -> BoxedFilter<(impl Reply,)> {
|
||||
health(pool)
|
||||
static_files(config.http.web_root.clone())
|
||||
.or(health(pool))
|
||||
.or(oauth2(pool, templates, &config.oauth2, &config.cookies))
|
||||
.or(views(
|
||||
pool,
|
||||
|
2
crates/static-files/.gitignore
vendored
Normal file
2
crates/static-files/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/node_modules/
|
||||
/public/tailwind.css
|
15
crates/static-files/Cargo.toml
Normal file
15
crates/static-files/Cargo.toml
Normal file
@ -0,0 +1,15 @@
|
||||
[package]
|
||||
name = "mas-static-files"
|
||||
version = "0.1.0"
|
||||
authors = ["Quentin Gliech <quenting@element.io>"]
|
||||
edition = "2018"
|
||||
license = "Apache-2.0"
|
||||
|
||||
[features]
|
||||
dev = []
|
||||
|
||||
[dependencies]
|
||||
headers = "0.3.5"
|
||||
mime_guess = "2.0.3"
|
||||
rust-embed = "6.3.0"
|
||||
warp = "0.3.2"
|
3996
crates/static-files/package-lock.json
generated
Normal file
3996
crates/static-files/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
15
crates/static-files/package.json
Normal file
15
crates/static-files/package.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"name": "static-files",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "tailwindcss --postcss -o public/tailwind.css",
|
||||
"start": "tailwindcss --postcss -o public/tailwind.css --watch"
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"devDependencies": {
|
||||
"autoprefixer": "^10.4.0",
|
||||
"cssnano": "^5.0.12",
|
||||
"postcss": "^8.4.4",
|
||||
"tailwindcss": "^2.2.19"
|
||||
}
|
||||
}
|
21
crates/static-files/postcss.config.js
Normal file
21
crates/static-files/postcss.config.js
Normal file
@ -0,0 +1,21 @@
|
||||
// Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
cssnano: {},
|
||||
}
|
||||
}
|
113
crates/static-files/src/lib.rs
Normal file
113
crates/static-files/src/lib.rs
Normal file
@ -0,0 +1,113 @@
|
||||
// Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#![forbid(unsafe_code)]
|
||||
#![deny(clippy::all)]
|
||||
#![deny(rustdoc::broken_intra_doc_links)]
|
||||
#![warn(clippy::pedantic)]
|
||||
#![allow(clippy::module_name_repetitions)]
|
||||
#![allow(clippy::missing_panics_doc)]
|
||||
#![allow(clippy::missing_errors_doc)]
|
||||
#![allow(clippy::unused_async)]
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
use warp::{filters::BoxedFilter, Filter, Reply};
|
||||
|
||||
#[cfg(not(feature = "dev"))]
|
||||
mod builtin {
|
||||
use std::{convert::TryInto, fmt::Write, str::FromStr};
|
||||
|
||||
use headers::{ContentLength, ContentType, ETag, HeaderMapExt};
|
||||
use rust_embed::RustEmbed;
|
||||
use warp::{
|
||||
filters::BoxedFilter, hyper::StatusCode, path::Tail, reply::Response, Filter, Rejection,
|
||||
Reply,
|
||||
};
|
||||
|
||||
#[derive(RustEmbed)]
|
||||
#[folder = "public/"]
|
||||
struct Asset;
|
||||
|
||||
async fn serve_embed(
|
||||
path: Tail,
|
||||
if_none_match: Option<String>,
|
||||
) -> Result<Box<dyn Reply>, Rejection> {
|
||||
let path = path.as_str();
|
||||
let asset = Asset::get(path).ok_or_else(warp::reject::not_found)?;
|
||||
|
||||
// TODO: this etag calculation is ugly
|
||||
let etag = {
|
||||
let mut s = String::with_capacity(32 * 2 + 2);
|
||||
write!(s, "\"").unwrap();
|
||||
for b in asset.metadata.sha256_hash() {
|
||||
write!(s, "{:02x}", b).unwrap();
|
||||
}
|
||||
write!(s, "\"").unwrap();
|
||||
s
|
||||
};
|
||||
|
||||
if Some(&etag) == if_none_match.as_ref() {
|
||||
return Ok(Box::new(StatusCode::NOT_MODIFIED));
|
||||
};
|
||||
|
||||
let len = asset.data.len().try_into().unwrap();
|
||||
let mime = mime_guess::from_path(path).first_or_octet_stream();
|
||||
|
||||
let mut res = Response::new(asset.data.into());
|
||||
res.headers_mut().typed_insert(ContentType::from(mime));
|
||||
res.headers_mut().typed_insert(ContentLength(len));
|
||||
res.headers_mut()
|
||||
.typed_insert(ETag::from_str(&etag).unwrap());
|
||||
Ok(Box::new(res))
|
||||
}
|
||||
|
||||
pub(crate) fn filter() -> BoxedFilter<(impl Reply,)> {
|
||||
warp::path::tail()
|
||||
.and(warp::filters::header::optional("If-None-Match"))
|
||||
.and_then(serve_embed)
|
||||
.boxed()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "dev")]
|
||||
mod builtin {
|
||||
use std::path::PathBuf;
|
||||
|
||||
use warp::{filters::BoxedFilter, Reply};
|
||||
|
||||
pub(crate) fn filter() -> BoxedFilter<(impl Reply,)> {
|
||||
let path = PathBuf::from(format!("{}/public", env!("CARGO_MANIFEST_DIR")));
|
||||
super::filter_for_path(path)
|
||||
}
|
||||
}
|
||||
|
||||
fn box_reply(reply: impl Reply + 'static) -> Box<dyn Reply> {
|
||||
Box::new(reply)
|
||||
}
|
||||
|
||||
fn filter_for_path(path: PathBuf) -> BoxedFilter<(impl Reply,)> {
|
||||
warp::fs::dir(path).boxed()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn filter(path: Option<PathBuf>) -> BoxedFilter<(Box<dyn Reply>,)> {
|
||||
let f = self::builtin::filter();
|
||||
|
||||
if let Some(path) = path {
|
||||
f.or(filter_for_path(path)).map(box_reply).boxed()
|
||||
} else {
|
||||
f.map(box_reply).boxed()
|
||||
}
|
||||
}
|
26
crates/static-files/tailwind.config.js
Normal file
26
crates/static-files/tailwind.config.js
Normal file
@ -0,0 +1,26 @@
|
||||
// Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
module.exports = {
|
||||
mode: "jit",
|
||||
purge: ["../templates/src/res/*.html"],
|
||||
darkMode: false,
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
variants: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
@ -21,6 +21,7 @@ limitations under the License.
|
||||
<title>{% block title %}matrix-authentication-service{% endblock title %}</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.3/css/bulma.min.css">
|
||||
<link rel="stylesheet" href="/tailwind.css">
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar is-dark" role="navigation" aria-label="main navigation">
|
||||
|
Reference in New Issue
Block a user