Skip to content
Merged
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
5 changes: 4 additions & 1 deletion frontend/index.html
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>%TITLE%</title>
<link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>%FAVICON%</text></svg>" />
<link rel="icon" href="/favicon.svg" />
<link rel="stylesheet" href="/static/%STYLESHEET%">
</head>

<body>
<div id="root"></div>
<script src="/static/%SCRIPT%"></script>
</body>

</html>
2 changes: 0 additions & 2 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import '@mantine/core/styles.css';

import { Alert, Box, Flex, MantineProvider, Title } from '@mantine/core';
import { useEtes } from "./useEtes";
import theme from './theme';
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/Commits.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export default function Commits({ state, dispatch }: CommitsProps) {
<Table.Tr>
<Table.Th scope="row">Message</Table.Th>
<Table.Td maw="180px">
<Anchor href={commit.url} target="_blank" style={{ whiteSpace: 'nowrap' }}>
<Anchor href={commit.url} target="_blank" className="no-wrap">
<Tooltip label={commit.message}>
<Text size="sm" truncate="end">
{commit.message}
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/DateTime.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export default function DateTime({ date }: { date: string }) {

return (
<Tooltip label={full}>
<time dateTime={date} style={{ cursor: 'default' }}>{timeAgo(new Date(date))}</time>
<time dateTime={date} className="cursor-default">{timeAgo(new Date(date))}</time>
</Tooltip>
);
}
2 changes: 1 addition & 1 deletion frontend/src/PullRequest.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ interface PullRequestProps {
export function PullRequest({ baseUrl, number, title }: PullRequestProps) {
const badge = (
<Anchor href={`${baseUrl}/pull/${number}`} target="_blank">
<Badge mr="md" style={{ cursor: 'pointer' }}>#{number}</Badge>
<Badge mr="md" className="cursor-pointer">#{number}</Badge>
</Anchor>
);

Expand Down
2 changes: 1 addition & 1 deletion frontend/src/PullTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ export function PullTable({ state, dispatch }: PullTableProps) {
>
<Badge
variant="outline"
style={{ cursor: 'pointer' }}
className="cursor-pointer"
color={statusColor(pull.status)}
leftSection={statusIcon(pull.status)}
>
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/Releases.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export default function Releases({ state, dispatch }: ReleasesProps) {
<Table.Tr>
<Table.Th scope="row">Tag</Table.Th>
<Table.Td maw="180px">
<Anchor href={release.url} target="_blank" style={{ whiteSpace: 'nowrap' }}>
<Anchor href={release.url} target="_blank" className="no-wrap">
<Text size="sm" truncate="end">
{release.tagName}
</Text>
Expand Down
11 changes: 11 additions & 0 deletions frontend/src/index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
.no-wrap {
white-space: nowrap;
}

.cursor-pointer {
cursor: pointer;
}

.cursor-default {
cursor: default;
}
3 changes: 3 additions & 0 deletions frontend/src/main.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import '@mantine/core/styles.css';
import './index.css';

import React from 'react';
import { createRoot } from 'react-dom/client';
import { App } from './App.tsx';
Expand Down
52 changes: 51 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ use axum::{
Router,
body::Body,
extract::FromRef,
extract::State,
http::{HeaderValue, header, header::CONTENT_SECURITY_POLICY},
middleware::{self, Next},
routing::{any, get, put},
};
use cookie::Key;
Expand Down Expand Up @@ -117,10 +120,28 @@ impl AppStateContainer {
}
}

fn build_csp_header() -> String {
[
"default-src 'none'",
"base-uri 'none'",
"frame-ancestors 'none'",
"object-src 'none'",
"script-src 'self'",
"style-src 'self' 'unsafe-inline'",
"img-src 'self' https://avatars.githubusercontent.com",
"font-src 'self'",
"connect-src 'self'",
"manifest-src 'none'",
"form-action 'self'",
]
.join("; ")
}

async fn app(with_frontend: bool) -> Result<(AppState, Router)> {
let state: AppState = AppStateContainer::new()?.into();

let mut app = Router::new()
.route("/favicon.svg", get(favicon_svg))
.route("/etes/login", get(auth::login))
.route("/etes/logout", get(auth::logout))
.route("/etes/authorize", get(auth::authorize))
Expand All @@ -136,13 +157,42 @@ async fn app(with_frontend: bool) -> Result<(AppState, Router)> {
let index =
include_str!("../frontend/index.html").replace("%FAVICON%", &state.config.favicon);
let frontend = spaxum::load!(&state.config.title).set_html_template(index);

app = app.merge(frontend.router());

// add CSP for release builds
if !cfg!(debug_assertions) {
let csp = build_csp_header();
let csp_header = HeaderValue::from_str(&csp).expect("CSP header value must be ASCII");
app = app.layer(middleware::from_fn(move |req, next: Next| {
let csp_header = csp_header.clone();
async move {
let mut res = next.run(req).await;
res.headers_mut()
.insert(CONTENT_SECURITY_POLICY, csp_header);
res
}
}));
}
}

Ok((state, app))
}

async fn favicon_svg(State(state): State<AppState>) -> impl axum::response::IntoResponse {
let svg = format!(
"<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 100 100\"><text y=\".9em\" font-size=\"90\">{}</text></svg>",
state.config.favicon
);

(
[(
header::CONTENT_TYPE,
HeaderValue::from_static("image/svg+xml; charset=utf-8"),
)],
svg,
)
}

#[tokio::main]
async fn main() -> Result<()> {
tracing_subscriber::registry()
Expand Down
Loading