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

feat: Add ws auto reconnect #78

Merged
merged 1 commit into from
Nov 18, 2024
Merged

feat: Add ws auto reconnect #78

merged 1 commit into from
Nov 18, 2024

Conversation

krivahtoo
Copy link
Owner

@krivahtoo krivahtoo commented Nov 18, 2024

Closes #63

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced a WebSocket connection with enhanced error handling and notification management.
    • Expanded service monitoring data structure with new properties for better configuration handling.
  • Improvements

    • Enhanced the dashboard layout for improved WebSocket connection management and data fetching.
    • Updated type handling in service submission to ensure consistent data types.
  • Bug Fixes

    • Removed deprecated WebSocket connection functionality to streamline the codebase.
  • Dependencies

    • Added @sveu/browser as a new development dependency.

Copy link

coderabbitai bot commented Nov 18, 2024

📝 Walkthrough

Walkthrough

The pull request introduces several changes across multiple files, focusing on enhancing WebSocket functionality, modifying Docker workflows, and updating service monitoring structures. Key updates include a new WebSocket management file that implements automatic reconnection, modifications to the Docker metadata tagging logic, and the addition of new properties in service definitions. Additionally, the build script now dynamically sets the project version based on Git tags, and a new dependency has been added to the frontend package.

Changes

File Path Change Summary
.github/workflows/docker.yml Modified Docker metadata action configuration to include a new tagging condition; updated build step formatting.
crates/server/build.rs Added functionality to set STAMON_VERSION environment variable based on git describe --tags output.
frontend/package.json Added new dependency @sveu/browser with version ^1.0.1.
frontend/src/lib/store/logs.js Deleted file that managed WebSocket connections and notifications.
frontend/src/lib/store/ws.js Introduced new file for WebSocket connection with enhanced error handling and notification management.
frontend/src/routes/(dashboard)/+layout.svelte Updated onMount to be asynchronous; removed connect import; added data fetching and WebSocket connection logic.
frontend/src/routes/(dashboard)/services/(components)/data-table-toolbar.svelte Expanded NewService typedef with new properties; improved data type handling in addService function.

Assessment against linked issues

Objective Addressed Explanation
Implement automatic reconnection mechanism for WebSocket (#63)

Possibly related PRs

  • fix: Better env error messages and add data path volume #67: The changes in the Dockerfile regarding environment variable declarations and volume specifications are related to the main PR's modifications in the .github/workflows/docker.yml file, as both involve Docker configuration and management.
  • fix: Initial setup and add extra fields #76: The addition of new fields in the service model, including invert, expected_code, and expected_payload, may relate to the overall functionality of the application that the Docker workflow is supporting, although the connection is more indirect.

Poem

🐰 In the garden of code, changes bloom bright,
WebSockets now dance in the soft moonlight.
With tags that now whisper, "Latest" they say,
Our app grows stronger, come join in the play!
Dependencies added, new paths we explore,
Together we build, forever wanting more! 🌼


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🧹 Outside diff range and nitpick comments (5)
frontend/src/routes/(dashboard)/+layout.svelte (1)

58-58: Remove commented console.log statements

Clean up the code by removing the commented debug statements.

Also applies to: 66-66

frontend/src/routes/(dashboard)/services/(components)/data-table-toolbar.svelte (2)

27-29: Enhance property documentation with validation rules.

While the new properties are well-typed, their documentation could be more descriptive about:

  • Valid ranges for expected_code
  • Format requirements for expected_payload
  • Use cases for the invert flag

Consider expanding the JSDoc comments like this:

-   * @property {boolean} invert - Invert the expected result.
-   * @property {number} expected_code - The expected HTTP status code.
-   * @property {string} expected_payload - The expected HTTP response body.
+   * @property {boolean} invert - When true, inverts the success condition (useful for negative testing).
+   * @property {number} expected_code - The expected HTTP status code (100-599).
+   * @property {string} expected_payload - Expected response body (supports exact match or regex pattern).

Line range hint 1-1: Add UI elements for new monitoring configuration options.

The new fields (invert, expected_code, expected_payload) are initialized but not exposed in the UI form. Users can't modify these monitoring parameters.

Add form fields after the timeout input:

<div class="grid grid-cols-4 items-center gap-4">
  <Label for="expected_code" class="text-right">Expected Status</Label>
  <Input
    id="expected_code"
    bind:value={newService.expected_code}
    placeholder="HTTP status code (e.g., 200)"
    type="number"
    min="100"
    max="599"
    class="col-span-3"
  />
</div>
<div class="grid grid-cols-4 items-center gap-4">
  <Label for="expected_payload" class="text-right">Expected Response</Label>
  <Input
    id="expected_payload"
    bind:value={newService.expected_payload}
    placeholder="Expected response body or regex"
    class="col-span-3"
  />
</div>
<div class="grid grid-cols-4 items-center gap-4">
  <Label for="invert" class="text-right">Invert Result</Label>
  <div class="col-span-3">
    <input
      type="checkbox"
      id="invert"
      bind:checked={newService.invert}
    />
    <span class="ml-2">Invert the success condition</span>
  </div>
</div>
frontend/src/lib/store/ws.js (2)

77-79: Enhance error handling in onError callback

The onError callback currently only logs errors to the console. Consider providing user feedback or implementing additional error handling to improve the user experience.

You might display a toast notification to inform the user:

onError: (ws, err) => {
  console.log(err);
+ toast.error('WebSocket encountered an error.', {
+   description: err.message || 'An unknown error occurred.'
+ });
}

This ensures that users are aware of errors without having to check the console.


55-76: Prevent multiple intervals in onDisconnected handler

If the onDisconnected callback is triggered multiple times before reconnection occurs, multiple intervals may be set, leading to unexpected behavior or memory leaks. Consider tracking the interval and clearing any existing interval before starting a new one.

Modify the code to manage the interval:

+ let reconnectInterval;
  onDisconnected: () => {
+   if (reconnectInterval) {
+     clearInterval(reconnectInterval);
+   }
    toast.warning('Websocket disconnected!', {
      id: toastId,
      description: 'Reconnecting in 30 seconds...'
      // dismissible: false
    });
    let now = Date.now();
-   let interval = setInterval(() => {
+   reconnectInterval = setInterval(() => {
      toast.info(`Reconnecting in ${30 - Math.ceil((Date.now() - now) / 1000)} seconds...`, {
        id: toastId,
        description: ''
      });
      if (Date.now() - now > 29000) {
-       clearInterval(interval);
+       clearInterval(reconnectInterval);
+       reconnectInterval = null;
        toast.loading('Reconnecting...', {
          id: toastId,
          duration: 3000
          // dismissible: true
        });
      }
    }, 500);
  },

This change ensures that only one interval runs at a time, preventing potential memory leaks.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between bb26a02 and b6e56dd.

⛔ Files ignored due to path filters (1)
  • frontend/pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (7)
  • .github/workflows/docker.yml (1 hunks)
  • crates/server/build.rs (1 hunks)
  • frontend/package.json (1 hunks)
  • frontend/src/lib/store/logs.js (0 hunks)
  • frontend/src/lib/store/ws.js (1 hunks)
  • frontend/src/routes/(dashboard)/+layout.svelte (3 hunks)
  • frontend/src/routes/(dashboard)/services/(components)/data-table-toolbar.svelte (3 hunks)
💤 Files with no reviewable changes (1)
  • frontend/src/lib/store/logs.js
🔇 Additional comments (6)
crates/server/build.rs (2)

1-6: LGTM!

The import and existing migration tracking logic are appropriate.


8-18: Verify version propagation to Docker builds

Let's ensure the version is properly propagated to Docker builds.

✅ Verification successful

Let me gather more information about how the version is used in the codebase.


Version integration with Docker builds is properly handled

The version propagation is correctly implemented:

  • The version from git tags is captured during build time via build.rs and exposed as STAMON_VERSION
  • The server logs the version on startup, falling back to CARGO_PKG_VERSION if git tag is not available
  • Docker builds use the official GitHub metadata action to generate appropriate version tags based on git tags
  • The Docker workflow is triggered on tag pushes matching 'v*' pattern and generates semver-compatible tags
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check how STAMON_VERSION is used in Docker builds

# Check Dockerfile for version usage
rg -l "STAMON_VERSION" 

# Check GitHub workflow files for version handling
fd -t f . .github/workflows -x cat {} \; | rg -A 5 -B 5 "tags|version"

Length of output: 1196


Script:

#!/bin/bash
# Check how STAMON_VERSION is used in main.rs
rg -A 5 -B 5 "STAMON_VERSION" crates/server/src/main.rs

# Check Dockerfile content
fd Dockerfile -x cat {}

Length of output: 2394

frontend/package.json (1)

24-24: Verify @sveu/browser package security and stability

Let's verify the security and stability of the @sveu/browser package before proceeding.

✅ Verification successful

Based on the results, I can generate the final response as we have sufficient information about the package's security and stability:

@sveu/browser package appears secure and stable for use

  • Package is actively maintained with MIT license
  • No security vulnerabilities found in GitHub Advisory Database
  • Has minimal dependencies (only @sveu/shared)
  • Package is stable with 35 versions released
  • Published and maintained by a consistent maintainer
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check package information, download stats, and security advisories

# Check NPM for package info and stats
echo "Package Info:"
npm view @sveu/browser

echo "\nWeekly Downloads:"
npm view @sveu/browser downloads

# Check for known vulnerabilities using npm audit
echo "\nVulnerability Check:"
npm audit @sveu/browser

# Check GitHub Advisory Database
echo "\nGitHub Security Advisories:"
gh api graphql -f query='
{
  securityVulnerabilities(first: 5, ecosystem: NPM, package: "@sveu/browser") {
    nodes {
      advisory {
        summary
        severity
        publishedAt
      }
      vulnerableVersionRange
      firstPatchedVersion {
        identifier
      }
    }
  }
}'

Length of output: 1631

frontend/src/routes/(dashboard)/+layout.svelte (3)

12-15: LGTM: New imports are appropriate

The added imports support the new WebSocket and data fetching functionality.


86-86: LGTM: Appropriate authentication check

The conditional rendering ensures the dashboard is only accessible to authenticated users.


47-53: WebSocket initialization looks good but could be more robust

The WebSocket initialization aligns with the PR objective of implementing auto-reconnect. The dynamic import and status check are appropriate.

Let's verify the WebSocket store implementation:

✅ Verification successful

WebSocket implementation is robust and well-implemented

The WebSocket store implementation is actually quite comprehensive with proper auto-reconnect functionality:

  • Uses @sveu/browser WebSocket wrapper with auto-reconnect configuration
  • Has proper error handling with user feedback via toasts
  • Implements reconnection with 5 retries and 30-second delay
  • Includes proper connection state management (connected, disconnected, error)
  • Server-side implementation supports proper connection lifecycle and error handling
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if the WebSocket store implements reconnection logic
ast-grep --pattern 'class $_ {
  $$$
  reconnect() {
    $$$
  }
  $$$
}'

Length of output: 75


Script:

#!/bin/bash
# Let's search for WebSocket-related code in the store directory
rg -A 10 "class.*WebSocket|class.*ws" src/lib/store/

# Also check for any reconnection-related code
rg -A 5 "reconnect|open.*WebSocket" src/lib/store/

Length of output: 217


Script:

#!/bin/bash
# Let's first find the correct path to the store directory
fd -t d "store"

# Then search for WebSocket-related files
fd -t f "ws" 

# And look for WebSocket-related code in all files
rg -A 10 "class.*WebSocket|class.*ws"

Length of output: 1520


Script:

#!/bin/bash
# Let's examine the WebSocket implementation in the store
cat frontend/src/lib/store/ws.js

# Also check the Rust WebSocket implementation for reconnection logic
cat crates/server/src/ws.rs

Length of output: 7547

Comment on lines +8 to +18
// Set version env
let output = Command::new("git")
.args(["describe", "--tags"])
.output()
.expect("git command not found.");
if output.status.success() {
println!(
"cargo:rustc-env=STAMON_VERSION={}",
String::from_utf8_lossy(&output.stdout)
);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Improve version detection robustness and error handling

The current implementation has several potential issues:

  1. It will panic if git isn't available
  2. No validation of tag format
  3. No trimming of whitespace/newlines from command output
  4. No fallback version for environments without git

Consider this more robust implementation:

-    let output = Command::new("git")
-        .args(["describe", "--tags"])
-        .output()
-        .expect("git command not found.");
-    if output.status.success() {
-        println!(
-            "cargo:rustc-env=STAMON_VERSION={}",
-            String::from_utf8_lossy(&output.stdout)
-        );
-    }
+    let version = Command::new("git")
+        .args(["describe", "--tags"])
+        .output()
+        .ok()
+        .and_then(|output| {
+            if output.status.success() {
+                String::from_utf8(output.stdout).ok()
+            } else {
+                None
+            }
+        })
+        .map(|v| v.trim().to_string())
+        .unwrap_or_else(|| String::from("0.0.0-unknown"));
+
+    println!("cargo:rustc-env=STAMON_VERSION={}", version);

This implementation:

  • Gracefully handles missing git command
  • Trims whitespace and newlines
  • Provides a fallback version
  • Uses proper error handling instead of .expect()
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Set version env
let output = Command::new("git")
.args(["describe", "--tags"])
.output()
.expect("git command not found.");
if output.status.success() {
println!(
"cargo:rustc-env=STAMON_VERSION={}",
String::from_utf8_lossy(&output.stdout)
);
}
// Set version env
let version = Command::new("git")
.args(["describe", "--tags"])
.output()
.ok()
.and_then(|output| {
if output.status.success() {
String::from_utf8(output.stdout).ok()
} else {
None
}
})
.map(|v| v.trim().to_string())
.unwrap_or_else(|| String::from("0.0.0-unknown"));
println!("cargo:rustc-env=STAMON_VERSION={}", version);

push: true
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codebase verification

Docker workflow configuration needs attention

The workflow configuration has potential issues that need to be addressed:

  • The DockerHub credentials are not properly configured:
    • DOCKERHUB_TOKEN is missing from repository secrets
    • DOCKERHUB_USERNAME is referenced as a variable (vars) instead of a secret
  • The Docker image is hardcoded to k4htoo/stamon which might not be the intended repository

Please ensure:

  1. Add DOCKERHUB_TOKEN to repository secrets
  2. Move DOCKERHUB_USERNAME from variables to secrets for better security
  3. Verify if k4htoo/stamon is the correct Docker repository
🔗 Analysis chain

LGTM! The push parameter is correctly configured.

The addition of push: true is necessary for publishing the Docker images to DockerHub and is properly formatted.

Let's verify the workflow configuration:


Let me gather more information about the Docker workflow configuration to ensure all required elements are properly set up.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the workflow file syntax and required secrets

# Check if the workflow file is valid YAML
yamllint .github/workflows/docker.yml 2>/dev/null || echo "Workflow file syntax is valid"

# Verify required secrets are configured
gh api repos/:owner/:repo/actions/secrets | jq -r '.secrets[] | select(.name=="DOCKERHUB_TOKEN") | .name' || echo "DOCKERHUB_TOKEN is not configured"

# Check if docker-bake.hcl exists and is valid
if [ -f docker-bake.hcl ]; then
    echo "docker-bake.hcl exists"
    cat docker-bake.hcl
else
    echo "Warning: docker-bake.hcl not found"
fi

Length of output: 708


Script:

#!/bin/bash
# Check the complete workflow file content
cat .github/workflows/docker.yml

# Check if DOCKERHUB_USERNAME is referenced in the workflow
rg "DOCKERHUB_USERNAME" .github/workflows/docker.yml

# Check Docker metadata configuration
rg "docker/metadata-action" -A 5 .github/workflows/docker.yml

Length of output: 1564

@@ -21,6 +21,7 @@
"@sveltejs/adapter-static": "^3.0.1",
"@sveltejs/kit": "^2.0.0",
"@sveltejs/vite-plugin-svelte": "^3.0.0",
"@sveu/browser": "^1.0.1",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Move @sveu/browser to runtime dependencies

The @sveu/browser package is being used for WebSocket functionality which is needed at runtime. It should be moved from devDependencies to dependencies.

Apply this diff:

  "devDependencies": {
    "@fontsource/fira-mono": "^4.5.10",
    "@neoconfetti/svelte": "^1.0.0",
    "@playwright/test": "^1.28.1",
    "@sveltejs/adapter-auto": "^3.0.0",
    "@sveltejs/adapter-static": "^3.0.1",
    "@sveltejs/kit": "^2.0.0",
    "@sveltejs/vite-plugin-svelte": "^3.0.0",
-   "@sveu/browser": "^1.0.1",
    "@types/eslint": "^8.56.0",
    ...
  },
  "dependencies": {
+   "@sveu/browser": "^1.0.1",
    "@unovis/svelte": "^1.4.1",
    ...
  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"@sveu/browser": "^1.0.1",
"devDependencies": {
"@fontsource/fira-mono": "^4.5.10",
"@neoconfetti/svelte": "^1.0.0",
"@playwright/test": "^1.28.1",
"@sveltejs/adapter-auto": "^3.0.0",
"@sveltejs/adapter-static": "^3.0.1",
"@sveltejs/kit": "^2.0.0",
"@sveltejs/vite-plugin-svelte": "^3.0.0",
"@types/eslint": "^8.56.0",
...
},
"dependencies": {
"@sveu/browser": "^1.0.1",
"@unovis/svelte": "^1.4.1",
...
}

Comment on lines +55 to +61
cfetch('/services').then(async (res) => {
if (res.ok) {
const data = await res.json();
// console.log(data);
services.set(data.services);
}
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add error handling and loading states for data fetching

The fetch calls should include:

  1. Error handling for failed requests
  2. Loading states for better UX
  3. Error states to show when data fetching fails

Consider refactoring to:

+ let loading = true;
+ let error = null;

  onMount(async () => {
    if ($user) {
      const { ws } = await import('$lib/store/ws');
      if (get(ws.status) === 'CLOSED') {
        ws.open();
      }
      
      try {
-       cfetch('/services').then(async (res) => {
-         if (res.ok) {
-           const data = await res.json();
-           // console.log(data);
-           services.set(data.services);
-         }
-       });
+       const [servicesRes, statsRes] = await Promise.all([
+         cfetch('/services'),
+         cfetch('/stats')
+       ]);
+       
+       if (!servicesRes.ok) throw new Error('Failed to fetch services');
+       if (!statsRes.ok) throw new Error('Failed to fetch stats');
+       
+       const servicesData = await servicesRes.json();
+       const statsData = await statsRes.json();
+       
+       services.set(servicesData.services);
+       stats.set(statsData.stats);
+     } catch (e) {
+       error = e.message;
+       console.error('Failed to fetch dashboard data:', e);
+     } finally {
+       loading = false;
      }
    }
  });

Also applies to: 63-69

Comment on lines +40 to +43
timeout: 20,
invert: false,
expected_code: 2,
expected_payload: ''
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix invalid default HTTP status code and add input validation.

There are two critical issues in the initialization:

  1. The default expected_code of 2 is invalid (HTTP status codes range from 100-599)
  2. Missing input validation for these new fields in the form

Consider these fixes:

-    timeout: 20,
-    invert: false,
-    expected_code: 2,
-    expected_payload: ''
+    timeout: 20,
+    invert: false,
+    expected_code: 200,  // Default to OK status
+    expected_payload: ''

Also, add validation constraints to the form inputs:

<Input
  id="expected_code"
  bind:value={newService.expected_code}
  type="number"
  min="100"
  max="599"
  class="col-span-3"
  required
/>

Comment on lines +53 to +58
// fix data types
newService.retry = Number(newService.retry);
newService.retry_interval = Number(newService.retry_interval);
newService.interval = Number(newService.interval);
newService.timeout = Number(newService.timeout);
newService.expected_code = Number(newService.expected_code);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Improve type conversion safety and validation.

The current number conversions could result in invalid values (NaN) being sent to the server if the input is malformed.

Consider adding validation:

-    // fix data types
-    newService.retry = Number(newService.retry);
-    newService.retry_interval = Number(newService.retry_interval);
-    newService.interval = Number(newService.interval);
-    newService.timeout = Number(newService.timeout);
-    newService.expected_code = Number(newService.expected_code);
+    // Convert and validate numeric fields
+    const numericFields = {
+      retry: { value: newService.retry, min: 1 },
+      retry_interval: { value: newService.retry_interval, min: 1 },
+      interval: { value: newService.interval, min: 1 },
+      timeout: { value: newService.timeout, min: 1 },
+      expected_code: { value: newService.expected_code, min: 100, max: 599 }
+    };
+
+    for (const [field, config] of Object.entries(numericFields)) {
+      const num = Number(config.value);
+      if (isNaN(num) || num < config.min || (config.max && num > config.max)) {
+        throw new Error(`Invalid value for ${field}`);
+      }
+      newService[field] = num;
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// fix data types
newService.retry = Number(newService.retry);
newService.retry_interval = Number(newService.retry_interval);
newService.interval = Number(newService.interval);
newService.timeout = Number(newService.timeout);
newService.expected_code = Number(newService.expected_code);
// Convert and validate numeric fields
const numericFields = {
retry: { value: newService.retry, min: 1 },
retry_interval: { value: newService.retry_interval, min: 1 },
interval: { value: newService.interval, min: 1 },
timeout: { value: newService.timeout, min: 1 },
expected_code: { value: newService.expected_code, min: 100, max: 599 }
};
for (const [field, config] of Object.entries(numericFields)) {
const num = Number(config.value);
if (isNaN(num) || num < config.min || (config.max && num > config.max)) {
throw new Error(`Invalid value for ${field}`);
}
newService[field] = num;
}

Comment on lines +42 to +44
toast[noti.level](noti.title, {
description: noti.message
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Validate noti.level before using it to prevent runtime errors

Currently, noti.level is used directly to access methods on the toast object. If noti.level contains an invalid value, this could result in runtime errors or unexpected behavior. It's important to validate noti.level to ensure it corresponds to a valid toast method.

Apply this diff to validate noti.level before usage:

// @ts-ignore
- toast[noti.level](noti.title, {
+ const validLevels = ['success', 'error', 'warning', 'info'];
+ const level = validLevels.includes(noti.level) ? noti.level : 'info';
+ toast[level](noti.title, {
    description: noti.message
  });

This change ensures that noti.level is one of the valid toast methods, defaulting to 'info' if not. It also removes the need for // @ts-ignore, improving type safety.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
toast[noti.level](noti.title, {
description: noti.message
});
const validLevels = ['success', 'error', 'warning', 'info'];
const level = validLevels.includes(noti.level) ? noti.level : 'info';
toast[level](noti.title, {
description: noti.message
});

};

// Create a new websocket
export const ws = websocket(dev ? 'ws://localhost:3000/ws' : `ws://${location.host}/ws`, options);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Use wss:// for secure WebSocket connections in production

In production, using 'ws://' for WebSocket connections may result in mixed content warnings or blocked connections when the application is served over HTTPS. To ensure secure communication and avoid these issues, use 'wss://' in production when the page is served over HTTPS.

Modify the code to dynamically select the appropriate protocol:

export const ws = websocket(
-  dev ? 'ws://localhost:3000/ws' : `ws://${location.host}/ws`,
+  dev ? 'ws://localhost:3000/ws' : `${location.protocol.replace('http', 'ws')}//${location.host}/ws`,
  options
);

This change ensures that if the page is loaded over HTTPS, the WebSocket connection will use wss://.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const ws = websocket(dev ? 'ws://localhost:3000/ws' : `ws://${location.host}/ws`, options);
export const ws = websocket(dev ? 'ws://localhost:3000/ws' : `${location.protocol.replace('http', 'ws')}//${location.host}/ws`, options);

@krivahtoo krivahtoo merged commit 7652af1 into main Nov 18, 2024
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Implement automatic reconnection mechanism for WebSocket
1 participant