Skip to content

Commit ad5fa9b

Browse files
committed
WIP
1 parent 2fd674a commit ad5fa9b

25 files changed

+2234
-703
lines changed

backend/Cargo.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ thiserror = "1.0"
3737
## General
3838
tokio = { version = "1.32", features = ["rt", "macros", "rt-multi-thread" ] }
3939
hyper = { version = "0.14", features = ["http2", "client"] }
40-
nutype = { version = "0.3", features = ["serde", "regex"] }
40+
prae = { version = "0.8", features = ["serde"] }
4141
lazy_static = "1.4"
4242
regex = "1.10"
4343
serde = { version = "1.0", features = ["derive"] }
@@ -59,6 +59,9 @@ cli = { path = "../cli" }
5959
## Frontend
6060
frontend = { path = "../frontend", optional = true }
6161

62+
## Transpiling
63+
tsync = "2.0"
64+
6265
[features]
6366
default = [ "swagger", "frontend-bundle" ]
6467
swagger = [ "dep:utoipa", "dep:utoipa-swagger-ui" , "dep:utoipa-redoc", "dep:utoipa-rapidoc" ]

backend/src/connector/mod.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use axum::headers::{authorization::Basic, Authorization};
1515
use error_stack::ResultExt;
1616
use frunk::hlist;
1717
use hyper::StatusCode;
18+
use prae::Wrapper;
1819
use std::{collections::HashMap, sync::Arc};
1920
use thiserror::Error;
2021

@@ -132,7 +133,8 @@ impl BobClient {
132133

133134
let context: ClientContext = hlist![timeout, auth, XSpanIdString::default()];
134135
let client = Box::new(
135-
Client::try_new_http(&hostname.to_string()).change_context(ClientError::InitClient)?,
136+
Client::try_new_http(&hostname.get().to_string())
137+
.change_context(ClientError::InitClient)?,
136138
);
137139
let nodes = client
138140
.with_context(context.clone())
@@ -158,11 +160,11 @@ impl BobClient {
158160
}
159161
hostname
160162
.ok()
161-
.map(|hostname| (name, Client::try_new_http(&hostname.to_string())))
163+
.map(|hostname| (name, Client::try_new_http(&hostname.get().to_string())))
162164
})
163165
.filter_map(|(name, client)| {
164166
if client.is_err() {
165-
tracing::warn!("couldn't create client for {hostname}");
167+
tracing::warn!("couldn't create client for {}", hostname.get());
166168
}
167169
client.ok().map(|client| {
168170
(
@@ -173,7 +175,8 @@ impl BobClient {
173175
})
174176
.collect();
175177
let client = Box::new(
176-
Client::try_new_http(&hostname.to_string()).change_context(ClientError::InitClient)?,
178+
Client::try_new_http(&hostname.get().to_string())
179+
.change_context(ClientError::InitClient)?,
177180
);
178181
Ok(Self {
179182
hostname: bob_data.hostname,

backend/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ pub mod services;
1717
#[cfg_attr(all(feature = "swagger", debug_assertions), openapi(
1818
paths(root, services::auth::login, services::auth::logout),
1919
components(
20-
schemas(models::shared::Credentials, models::shared::HostnameApi, models::shared::BobConnectionData)
20+
schemas(models::shared::Credentials, models::shared::Hostname, models::shared::BobConnectionData)
2121
),
2222
tags(
2323
(name = "bob", description = "BOB management API")

backend/src/main.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
#![allow(clippy::multiple_crate_versions)]
22

3-
use axum::Router;
3+
use axum::{Extension, Router};
44
use axum_login::{memory_store::MemoryStore as AuthMemoryStore, AuthLayer};
55
use axum_sessions::async_session::MemoryStore as SessionMemoryStore;
66
use axum_sessions::SessionLayer;
7+
use backend::models::shared::RequestTimeout;
8+
use backend::router::{NoApi, RouterApiExt};
79
use backend::services::auth::SocketBobMemoryStore;
810
use backend::{
911
config::ConfigExt,
1012
prelude::*,
1113
root,
12-
router::{ApiV1, ApiVersion, NoApi, RouterApiExt},
14+
router::{ApiV1, ApiVersion},
1315
services::api_router_v1,
1416
ApiDoc,
1517
};
@@ -47,7 +49,11 @@ async fn main() -> Result<(), AppError> {
4749

4850
let app = router(cors);
4951
#[cfg(all(feature = "swagger", debug_assertions))]
50-
let app = app.merge(backend::openapi_doc());
52+
let app = app
53+
.merge(backend::openapi_doc())
54+
.layer(Extension(RequestTimeout::from_millis(
55+
config.request_timeout.as_millis() as u64,
56+
)));
5157

5258
axum::Server::bind(&addr)
5359
.serve(app.into_make_service())

backend/src/models/shared.rs

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,22 @@
1-
use nutype::nutype;
1+
use lazy_static::lazy_static;
2+
use prae::{ConstructionError, Wrapper};
3+
use regex::Regex;
24
use serde::{Deserialize, Serialize};
35
use std::{net::SocketAddr, time::Duration};
6+
use tsync::tsync;
47
use utoipa::{IntoParams, ToSchema};
58

6-
#[nutype(sanitize(trim, lowercase) validate(not_empty, regex = r"(https?)?(?::[\/]{2})?([^\:]+):(([1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$)"))]
7-
#[derive(Display, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
8-
pub struct Hostname(String);
9+
// #[nutype(sanitize(trim, lowercase) validate(not_empty, regex = r"(https?)?(?::[\/]{2})?([^\:]+):(([1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$)"))]
10+
lazy_static! {
11+
static ref HOSTNAME_REGEX: Regex = Regex::new(r"^(https?)?(?::[\/]{2})?([^\:]+):(([1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$)$").unwrap();
12+
}
913

10-
#[derive(ToSchema)]
11-
#[schema(as = Hostname)]
12-
pub struct HostnameApi(String);
14+
prae::define! {
15+
#[derive(ToSchema, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
16+
pub Hostname: String;
17+
adjust |host| *host = host.trim().to_owned();
18+
ensure |host| !host.is_empty() && HOSTNAME_REGEX.is_match(host);
19+
}
1320

1421
impl Hostname {
1522
/// Can be safely unwraped thanks to regex
@@ -27,7 +34,7 @@ impl Hostname {
2734
}
2835

2936
impl TryFrom<SocketAddr> for Hostname {
30-
type Error = HostnameError;
37+
type Error = ConstructionError<Self>;
3138

3239
fn try_from(value: SocketAddr) -> Result<Self, Self::Error> {
3340
Self::new(value.to_string())
@@ -38,11 +45,12 @@ impl TryFrom<Hostname> for SocketAddr {
3845
type Error = std::net::AddrParseError;
3946

4047
fn try_from(value: Hostname) -> Result<Self, Self::Error> {
41-
value.to_string().parse()
48+
value.get().to_string().parse()
4249
}
4350
}
4451

4552
/// Data needed to connect to a BOB cluster
53+
#[tsync]
4654
#[derive(
4755
IntoParams, ToSchema, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize,
4856
)]
@@ -57,6 +65,7 @@ pub struct BobConnectionData {
5765
}
5866

5967
/// Optional auth credentials for a BOB cluster
68+
#[tsync]
6069
#[derive(
6170
ToSchema, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Serialize, Deserialize,
6271
)]

frontend/.eslintrc.cjs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,30 @@ module.exports = {
2020
'plugin:react/recommended',
2121
'plugin:react-hooks/recommended',
2222
'plugin:prettier/recommended', // Enables eslint-plugin-prettier and eslint-config-prettier. This will display prettier errors as ESLint errors. Make sure this is always the last configuration in the extends array.
23+
'plugin:astro/recommended', //
2324
],
2425
plugins: ['react-refresh'],
2526
ignorePatterns: ['dist', '.eslintrc.cjs'],
2627
rules: {
2728
'react-refresh/only-export-components': ['warn', { allowConstantExport: true }],
2829
},
30+
// overrides: [
31+
// {
32+
// // Define the configuration for `.astro` file.
33+
// files: ['*.astro'],
34+
// // Allows Astro components to be parsed.
35+
// parser: 'astro-eslint-parser',
36+
// // Parse the script in `.astro` as TypeScript by adding the following configuration.
37+
// // It's the setting you need when using TypeScript.
38+
// parserOptions: {
39+
// parser: '@typescript-eslint/parser',
40+
// extraFileExtensions: ['.astro'],
41+
// },
42+
// rules: {
43+
// // override/add rules settings here, such as:
44+
// // "astro/no-set-html-directive": "error"
45+
// },
46+
// },
47+
// // ...
48+
// ],
2949
};

frontend/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ repository.workspace = true
1010

1111
[lib]
1212
name = "frontend"
13-
path = "frontend.rs"
13+
path = "/dev/null"
1414
crate-type = ["lib"]
1515

1616
[build-dependencies]

frontend/astro.config.mjs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
import { defineConfig } from 'astro/config';
22
import react from '@astrojs/react';
3-
43
import tailwind from '@astrojs/tailwind';
54

65
// https://astro.build/config
76
export default defineConfig({
8-
integrations: [react(), tailwind()],
9-
outDir: './frontend',
7+
integrations: [react(), tailwind()],
8+
outDir: './frontend',
109
});

frontend/build.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,11 @@ pub fn build_types() {
5858
let mut inputs = vec![PathBuf::from(env!("CARGO_MANIFEST_DIR"))];
5959
let mut output = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
6060
inputs[0].pop();
61+
inputs.push(inputs[0].clone());
6162

6263
inputs[0].push("backend");
64+
inputs[1].push("frontend");
65+
inputs[1].push("frontend.rs");
6366
output.push("src/types/rust.d.ts");
6467

6568
tsync::generate_typescript_defs(inputs, output, false);

frontend/frontend.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,4 @@
1+
// Some structs that must be redefined for transpiling without changing actual types on backend
12

3+
#[tsync]
4+
pub type Hostname = String;

frontend/package.json

Lines changed: 45 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,47 @@
11
{
2-
"name": "bob-management-frontend",
3-
"type": "module",
4-
"version": "0.0.1",
5-
"scripts": {
6-
"dev": "astro dev",
7-
"start": "astro dev",
8-
"build": "astro check && tsc --noEmit && astro build",
9-
"preview": "astro preview",
10-
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
11-
"astro": "astro"
12-
},
13-
"dependencies": {
14-
"@astrojs/react": "^3.0.0",
15-
"@astrojs/tailwind": "^5.0.0",
16-
"@astrojs/check": "^0.2.0",
17-
"@playwright/test": "^1.30.0",
18-
"@types/react": "^18.0.21",
19-
"@types/react-dom": "^18.0.6",
20-
"astro": "^3.0.2",
21-
"react": "^18.0.0",
22-
"react-dom": "^18.0.0",
23-
"tailwindcss": "^3.0.24",
24-
"typescript": "^5.2.2",
25-
"vite": "^4.4.9"
26-
},
27-
"devDependencies": {
28-
"eslint": "^8.48.0",
29-
"eslint-config-prettier": "^9.0.0",
30-
"eslint-plugin-prettier": "^5.0.0",
31-
"eslint-plugin-react-hooks": "^4.6.0",
32-
"eslint-plugin-react-refresh": "^0.4.3",
33-
"prettier": "^3.0.2"
34-
}
2+
"name": "bob-management-frontend",
3+
"type": "module",
4+
"version": "0.0.1",
5+
"scripts": {
6+
"dev": "astro dev",
7+
"start": "astro dev",
8+
"build": "astro check && tsc --noEmit && astro build",
9+
"preview": "astro preview",
10+
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
11+
"astro": "astro"
12+
},
13+
"dependencies": {
14+
"@astrojs/check": "^0.2.0",
15+
"@astrojs/react": "^3.0.0",
16+
"@astrojs/tailwind": "^5.0.0",
17+
"@astrojs/ts-plugin": "^1.2.0",
18+
"@emotion/react": "^11.11.1",
19+
"@emotion/styled": "^11.11.0",
20+
"@mui/icons-material": "^5.14.0",
21+
"@mui/material": "^5.14.0",
22+
"@mui/x-data-grid": "^6.16.2",
23+
"@playwright/test": "^1.30.0",
24+
"@types/material-ui": "^0.21.12",
25+
"@types/mui-image": "^1.0.2",
26+
"@types/react": "^18.0.21",
27+
"@types/react-dom": "^18.0.6",
28+
"@types/react-router-dom": "^5.3.3",
29+
"astro": "^3.0.2",
30+
"mui-image": "^1.0.7",
31+
"react": "^18.0.0",
32+
"react-dom": "^18.0.0",
33+
"tailwindcss": "^3.0.24",
34+
"typescript": "^5.2.2",
35+
"vite": "^4.4.9"
36+
},
37+
"devDependencies": {
38+
"@typescript-eslint/parser": "^6.7.5",
39+
"eslint": "^8.51.0",
40+
"eslint-config-prettier": "^9.0.0",
41+
"eslint-plugin-astro": "^0.29.1",
42+
"eslint-plugin-prettier": "^5.0.0",
43+
"eslint-plugin-react-hooks": "^4.6.0",
44+
"eslint-plugin-react-refresh": "^0.4.3",
45+
"prettier": "^3.0.2"
46+
}
3547
}

frontend/public/brandmark.svg

Lines changed: 3 additions & 0 deletions
Loading

0 commit comments

Comments
 (0)