Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add login page (Frontend) #17

Open
wants to merge 1 commit into
base: 2-login-page
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ Bob Management GUI changelog
- CI/CD configuration (#11)
- Logger Initialization (#14)
- Login Page, backend (#16)
- Login Page, frontend (#17)
3 changes: 3 additions & 0 deletions backend/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ utoipa-swagger-ui = { version = "4.0", features = ["axum"], optional = true }
utoipa-redoc = { version = "1.0", features = ["axum"], optional = true }
utoipa-rapidoc = { version = "1.0", features = ["axum"], optional = true }

## Transpiling
tsync = "2.0"

## CLI
cli = { path = "../cli" }

Expand Down
18 changes: 8 additions & 10 deletions backend/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
)]

use bob_management::main::prelude::*;
use cli::Config;

const FRONTEND_FOLDER: &str = "frontend";

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

let cors: CorsLayer = config.get_cors_configuration();
tracing::info!("CORS: {cors:?}");

let addr = config.address;
tracing::info!("Listening on {addr}");

let app = router(cors);
let app = router(&config);
#[cfg(all(feature = "swagger", debug_assertions))]
let app = app
.merge(bob_management::openapi_doc())
.layer(Extension(RequestTimeout::from(config.request_timeout)));
let app = app.merge(bob_management::openapi_doc());

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

#[allow(clippy::unwrap_used, clippy::expect_used)]
fn router(cors: CorsLayer) -> Router {
fn router(config: &Config) -> Router {
let session_store = MemoryStore::default();
let session_service = ServiceBuilder::new()
.layer(HandleErrorLayer::new(|err: BoxError| async move {
Expand All @@ -50,7 +46,8 @@ fn router(cors: CorsLayer) -> Router {
}))
.layer(
SessionManagerLayer::new(session_store)
.with_expiry(tower_sessions::Expiry::OnSessionEnd),
.with_expiry(tower_sessions::Expiry::OnSessionEnd)
.with_http_only(false),
);

let user_store: InMemorySessionStore<Uuid, BobUser> = InMemorySessionStore::default();
Expand All @@ -69,7 +66,8 @@ fn router(cors: CorsLayer) -> Router {
ApiV1::to_path(),
api_router_v1(auth_state.clone())
.expect("couldn't get API routes")
.layer(ServiceBuilder::new().layer(cors)),
.layer(ServiceBuilder::new().layer(config.get_cors_configuration()))
.layer(Extension(RequestTimeout::from(config.request_timeout))),
)
.layer(session_service)
.with_state(auth_state)
Expand Down
1 change: 1 addition & 0 deletions backend/src/models/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ pub mod prelude {
pub use crate::prelude::*;
pub use hyper::Uri;
pub use std::{net::SocketAddr, time::Duration};
pub use tsync::tsync;
}
4 changes: 3 additions & 1 deletion backend/src/models/shared.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ impl TryFrom<Hostname> for SocketAddr {
type Error = std::net::AddrParseError;

fn try_from(value: Hostname) -> Result<Self, Self::Error> {
value.to_string().parse()
value.0.to_string().parse()
}
}

Expand All @@ -71,6 +71,7 @@ impl ToString for Hostname {
)]
#[cfg_attr(all(feature = "swagger", debug_assertions),
schema(example = json!({"hostname": "0.0.0.0:7000", "credentials": {"login": "archeoss", "password": "12345"}})))]
#[tsync]
pub struct BobConnectionData {
/// Address to connect to
pub hostname: Hostname,
Expand All @@ -84,6 +85,7 @@ pub struct BobConnectionData {
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Serialize, Deserialize)]
#[cfg_attr(all(feature = "swagger", debug_assertions), derive(ToSchema))]
#[cfg_attr(all(feature = "swagger", debug_assertions), schema(example = json!({"login": "archeoss", "password": "12345"})))]
#[tsync]
pub struct Credentials {
/// Login used during auth
pub login: String,
Expand Down
1 change: 1 addition & 0 deletions config.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
address: 0.0.0.0:9000
request-timeout: 1s
logger:
trace-level: INFO
file:
Expand Down
111 changes: 108 additions & 3 deletions frontend/.eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ module.exports = {
parserOptions: {
ecmaVersion: 2020, // Allows for the parsing of modern ECMAScript features
sourceType: 'module', // Allows for the use of imports
extraFileExtensions: ['.astro'],
ecmaFeatures: {
jsx: true, // Allows for the parsing of JSX
},
Expand All @@ -16,14 +17,118 @@ module.exports = {
},
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended', // Uses the recommended rules from the @typescript-eslint/eslint-plugin
'plugin:@typescript-eslint/recommended',
'plugin:react/recommended',
'plugin:react-hooks/recommended',
'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.
'plugin:import/recommended',
'plugin:prettier/recommended',
'plugin:astro/recommended',
],
plugins: ['react-refresh'],
ignorePatterns: ['dist', '.eslintrc.cjs'],
ignorePatterns: ['dist', '.eslintrc.cjs', 'rust.d.ts'],
rules: {
'react-refresh/only-export-components': ['warn', { allowConstantExport: true }],
},
overrides: [
// Configuration for mjs,cjs files
{
files: ['*.mjs', '*.cjs'],
extends: ['plugin:prettier/recommended'],
rules: {
'import/no-extraneous-dependencies': 'off', // mjs is only used by Astro for configuration, false positive
'import/no-unresolved': 'off', // Also false positive with mjs file
},
},
// Configuration for TypeScript files
{
parser: '@typescript-eslint/parser',
files: ['*.ts', '*.tsx'],

plugins: ['@typescript-eslint', 'react', 'unused-imports', 'tailwindcss', 'simple-import-sort'],
extends: ['plugin:tailwindcss/recommended', 'airbnb-typescript/base', 'plugin:prettier/recommended'],
parserOptions: {
project: './tsconfig.json',
},
rules: {
'import/extensions': [
'error',
'ignorePackages',
{
js: 'never',
jsx: 'never',
ts: 'never',
tsx: 'never',
'': 'never',
},
], // Avoid missing file extension errors when using '@/' alias
'react/destructuring-assignment': 'off', // Vscode doesn't support automatically destructuring, it's a pain to add a new variable
'react/require-default-props': 'off', // Allow non-defined react props as undefined
'react/jsx-props-no-spreading': 'off', // _app.tsx uses spread operator and also, react-hook-form
'@typescript-eslint/comma-dangle': 'off', // Avoid conflict rule between Eslint and Prettier
'@typescript-eslint/consistent-type-imports': 'error', // Ensure `import type` is used when it's necessary
'import/prefer-default-export': 'off', // Named export is easier to refactor automatically
'tailwindcss/classnames-order': [
'warn',
{
officialSorting: true,
},
], // Follow the same ordering as the official plugin `prettier-plugin-tailwindcss`
'simple-import-sort/imports': 'error', // Import configuration for `eslint-plugin-simple-import-sort`
'simple-import-sort/exports': 'error', // Export configuration for `eslint-plugin-simple-import-sort`
'@typescript-eslint/no-unused-vars': 'off',
'unused-imports/no-unused-imports': 'error',
'unused-imports/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
},
},
// Configuration for Astro
{
files: ['*.astro'],
plugins: ['@typescript-eslint', 'react', 'unused-imports', 'tailwindcss', 'simple-import-sort'],
extends: ['plugin:tailwindcss/recommended', 'airbnb-typescript/base', 'plugin:prettier/recommended'],
parser: 'astro-eslint-parser',
parserOptions: {
parser: '@typescript-eslint/parser',
},
rules: {
'import/extensions': [
'error',
'ignorePackages',
{
js: 'never',
jsx: 'never',
ts: 'never',
tsx: 'never',
'': 'never',
},
], // Avoid missing file extension errors in .astro files
'import/no-unresolved': [
'error',
{
ignore: ['@/*'],
},
], // Disable no-unresolved rule for .astro files
'react/jsx-filename-extension': [1, { extensions: ['.astro'] }], // Accept jsx in astro files
'react/destructuring-assignment': 'off', // Vscode doesn't support automatically destructuring, it's a pain to add a new variable
'react/require-default-props': 'off', // Allow non-defined react props as undefined
'react/jsx-props-no-spreading': 'off', // _app.tsx uses spread operator and also, react-hook-form
'@typescript-eslint/comma-dangle': 'off', // Avoid conflict rule between Eslint and Prettier
'@typescript-eslint/consistent-type-imports': 'error', // Ensure `import type` is used when it's necessary
'import/prefer-default-export': 'off', // Named export is easier to refactor automatically
'tailwindcss/classnames-order': [
'warn',
{
officialSorting: true,
},
], // Follow the same ordering as the official plugin `prettier-plugin-tailwindcss`
'simple-import-sort/imports': 'error', // Import configuration for `eslint-plugin-simple-import-sort`
'simple-import-sort/exports': 'error', // Export configuration for `eslint-plugin-simple-import-sort`
'@typescript-eslint/no-unused-vars': 'off',
'unused-imports/no-unused-imports': 'error',
'unused-imports/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
},
globals: {
Astro: 'readonly',
},
},
],
};
2 changes: 1 addition & 1 deletion frontend/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ repository.workspace = true

[lib]
name = "frontend"
path = "frontend.rs"
path = "bindings.rs"
crate-type = ["lib"]

[build-dependencies]
Expand Down
15 changes: 12 additions & 3 deletions frontend/astro.config.mjs
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
import { defineConfig } from 'astro/config';
import react from '@astrojs/react';

import tailwind from '@astrojs/tailwind';
// import node from '@astrojs/node';

// https://astro.build/config
export default defineConfig({
integrations: [react(), tailwind()],
outDir: './frontend',
// output: 'server',
integrations: [
react({
experimentalReactChildren: true,
}),
tailwind(),
],
// adapter: node({
// mode: 'standalone',
// }),
outDir: './frontend',
});
6 changes: 6 additions & 0 deletions frontend/bindings.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Some structs that must be redefined for transpiling without changing actual types on backend

use tsync::tsync;

#[tsync]
pub type Hostname = String;
9 changes: 9 additions & 0 deletions frontend/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,11 @@ pub fn build_types() {
let mut inputs = vec![PathBuf::from(env!("CARGO_MANIFEST_DIR"))];
let mut output = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
inputs[0].pop();
inputs.push(inputs[0].clone());

inputs[0].push("backend");
inputs[1].push("frontend");
inputs[1].push("frontend.rs");
output.push("src/types/rust.d.ts");

tsync::generate_typescript_defs(inputs, output, false);
Expand All @@ -70,6 +73,10 @@ pub fn build_frontend() {
// #[cfg(not(debug_assertions))]
shell("yarn");

let mut project_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
project_dir.push(FRONTEND_DIR);
std::fs::remove_dir_all(project_dir);

// Only build frontend when building a release
// #[cfg(not(debug_assertions))]
shell("yarn build");
Expand All @@ -82,6 +89,8 @@ pub fn move_frontend() {
target.pop();
target.push(FRONTEND_DIR);

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

Expand Down
2 changes: 1 addition & 1 deletion frontend/e2e/example.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { test, expect } from '@playwright/test';
import { expect, test } from '@playwright/test';

test('has title', async ({ page }) => {
await page.goto('https://playwright.dev/');
Expand Down
1 change: 0 additions & 1 deletion frontend/frontend.rs

This file was deleted.

Loading
Loading