Skip to content
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
32 changes: 32 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@

root = true

[*]
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
indent_style = space
indent_size = 4
trim_trailing_whitespace = true
insert_final_newline = true


[*.{js,ts,json,md,html,css,scss}]
indent_style = space
indent_size = 2
end_of_line = lf
trim_trailing_whitespace = true
insert_final_newline = true

[*.{yaml,yml}]
indent_style = space
indent_size = 2

# JSON Files
[*.{json,json5,webmanifest}]
indent_size = 2

# Markdown Files
[*.{md,mdx}]
trim_trailing_whitespace = false
51 changes: 51 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
name: 'Lint, Build and Test'
on:
pull_request:
branches: ['main']
push:
branches: ['main']

jobs:
build-lint-and-test-changed-project:
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'

- name: Install Global Nx CLI
run: npm install -g nx

- name: Initialize Local Nx Wrapper
# sets up the local installation wrappers (.nx) that the subsequent 'npx nx' commands rely on.
run: nx init --interactive=false --useDotNxInstallation=true

- name: Setup Rust Toolchain
uses: dtolnay/rust-toolchain@stable
with:
# Ensure clippy and rustfmt are available for the lint tasks
components: rustfmt, clippy

# Determines which projects have changed since the last successful build on 'main'
- name: Set up NX Base and Head
uses: nrwl/nx-set-shas@v4
with:
main-branch-name: 'main'

# Lint Affected Projects
- name: Lint Affected Rust Projects (with Nx)
run: npx nx affected --target=lint --parallel=3

- name: Build Affected Rust Projects (with Nx)
run: npx nx affected --target=build --parallel=3

# Run Tests for Affected Projects
- name: Run Tests for Affected Rust Projects (with Nx)
run: npx nx affected --target=test --parallel=3
27 changes: 26 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,39 @@
// "editor.formatOnSave": true,
// Rust-specific
"[rust]": {
// Set line ending to LF (Linux/macOS style, common in source control)
"files.eol": "\n",

"editor.defaultFormatter": "rust-lang.rust-analyzer",
// Format on save
"editor.formatOnSave": true,
// Indentation: Each tab indents 4 spaces
"editor.tabSize": 4
"editor.insertSpaces": true,
},
"rust-analyzer.linkedProjects": [
"./back-end/Cargo.toml"
],
"rust-analyzer.cargo.features": "all"
}


// Use 'clippy' instead of 'check' for continuous analysis,
// which provides much more comprehensive linting.
"rust-analyzer.check.command": "clippy",

// Treat clippy warnings/errors as VS Code diagnostics
"rust-analyzer.check.allTargets": true,

// Enable more diagnostic details in tooltips
"rust-analyzer.diagnostics.enableExperimental": true,

"rust-analyzer.inlayHints.parameterHints.enable": true,

// Suggest extensions that are beneficial for a Rust project
"recommendations": [
"rust-lang.rust-analyzer", // Official Language Server
"vadimcn.vscode-lldb", // Debugger (essential for F5 debugging)
"tamasfe.even-better-toml", // Syntax highlighting for Cargo.toml and other TOML files
"serayuzgur.crates" // Dependency version management for Cargo.toml
]
}
41 changes: 41 additions & 0 deletions .zed/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"tab_size": 4,
"soft_tabs": true,
"format_on_save": "on",

"languages": {
"Rust": {
"formatter": "language_server",
"format_on_save": "on"
}
},

"lsp": {
"rust-analyzer": {
"initialization_options": {
"check": {
// Use 'clippy' for additional diagnostics on save (recommended)
"command": "clippy",
// Check the entire workspace (all crates in the project)
"allTargets": true,
"features": "all"
},
"inlayHints": {
"chainingHints": true,
"parameterNames": true,
"typeHints": true,
"lifetimeElisionHints": "skip_trivial"
},
"diagnostics": {
"experimental": {
"enable": false
}
},
"cargo": {
// Include documentation targets in checks
"allTargets": true
}
}
}
}
}
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,22 @@ The project uses [nx](https://nx.dev/) to manage the workspace. You will need to
- Rust and Cargo installed
- Node.js and npm/yarn/pnpm

Depending on how you installed `nx`. In the project root, run

```sh
nx --version

# Output
# Nx Version:
# - Local: Not found
# - Global: v21.6.4
```

you might need to initilise `nx`. If you installed, `nx` with `brew` or the output of the command above returns ` - Local: Not found`

```
nx init --interactive=false --useDotNxInstallation=true
```

### Getting Started

Expand Down
10 changes: 5 additions & 5 deletions back-end/node/src/api/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ impl Node {
}

pub async fn create_space(&self, dir: &str) -> Result<(), AppError> {
info!("Setting up space in Directory: {}", dir);
info!("Setting up space in Directory: {dir}");
space::new_space(&self.db, dir).await?;
Ok(())
}
Expand All @@ -42,7 +42,7 @@ impl Node {
info!("Starting WebAuthn Registration..");
webauthn::auth::start_registration(self)
.await
.map_err(|e| AppError::Auth(format!("WebAuthn registration failed: {}", e)))
.map_err(|e| AppError::Auth(format!("WebAuthn registration failed: {e}")))
}

pub async fn finish_webauthn_registration(
Expand All @@ -53,15 +53,15 @@ impl Node {
info!("Finishing WebAuthn Registration..");
webauthn::auth::finish_registration(self, challenge_id, reg)
.await
.map_err(|e| AppError::Auth(format!("WebAuthn registration failed: {}", e)))
.map_err(|e| AppError::Auth(format!("WebAuthn registration failed: {e}")))
}

pub async fn start_webauthn_authentication(
&self,
) -> Result<(RequestChallengeResponse, String), AppError> {
webauthn::auth::start_authentication(self)
.await
.map_err(|e| AppError::Auth(format!("WebAuthn authentication start failed: {}", e)))
.map_err(|e| AppError::Auth(format!("WebAuthn authentication start failed: {e}")))
}

pub async fn finish_webauthn_authentication(
Expand All @@ -72,6 +72,6 @@ impl Node {
info!("Finishing WebAuthn Authentication..");
webauthn::auth::finish_authentication(self, challenge_id, auth)
.await
.map_err(|e| AppError::Auth(format!("WebAuthn authentication failed: {}", e)))
.map_err(|e| AppError::Auth(format!("WebAuthn authentication failed: {e}")))
}
}
28 changes: 8 additions & 20 deletions back-end/node/src/api/servers/rest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,7 @@ async fn start_webauthn_registration(
let node = app_state.node.read().await;
match node.start_webauthn_registration().await {
Ok((challenge, challenge_key)) => {
info!(
"WebAuthn registration started successfully with challenge_id: {}",
challenge_key
);
info!("WebAuthn registration started successfully with challenge_id: {challenge_key}");
Ok(Json(json!({
"challenge": challenge,
"challenge_id": challenge_key
Expand Down Expand Up @@ -104,10 +101,10 @@ async fn finish_webauthn_registration(
serde_json::Value::Object(credential_value.clone()),
)
.map_err(|e| {
error!("Failed to parse credential: {}", e);
error!("Failed to parse credential: {e}");
(
StatusCode::BAD_REQUEST,
format!("Invalid credential format: {}", e),
format!("Invalid credential format: {e}"),
)
})?;

Expand All @@ -116,10 +113,7 @@ async fn finish_webauthn_registration(
.finish_webauthn_registration(challenge_id, reg_credential)
.await
.map_err(|e| {
error!(
"WebAuthn registration failed for challenge_id {}: {}",
challenge_id, e
);
error!("WebAuthn registration failed for challenge_id {challenge_id}: {e}");
(StatusCode::INTERNAL_SERVER_ERROR, e.to_string())
})?;

Expand All @@ -137,10 +131,7 @@ async fn start_webauthn_authentication(
let node = app_state.node.read().await;
match node.start_webauthn_authentication().await {
Ok((challenge, challenge_id)) => {
info!(
"WebAuthn authentication started successfully with challenge_id: {}",
challenge_id
);
info!("WebAuthn authentication started successfully with challenge_id: {challenge_id}");
Ok(Json(json!({
"challenge": challenge,
"challenge_id": challenge_id
Expand Down Expand Up @@ -168,10 +159,10 @@ async fn finish_webauthn_authentication(
credential_value.clone(),
))
.map_err(|e| {
error!("Failed to parse authentication credential: {}", e);
error!("Failed to parse authentication credential: {e}");
(
StatusCode::BAD_REQUEST,
format!("Invalid credential format: {}", e),
format!("Invalid credential format: {e}"),
)
})?;

Expand All @@ -180,10 +171,7 @@ async fn finish_webauthn_authentication(
.finish_webauthn_authentication(challenge_id, auth_credential)
.await
.map_err(|e| {
error!(
"WebAuthn authentication failed for challenge_id {}: {}",
challenge_id, e
);
error!("WebAuthn authentication failed for challenge_id {challenge_id}: {e}");
(StatusCode::INTERNAL_SERVER_ERROR, e.to_string())
})?;

Expand Down
4 changes: 2 additions & 2 deletions back-end/node/src/bootstrap/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ impl Config {
.ok()
.map(|s| s.parse::<u64>())
.transpose()
.map_err(|_| AppError::Config(format!("Invalid value for {}", key)))?
.map_err(|_| AppError::Config(format!("Invalid value for {key}")))?
.unwrap_or(default))
};

Expand All @@ -53,7 +53,7 @@ impl Config {
.ok()
.map(|s| bool::from_str(&s.to_lowercase()))
.transpose()
.map_err(|_| AppError::Config(format!("Invalid boolean value for {}", key)))?
.map_err(|_| AppError::Config(format!("Invalid boolean value for {key}")))?
.unwrap_or(default))
};

Expand Down
21 changes: 9 additions & 12 deletions back-end/node/src/bootstrap/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ pub fn get_flow_config_dir() -> String {
pub fn initialize_config_dir(dir: &str) -> Result<NodeData, AppError> {
let p = paths(dir);
let _created = create_directory(&p.config_dir)
.map_err(|e| AppError::Bootstrap(format!("Failed to create directory. {}", e)))?;
.map_err(|e| AppError::Bootstrap(format!("Failed to create directory. {e}")))?;

let lock_file = p.config_dir.join(".init.lock");
let file = fs::OpenOptions::new()
Expand All @@ -73,10 +73,7 @@ pub fn initialize_config_dir(dir: &str) -> Result<NodeData, AppError> {
if p.auth_file.exists() {
fs4::fs_std::FileExt::unlock(&file)?;
return load_existing(&p).map_err(|e| {
AppError::Bootstrap(format!(
"Error while loading existing configurations. {}",
e
))
AppError::Bootstrap(format!("Error while loading existing configurations. {e}"))
});
}

Expand Down Expand Up @@ -112,7 +109,7 @@ fn generate_keys_and_did() -> (Vec<u8>, Vec<u8>, String, String) {
multicodec_key.extend_from_slice(&pub_key_bytes);

let pub_key_multibase = multibase::encode(Base::Base58Btc, &multicodec_key);
let did = format!("did:key:{}", pub_key_multibase);
let did = format!("did:key:{pub_key_multibase}");

(priv_key_bytes, pub_key_bytes, pub_key_multibase, did)
}
Expand Down Expand Up @@ -149,28 +146,28 @@ fn load_existing(p: &Paths) -> Result<NodeData, Box<dyn Error>> {

fn bootstrap_new(p: &Paths) -> Result<NodeData, AppError> {
ensure_keystore_dir(p)
.map_err(|e| AppError::Bootstrap(format!("Failed to setup Keystore directories. {}", e)))?;
.map_err(|e| AppError::Bootstrap(format!("Failed to setup Keystore directories. {e}")))?;

let (priv_key_bytes, pub_key_bytes, pub_key_multibase, did) = generate_keys_and_did();

write_atomic_with_mode(&p.priv_key_file, &priv_key_bytes, 0o600)
.map_err(|e| AppError::Bootstrap(format!("Failed to write private key: {}", e)))?;
.map_err(|e| AppError::Bootstrap(format!("Failed to write private key: {e}")))?;

write_atomic_with_mode(&p.pub_key_file, &pub_key_bytes, 0o644)
.map_err(|e| AppError::Bootstrap(format!("Failed to write public key: {}", e)))?;
.map_err(|e| AppError::Bootstrap(format!("Failed to write public key: {e}")))?;

let meta = AuthMetadata {
schema: "flow-auth/v1".to_string(),
did: did.clone(),
created_at: chrono::Utc::now().to_rfc3339(),
pub_key_multibase: pub_key_multibase,
pub_key_multibase,
};

let json = serde_json::to_string_pretty(&meta)
.map_err(|e| AppError::Bootstrap(format!("Failed to serialize auth metadata: {}", e)))?;
.map_err(|e| AppError::Bootstrap(format!("Failed to serialize auth metadata: {e}")))?;

write_atomic_with_mode(&p.auth_file, json.as_bytes(), 0o644)
.map_err(|e| AppError::Bootstrap(format!("Failed to write auth file: {}", e)))?;
.map_err(|e| AppError::Bootstrap(format!("Failed to write auth file: {e}")))?;

Ok(NodeData {
id: did,
Expand Down
Loading