Skip to content

Commit 80fc86d

Browse files
committed
Add Login Page
1 parent 35d21ae commit 80fc86d

36 files changed

+2232
-1042
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@ Bob Management GUI changelog
1212
- CI/CD configuration (#11)
1313
- Logger Initialization (#14)
1414
- Login Page, backend (#16)
15+
- Login Page, frontend (#17)

backend/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ utoipa-swagger-ui = { version = "4.0", features = ["axum"], optional = true }
4545
utoipa-redoc = { version = "1.0", features = ["axum"], optional = true }
4646
utoipa-rapidoc = { version = "1.0", features = ["axum"], optional = true }
4747

48+
## Transpiling
49+
tsync = "2.0"
50+
4851
## CLI
4952
cli = { path = "../cli" }
5053

backend/src/main.rs

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
)]
66

77
use bob_management::main::prelude::*;
8+
use cli::Config;
89

910
const FRONTEND_FOLDER: &str = "frontend";
1011

@@ -19,17 +20,12 @@ async fn main() -> Result<(), AppError> {
1920
let _guard = logger.init_logger().unwrap();
2021
tracing::info!("Logger: {logger:?}");
2122

22-
let cors: CorsLayer = config.get_cors_configuration();
23-
tracing::info!("CORS: {cors:?}");
24-
2523
let addr = config.address;
2624
tracing::info!("Listening on {addr}");
2725

28-
let app = router(cors);
26+
let app = router(&config);
2927
#[cfg(all(feature = "swagger", debug_assertions))]
30-
let app = app
31-
.merge(bob_management::openapi_doc())
32-
.layer(Extension(RequestTimeout::from(config.request_timeout)));
28+
let app = app.merge(bob_management::openapi_doc());
3329

3430
axum::Server::bind(&addr)
3531
.serve(app.into_make_service())
@@ -41,7 +37,7 @@ async fn main() -> Result<(), AppError> {
4137
}
4238

4339
#[allow(clippy::unwrap_used, clippy::expect_used)]
44-
fn router(cors: CorsLayer) -> Router {
40+
fn router(config: &Config) -> Router {
4541
let session_store = MemoryStore::default();
4642
let session_service = ServiceBuilder::new()
4743
.layer(HandleErrorLayer::new(|err: BoxError| async move {
@@ -50,7 +46,8 @@ fn router(cors: CorsLayer) -> Router {
5046
}))
5147
.layer(
5248
SessionManagerLayer::new(session_store)
53-
.with_expiry(tower_sessions::Expiry::OnSessionEnd),
49+
.with_expiry(tower_sessions::Expiry::OnSessionEnd)
50+
.with_http_only(false),
5451
);
5552

5653
let user_store: InMemorySessionStore<Uuid, BobUser> = InMemorySessionStore::default();
@@ -69,7 +66,8 @@ fn router(cors: CorsLayer) -> Router {
6966
ApiV1::to_path(),
7067
api_router_v1(auth_state.clone())
7168
.expect("couldn't get API routes")
72-
.layer(ServiceBuilder::new().layer(cors)),
69+
.layer(ServiceBuilder::new().layer(config.get_cors_configuration()))
70+
.layer(Extension(RequestTimeout::from(config.request_timeout))),
7371
)
7472
.layer(session_service)
7573
.with_state(auth_state)

backend/src/models/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ pub mod prelude {
66
pub use crate::prelude::*;
77
pub use hyper::Uri;
88
pub use std::{net::SocketAddr, time::Duration};
9+
pub use tsync::tsync;
910
}

backend/src/models/shared.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ impl TryFrom<Hostname> for SocketAddr {
5353
type Error = std::net::AddrParseError;
5454

5555
fn try_from(value: Hostname) -> Result<Self, Self::Error> {
56-
value.to_string().parse()
56+
value.0.to_string().parse()
5757
}
5858
}
5959

@@ -71,6 +71,7 @@ impl ToString for Hostname {
7171
)]
7272
#[cfg_attr(all(feature = "swagger", debug_assertions),
7373
schema(example = json!({"hostname": "0.0.0.0:7000", "credentials": {"login": "archeoss", "password": "12345"}})))]
74+
#[tsync]
7475
pub struct BobConnectionData {
7576
/// Address to connect to
7677
pub hostname: Hostname,
@@ -84,6 +85,7 @@ pub struct BobConnectionData {
8485
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Serialize, Deserialize)]
8586
#[cfg_attr(all(feature = "swagger", debug_assertions), derive(ToSchema))]
8687
#[cfg_attr(all(feature = "swagger", debug_assertions), schema(example = json!({"login": "archeoss", "password": "12345"})))]
88+
#[tsync]
8789
pub struct Credentials {
8890
/// Login used during auth
8991
pub login: String,

config.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
address: 0.0.0.0:9000
2+
request-timeout: 1s
23
logger:
34
trace-level: INFO
45
file:

frontend/.eslintrc.cjs

Lines changed: 108 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ module.exports = {
55
parserOptions: {
66
ecmaVersion: 2020, // Allows for the parsing of modern ECMAScript features
77
sourceType: 'module', // Allows for the use of imports
8+
extraFileExtensions: ['.astro'],
89
ecmaFeatures: {
910
jsx: true, // Allows for the parsing of JSX
1011
},
@@ -16,14 +17,118 @@ module.exports = {
1617
},
1718
extends: [
1819
'eslint:recommended',
19-
'plugin:@typescript-eslint/recommended', // Uses the recommended rules from the @typescript-eslint/eslint-plugin
20+
'plugin:@typescript-eslint/recommended',
2021
'plugin:react/recommended',
2122
'plugin:react-hooks/recommended',
22-
'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:import/recommended',
24+
'plugin:prettier/recommended',
25+
'plugin:astro/recommended',
2326
],
2427
plugins: ['react-refresh'],
25-
ignorePatterns: ['dist', '.eslintrc.cjs'],
28+
ignorePatterns: ['dist', '.eslintrc.cjs', 'rust.d.ts'],
2629
rules: {
2730
'react-refresh/only-export-components': ['warn', { allowConstantExport: true }],
2831
},
32+
overrides: [
33+
// Configuration for mjs,cjs files
34+
{
35+
files: ['*.mjs', '*.cjs'],
36+
extends: ['plugin:prettier/recommended'],
37+
rules: {
38+
'import/no-extraneous-dependencies': 'off', // mjs is only used by Astro for configuration, false positive
39+
'import/no-unresolved': 'off', // Also false positive with mjs file
40+
},
41+
},
42+
// Configuration for TypeScript files
43+
{
44+
parser: '@typescript-eslint/parser',
45+
files: ['*.ts', '*.tsx'],
46+
47+
plugins: ['@typescript-eslint', 'react', 'unused-imports', 'tailwindcss', 'simple-import-sort'],
48+
extends: ['plugin:tailwindcss/recommended', 'airbnb-typescript/base', 'plugin:prettier/recommended'],
49+
parserOptions: {
50+
project: './tsconfig.json',
51+
},
52+
rules: {
53+
'import/extensions': [
54+
'error',
55+
'ignorePackages',
56+
{
57+
js: 'never',
58+
jsx: 'never',
59+
ts: 'never',
60+
tsx: 'never',
61+
'': 'never',
62+
},
63+
], // Avoid missing file extension errors when using '@/' alias
64+
'react/destructuring-assignment': 'off', // Vscode doesn't support automatically destructuring, it's a pain to add a new variable
65+
'react/require-default-props': 'off', // Allow non-defined react props as undefined
66+
'react/jsx-props-no-spreading': 'off', // _app.tsx uses spread operator and also, react-hook-form
67+
'@typescript-eslint/comma-dangle': 'off', // Avoid conflict rule between Eslint and Prettier
68+
'@typescript-eslint/consistent-type-imports': 'error', // Ensure `import type` is used when it's necessary
69+
'import/prefer-default-export': 'off', // Named export is easier to refactor automatically
70+
'tailwindcss/classnames-order': [
71+
'warn',
72+
{
73+
officialSorting: true,
74+
},
75+
], // Follow the same ordering as the official plugin `prettier-plugin-tailwindcss`
76+
'simple-import-sort/imports': 'error', // Import configuration for `eslint-plugin-simple-import-sort`
77+
'simple-import-sort/exports': 'error', // Export configuration for `eslint-plugin-simple-import-sort`
78+
'@typescript-eslint/no-unused-vars': 'off',
79+
'unused-imports/no-unused-imports': 'error',
80+
'unused-imports/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
81+
},
82+
},
83+
// Configuration for Astro
84+
{
85+
files: ['*.astro'],
86+
plugins: ['@typescript-eslint', 'react', 'unused-imports', 'tailwindcss', 'simple-import-sort'],
87+
extends: ['plugin:tailwindcss/recommended', 'airbnb-typescript/base', 'plugin:prettier/recommended'],
88+
parser: 'astro-eslint-parser',
89+
parserOptions: {
90+
parser: '@typescript-eslint/parser',
91+
},
92+
rules: {
93+
'import/extensions': [
94+
'error',
95+
'ignorePackages',
96+
{
97+
js: 'never',
98+
jsx: 'never',
99+
ts: 'never',
100+
tsx: 'never',
101+
'': 'never',
102+
},
103+
], // Avoid missing file extension errors in .astro files
104+
'import/no-unresolved': [
105+
'error',
106+
{
107+
ignore: ['@/*'],
108+
},
109+
], // Disable no-unresolved rule for .astro files
110+
'react/jsx-filename-extension': [1, { extensions: ['.astro'] }], // Accept jsx in astro files
111+
'react/destructuring-assignment': 'off', // Vscode doesn't support automatically destructuring, it's a pain to add a new variable
112+
'react/require-default-props': 'off', // Allow non-defined react props as undefined
113+
'react/jsx-props-no-spreading': 'off', // _app.tsx uses spread operator and also, react-hook-form
114+
'@typescript-eslint/comma-dangle': 'off', // Avoid conflict rule between Eslint and Prettier
115+
'@typescript-eslint/consistent-type-imports': 'error', // Ensure `import type` is used when it's necessary
116+
'import/prefer-default-export': 'off', // Named export is easier to refactor automatically
117+
'tailwindcss/classnames-order': [
118+
'warn',
119+
{
120+
officialSorting: true,
121+
},
122+
], // Follow the same ordering as the official plugin `prettier-plugin-tailwindcss`
123+
'simple-import-sort/imports': 'error', // Import configuration for `eslint-plugin-simple-import-sort`
124+
'simple-import-sort/exports': 'error', // Export configuration for `eslint-plugin-simple-import-sort`
125+
'@typescript-eslint/no-unused-vars': 'off',
126+
'unused-imports/no-unused-imports': 'error',
127+
'unused-imports/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
128+
},
129+
globals: {
130+
Astro: 'readonly',
131+
},
132+
},
133+
],
29134
};

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 = "bindings.rs"
1414
crate-type = ["lib"]
1515

1616
[build-dependencies]

frontend/astro.config.mjs

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

66
// https://astro.build/config
77
export default defineConfig({
8-
integrations: [react(), tailwind()],
9-
outDir: './frontend',
8+
// output: 'server',
9+
integrations: [
10+
react({
11+
experimentalReactChildren: true,
12+
}),
13+
tailwind(),
14+
],
15+
// adapter: node({
16+
// mode: 'standalone',
17+
// }),
18+
outDir: './frontend',
1019
});

frontend/bindings.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// Some structs that must be redefined for transpiling without changing actual types on backend
2+
3+
use tsync::tsync;
4+
5+
#[tsync]
6+
pub type Hostname = String;

frontend/build.rs

Lines changed: 9 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);
@@ -70,6 +73,10 @@ pub fn build_frontend() {
7073
// #[cfg(not(debug_assertions))]
7174
shell("yarn");
7275

76+
let mut project_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
77+
project_dir.push(FRONTEND_DIR);
78+
std::fs::remove_dir_all(project_dir);
79+
7380
// Only build frontend when building a release
7481
// #[cfg(not(debug_assertions))]
7582
shell("yarn build");
@@ -82,6 +89,8 @@ pub fn move_frontend() {
8289
target.pop();
8390
target.push(FRONTEND_DIR);
8491

92+
// Note: sometimes, build can fail if the previous build is still present
93+
// What a lovely language....
8594
let mut project_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
8695
project_dir.push(FRONTEND_DIR);
8796

frontend/e2e/example.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { test, expect } from '@playwright/test';
1+
import { expect, test } from '@playwright/test';
22

33
test('has title', async ({ page }) => {
44
await page.goto('https://playwright.dev/');

frontend/frontend.rs

Lines changed: 0 additions & 1 deletion
This file was deleted.

0 commit comments

Comments
 (0)