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

Implement profanity.dev as the swear filter with filter notifications. #112

Open
wants to merge 24 commits into
base: staging
Choose a base branch
from

Conversation

benrobson
Copy link
Member

@benrobson benrobson commented Jan 19, 2025

From https://github.com/ModularSoftAU/zander-issues/issues/181

Summary by CodeRabbit

Based on the comprehensive summary of changes, here are the high-level release notes:

  • New Features

    • Added Bridge Command Management
    • Implemented Vault Feature for Storing and Sharing Resources
    • Introduced Player Reporting System
    • Created Shop Directory Functionality
    • Enhanced User Profile with Reporting Tab
  • Improvements

    • Updated module imports to use explicit .js file extensions
    • Added feature toggles for new functionalities
    • Improved error handling and logging across various components
    • Enhanced database schema with new tables for server status and reports
  • Security Enhancements

    • Added httpOnly flag to cookies
    • Implemented email hashing utility
    • Refined permission checks for new features
  • Performance

    • Added cron jobs for periodic cleanup and maintenance tasks
    • Integrated DataTables for improved table interactions
  • Bug Fixes

    • Standardized import statements
    • Corrected module resolution in various files

@benrobson benrobson requested a review from JaedanC as a code owner January 19, 2025 11:29
Copy link

coderabbitai bot commented Jan 19, 2025

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

This pull request introduces a comprehensive set of changes across the entire application, focusing on module resolution, feature expansion, and code structure improvements. The primary modifications include adding .js extensions to import statements, introducing new API routes and Discord commands for features like reporting, shop directory, vault management, and bridge functionality, and enhancing existing routes with additional permissions and error handling. The changes aim to improve modularity, add new user-facing features, and ensure consistent module importing across the project.

Changes

File Category Changes
API Routes Updated import statements with .js extensions, added new routes for bridge, report, shop directory, and vault
Controllers Modified import statements, updated profile picture retrieval endpoint
Commands Added new Discord commands for bridge, report, shop directory, vault, and status
Views Introduced new templates for bridge, vault, shop directory, and report pages
Cron Jobs Added new scheduled tasks for bridge cleanup, cake day user check, and Discord stats update
Database Added new tables for server status, reports, and updated views

Sequence Diagram

sequenceDiagram
    participant User
    participant WebApp
    participant APIServer
    participant Database
    participant DiscordBot

    User->>WebApp: Access new features
    WebApp->>APIServer: Request feature data
    APIServer->>Database: Query data
    Database-->>APIServer: Return results
    APIServer-->>WebApp: Send feature data
    WebApp->>User: Render feature page

    alt Bridge Command
        User->>DiscordBot: Use /bridge command
        DiscordBot->>APIServer: Submit bridge request
        APIServer->>Database: Store/Process bridge command
        Database-->>APIServer: Confirm action
        APIServer-->>DiscordBot: Return status
        DiscordBot->>User: Display bridge command result
    end
Loading

Poem

🐰 A Rabbit's Ode to Code Evolution 🚀

With .js in hand and features bright,
Our codebase leaps to a new height!
Bridge, vault, and shop, oh what delight,
Modularity shines so pure and light!
Hop, hop, hurray for progress today! 🌟


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 generate docstrings to generate docstrings for this PR. (Beta)
  • @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.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

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.

@benrobson benrobson changed the base branch from master to staging January 19, 2025 11:29
Comment on lines +99 to +127
app.post(baseEndpoint + "/clear", async function (req, res) {
isFeatureEnabled(features.bridge, res, lang);

try {
db.query(
`TRUNCATE bridge;`,
function (error, results, fields) {
if (error) {
return res.send({
success: false,
message: `${error}`,
});
}

return res.send({
success: true,
message: `Bridge has now been cleared.`,
});
}
);
} catch (error) {
res.send({
success: false,
message: `${error}`,
});
}

return res;
});

Check failure

Code scanning / CodeQL

Missing rate limiting High

This route handler performs
a database access
, but is not rate-limited.

Copilot Autofix AI 12 days ago

To fix the problem, we need to introduce rate limiting to the route handlers in the bridgeApiRoute function. The best way to achieve this is by using the express-rate-limit middleware. This middleware allows us to set a maximum number of requests a user can make within a specified time window. We will apply this middleware to the relevant routes to ensure they are protected against excessive requests.

We will need to:

  1. Install the express-rate-limit package.
  2. Import the express-rate-limit package in the api/routes/bridge.js file.
  3. Set up a rate limiter with appropriate configuration.
  4. Apply the rate limiter to the relevant routes.
Suggested changeset 2
api/routes/bridge.js

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/api/routes/bridge.js b/api/routes/bridge.js
--- a/api/routes/bridge.js
+++ b/api/routes/bridge.js
@@ -6,2 +6,3 @@
 } from "../common.js";
+import RateLimit from "express-rate-limit";
 
@@ -10,2 +11,10 @@
 
+  // set up rate limiter: maximum of 100 requests per 15 minutes
+  const limiter = RateLimit({
+    windowMs: 15 * 60 * 1000, // 15 minutes
+    max: 100, // max 100 requests per windowMs
+  });
+
+  app.use(limiter);
+
   app.get(baseEndpoint + "/get", async function (req, res) {
@@ -64,3 +73,3 @@
 
-  app.post(baseEndpoint + "/command/add", async function (req, res) {
+  app.post(baseEndpoint + "/command/add", limiter, async function (req, res) {
     isFeatureEnabled(features.bridge, res, lang);
@@ -98,3 +107,3 @@
 
-  app.post(baseEndpoint + "/clear", async function (req, res) {
+  app.post(baseEndpoint + "/clear", limiter, async function (req, res) {
     isFeatureEnabled(features.bridge, res, lang);
@@ -128,3 +137,3 @@
 
-  app.get(baseEndpoint + "/server/get", async function (req, res) {
+  app.get(baseEndpoint + "/server/get", limiter, async function (req, res) {
     isFeatureEnabled(features.bridge, res, lang);
@@ -158,3 +167,3 @@
 
-  app.post(baseEndpoint + "/server/update", async function (req, res) {
+  app.post(baseEndpoint + "/server/update", limiter, async function (req, res) {
     isFeatureEnabled(features.bridge, res, lang);
EOF
@@ -6,2 +6,3 @@
} from "../common.js";
import RateLimit from "express-rate-limit";

@@ -10,2 +11,10 @@

// set up rate limiter: maximum of 100 requests per 15 minutes
const limiter = RateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // max 100 requests per windowMs
});

app.use(limiter);

app.get(baseEndpoint + "/get", async function (req, res) {
@@ -64,3 +73,3 @@

app.post(baseEndpoint + "/command/add", async function (req, res) {
app.post(baseEndpoint + "/command/add", limiter, async function (req, res) {
isFeatureEnabled(features.bridge, res, lang);
@@ -98,3 +107,3 @@

app.post(baseEndpoint + "/clear", async function (req, res) {
app.post(baseEndpoint + "/clear", limiter, async function (req, res) {
isFeatureEnabled(features.bridge, res, lang);
@@ -128,3 +137,3 @@

app.get(baseEndpoint + "/server/get", async function (req, res) {
app.get(baseEndpoint + "/server/get", limiter, async function (req, res) {
isFeatureEnabled(features.bridge, res, lang);
@@ -158,3 +167,3 @@

app.post(baseEndpoint + "/server/update", async function (req, res) {
app.post(baseEndpoint + "/server/update", limiter, async function (req, res) {
isFeatureEnabled(features.bridge, res, lang);
package.json
Outside changed files

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/package.json b/package.json
--- a/package.json
+++ b/package.json
@@ -51,3 +51,4 @@
     "querystring": "^0.2.1",
-    "zero-md": "^2.3.1"
+    "zero-md": "^2.3.1",
+    "express-rate-limit": "^7.5.0"
   }
EOF
@@ -51,3 +51,4 @@
"querystring": "^0.2.1",
"zero-md": "^2.3.1"
"zero-md": "^2.3.1",
"express-rate-limit": "^7.5.0"
}
This fix introduces these dependencies
Package Version Security advisories
express-rate-limit (npm) 7.5.0 None
Copilot is powered by AI and may make mistakes. Always verify output.
Positive Feedback
Negative Feedback

Provide additional feedback

Please help us improve GitHub Copilot by sharing more details about this comment.

Please select one or more of the options
Comment on lines +129 to +157
app.get(baseEndpoint + "/server/get", async function (req, res) {
isFeatureEnabled(features.bridge, res, lang);

try {
db.query(
`SELECT * FROM serverStatus;`,
function (error, results) {
if (error) {
return res.send({
success: false,
message: `Failed: ${error}`,
});
}

return res.send({
success: true,
data: results,
});
}
);
} catch (error) {
return res.send({
success: false,
message: `Unexpected error: ${error}`,
});
}

return res;
});

Check failure

Code scanning / CodeQL

Missing rate limiting High

This route handler performs
a database access
, but is not rate-limited.

Copilot Autofix AI 12 days ago

To fix the problem, we need to introduce rate limiting to the route handlers that perform database access operations. The best way to achieve this is by using the express-rate-limit middleware. This middleware allows us to set a maximum number of requests that can be made to the server within a specified time window. We will apply this middleware to the relevant routes to ensure that they are protected against denial-of-service attacks.

We will need to:

  1. Install the express-rate-limit package.
  2. Import the express-rate-limit package in the file.
  3. Set up a rate limiter with appropriate configuration.
  4. Apply the rate limiter to the relevant routes.
Suggested changeset 2
api/routes/bridge.js

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/api/routes/bridge.js b/api/routes/bridge.js
--- a/api/routes/bridge.js
+++ b/api/routes/bridge.js
@@ -6,4 +6,9 @@
 } from "../common.js";
+import RateLimit from "express-rate-limit";
 
 export default function bridgeApiRoute(app, config, db, features, lang) {
+  const limiter = RateLimit({
+    windowMs: 15 * 60 * 1000, // 15 minutes
+    max: 100, // max 100 requests per windowMs
+  });
   const baseEndpoint = "/api/bridge";
@@ -64,3 +69,3 @@
 
-  app.post(baseEndpoint + "/command/add", async function (req, res) {
+  app.post(baseEndpoint + "/command/add", limiter, async function (req, res) {
     isFeatureEnabled(features.bridge, res, lang);
@@ -98,3 +103,3 @@
 
-  app.post(baseEndpoint + "/clear", async function (req, res) {
+  app.post(baseEndpoint + "/clear", limiter, async function (req, res) {
     isFeatureEnabled(features.bridge, res, lang);
@@ -128,3 +133,3 @@
 
-  app.get(baseEndpoint + "/server/get", async function (req, res) {
+  app.get(baseEndpoint + "/server/get", limiter, async function (req, res) {
     isFeatureEnabled(features.bridge, res, lang);
@@ -158,3 +163,3 @@
 
-  app.post(baseEndpoint + "/server/update", async function (req, res) {
+  app.post(baseEndpoint + "/server/update", limiter, async function (req, res) {
     isFeatureEnabled(features.bridge, res, lang);
@@ -192,3 +197,3 @@
 
-  app.post(baseEndpoint + "/command/process", async function (req, res) {
+  app.post(baseEndpoint + "/command/process", limiter, async function (req, res) {
     isFeatureEnabled(features.bridge, res, lang);
EOF
@@ -6,4 +6,9 @@
} from "../common.js";
import RateLimit from "express-rate-limit";

export default function bridgeApiRoute(app, config, db, features, lang) {
const limiter = RateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // max 100 requests per windowMs
});
const baseEndpoint = "/api/bridge";
@@ -64,3 +69,3 @@

app.post(baseEndpoint + "/command/add", async function (req, res) {
app.post(baseEndpoint + "/command/add", limiter, async function (req, res) {
isFeatureEnabled(features.bridge, res, lang);
@@ -98,3 +103,3 @@

app.post(baseEndpoint + "/clear", async function (req, res) {
app.post(baseEndpoint + "/clear", limiter, async function (req, res) {
isFeatureEnabled(features.bridge, res, lang);
@@ -128,3 +133,3 @@

app.get(baseEndpoint + "/server/get", async function (req, res) {
app.get(baseEndpoint + "/server/get", limiter, async function (req, res) {
isFeatureEnabled(features.bridge, res, lang);
@@ -158,3 +163,3 @@

app.post(baseEndpoint + "/server/update", async function (req, res) {
app.post(baseEndpoint + "/server/update", limiter, async function (req, res) {
isFeatureEnabled(features.bridge, res, lang);
@@ -192,3 +197,3 @@

app.post(baseEndpoint + "/command/process", async function (req, res) {
app.post(baseEndpoint + "/command/process", limiter, async function (req, res) {
isFeatureEnabled(features.bridge, res, lang);
package.json
Outside changed files

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/package.json b/package.json
--- a/package.json
+++ b/package.json
@@ -51,3 +51,4 @@
     "querystring": "^0.2.1",
-    "zero-md": "^2.3.1"
+    "zero-md": "^2.3.1",
+    "express-rate-limit": "^7.5.0"
   }
EOF
@@ -51,3 +51,4 @@
"querystring": "^0.2.1",
"zero-md": "^2.3.1"
"zero-md": "^2.3.1",
"express-rate-limit": "^7.5.0"
}
This fix introduces these dependencies
Package Version Security advisories
express-rate-limit (npm) 7.5.0 None
Copilot is powered by AI and may make mistakes. Always verify output.
Positive Feedback
Negative Feedback

Provide additional feedback

Please help us improve GitHub Copilot by sharing more details about this comment.

Please select one or more of the options
Comment on lines +159 to +191
app.post(baseEndpoint + "/server/update", async function (req, res) {
isFeatureEnabled(features.bridge, res, lang);

const serverInfo = required(req.body, "serverInfo", res);
let lastUpdated = required(req.body, "lastUpdated", res);

try {
const serverInfoString = JSON.stringify(serverInfo);

db.query(
`UPDATE serverStatus SET statusInfo = ?, lastUpdated = ? WHERE serverStatusId = 1;`,
[serverInfoString, lastUpdated],
function (error, updateResults) {
if (error) {
return res.send({
success: false,
message: `Update failed: ${error}`,
});
}

return res.send({
success: true,
message: `Server status updated successfully.`,
});
}
);
} catch (error) {
return res.send({
success: false,
message: `Unexpected error: ${error}`,
});
}
});

Check failure

Code scanning / CodeQL

Missing rate limiting High

This route handler performs
a database access
, but is not rate-limited.

Copilot Autofix AI 12 days ago

To fix the problem, we need to introduce a rate-limiting middleware to the Express application. The express-rate-limit package is a well-known library for this purpose. We will configure the rate limiter to allow a maximum of 100 requests per 15 minutes and apply it to all routes in the bridgeApiRoute function.

  1. Install the express-rate-limit package.
  2. Import the express-rate-limit package in the api/routes/bridge.js file.
  3. Configure the rate limiter with appropriate settings.
  4. Apply the rate limiter to the Express application within the bridgeApiRoute function.
Suggested changeset 2
api/routes/bridge.js

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/api/routes/bridge.js b/api/routes/bridge.js
--- a/api/routes/bridge.js
+++ b/api/routes/bridge.js
@@ -6,4 +6,11 @@
 } from "../common.js";
+import RateLimit from "express-rate-limit";
 
 export default function bridgeApiRoute(app, config, db, features, lang) {
+  const limiter = RateLimit({
+    windowMs: 15 * 60 * 1000, // 15 minutes
+    max: 100, // max 100 requests per windowMs
+  });
+
+  app.use(limiter);
   const baseEndpoint = "/api/bridge";
EOF
@@ -6,4 +6,11 @@
} from "../common.js";
import RateLimit from "express-rate-limit";

export default function bridgeApiRoute(app, config, db, features, lang) {
const limiter = RateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // max 100 requests per windowMs
});

app.use(limiter);
const baseEndpoint = "/api/bridge";
package.json
Outside changed files

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/package.json b/package.json
--- a/package.json
+++ b/package.json
@@ -51,3 +51,4 @@
     "querystring": "^0.2.1",
-    "zero-md": "^2.3.1"
+    "zero-md": "^2.3.1",
+    "express-rate-limit": "^7.5.0"
   }
EOF
@@ -51,3 +51,4 @@
"querystring": "^0.2.1",
"zero-md": "^2.3.1"
"zero-md": "^2.3.1",
"express-rate-limit": "^7.5.0"
}
This fix introduces these dependencies
Package Version Security advisories
express-rate-limit (npm) 7.5.0 None
Copilot is powered by AI and may make mistakes. Always verify output.
Positive Feedback
Negative Feedback

Provide additional feedback

Please help us improve GitHub Copilot by sharing more details about this comment.

Please select one or more of the options
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.

Caution

Inline review comments failed to post. This is likely due to GitHub's limits when posting large numbers of comments.

🛑 Comments failed to post (53)
commands/shopdirectory.mjs (1)

79-89: ⚠️ Potential issue

Handle potential exceeding of Discord embed field limits

Discord embeds can have a maximum of 25 fields. If apiData.data contains more than 25 shop items, adding a field for each may exceed this limit and cause the bot to throw an error. Consider implementing pagination or limiting the number of items displayed to handle larger datasets gracefully.

Apply this diff to implement pagination using buttons or select menus:

+// Import necessary components for pagination
+import { ActionRowBuilder, ButtonBuilder, ButtonStyle } from "discord.js";
+
 // Add fields for each shop item
-apiData.data.forEach((shop) => {
+const itemsPerPage = 5;
+let page = 0;
+const totalPages = Math.ceil(apiData.data.length / itemsPerPage);
+
+const generateEmbed = (page) => {
+  const start = page * itemsPerPage;
+  const currentItems = apiData.data.slice(start, start + itemsPerPage);
+  const embed = new EmbedBuilder()
+    .setTitle("🛍️ Shop Directory")
+    .setDescription(
+      `🔍 Here are the shop items${
+        material ? ` for material: \`${material}\`` : ""
+      }.\nPage ${page + 1} of ${totalPages}`
+    )
+    .setColor(Colors.Blue);
+
+  currentItems.forEach((shop) => {
     const transactionType = shop.stock === -1 ? "💰 Buying" : "📦 Selling";
     const stockInfo = shop.stock !== -1 ? `**Stock:** ${shop.stock}\n` : "";

-    itemsEmbed.addFields([
+    embed.addFields([
       {
         name: `Item: ${shop.itemData.displayName}`,
         value: `**Seller:** \`${shop.userData.username}\`\n**Amount:** ${shop.amount}\n**Price:** $${shop.price}\n${stockInfo}**Type:** ${transactionType}\n**Location:** ${shop.x}, ${shop.y}, ${shop.z}`,
       },
     ]);
-  });
+  });
+
+  return embed;
+};
+
+const embedMessage = await interaction.editReply({
+  embeds: [generateEmbed(page)],
+  components: [
+    new ActionRowBuilder().addComponents(
+      new ButtonBuilder()
+        .setCustomId("prevPage")
+        .setLabel("Previous")
+        .setStyle(ButtonStyle.Primary)
+        .setDisabled(page === 0),
+      new ButtonBuilder()
+        .setCustomId("nextPage")
+        .setLabel("Next")
+        .setStyle(ButtonStyle.Primary)
+        .setDisabled(page === totalPages - 1)
+    ),
+  ],
+});
+
+// Set up a collector to handle button interactions
+const collector = embedMessage.createMessageComponentCollector({
+  time: 60000, // Collector will listen for interactions for 60 seconds
+});
+
+collector.on("collect", async (i) => {
+  if (i.customId === "prevPage" && page > 0) {
+    page--;
+  } else if (i.customId === "nextPage" && page < totalPages - 1) {
+    page++;
+  }
+  await i.update({
+    embeds: [generateEmbed(page)],
+    components: [
+      new ActionRowBuilder().addComponents(
+        new ButtonBuilder()
+          .setCustomId("prevPage")
+          .setLabel("Previous")
+          .setStyle(ButtonStyle.Primary)
+          .setDisabled(page === 0),
+        new ButtonBuilder()
+          .setCustomId("nextPage")
+          .setLabel("Next")
+          .setStyle(ButtonStyle.Primary)
+          .setDisabled(page === totalPages - 1)
+      ),
+    ],
+  });
+});
+
+collector.on("end", () => {
+  // Disable buttons after collector ends
+  embedMessage.edit({ components: [] });
+});

Alternatively, limit the number of items displayed to 25.

Committable suggestion skipped: line range outside the PR's diff.

commands/report.mjs (3)

55-55: ⚠️ Potential issue

Ensure userData is not null before accessing its properties

If the reporter's user data is not found, userData may be null. Accessing userData.discordId without checking if userData exists can cause a runtime error. Update the condition to handle this scenario.

Apply this diff:

-        if (!userData.discordId) {
+        if (!userData || !userData.discordId) {
📝 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.

    if (!userData || !userData.discordId) {

48-48: ⚠️ Potential issue

Use User ID instead of username for reported user

Discord usernames are not unique and can change. Using the User ID ensures the correct user is reported. Modify the code to use id instead of username.

Apply this diff:

-        const reportedUser = interaction.options.getUser("user").username;
+        const reportedUser = interaction.options.getUser("user").id;

Also, update the API request body accordingly.

📝 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.

    const reportedUser = interaction.options.getUser("user").id;

96-99: ⚠️ Potential issue

Avoid multiple replies to the same interaction

You are using interaction.reply in both success and error cases without returning or deferring the reply earlier. If the interaction has already been replied to or deferred, calling reply again will throw an error. Since you're not deferring the interaction, ensure that you only reply once or use editReply.

Apply this diff to use editReply instead:

-          interaction.reply({
+          interaction.editReply({

And add a return statement after the reply to prevent further code execution.

Committable suggestion skipped: line range outside the PR's diff.

api/routes/filter.js (4)

85-85: ⚠️ Potential issue

Sanitize content before embedding to prevent abuse

Including raw user content in embeds can lead to abuse, such as injection of malicious content or exceeding character limits. Sanitize the content to remove any harmful elements before adding it to the embed.

Consider applying input validation or truncating the content if it's too long.


112-114: ⚠️ Potential issue

Avoid exposing internal error details in API responses

Returning internal errors directly to the client can expose sensitive information. Instead, log the error internally and return a generic error message.

Apply this diff:

          console.log("Error processing request:", error);
          return res.send({
            success: false,
-           message: error,
+           message: "An internal server error occurred.",
          });
📝 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.

      console.log("Error processing request:", error);
      return res.send({
        success: false,
        message: "An internal server error occurred.",

41-47: ⚠️ Potential issue

Sanitize patterns before constructing regular expressions

Constructing regular expressions from untrusted input in filter.links can lead to Regular Expression Denial of Service (ReDoS) attacks or syntax errors. Ensure that the patterns are properly sanitized or escape any special characters.

Apply this diff to escape special characters:

 filter.links.forEach((link) => {
+  const escapedLink = link.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
-  const regex = new RegExp(link, "i");
+  const regex = new RegExp(escapedLink, "i");
   if (regex.test(content)) {

Alternatively, pre-compile the regex patterns safely when defining filter.links.

📝 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.

      filter.links.forEach((link) => {
        const escapedLink = link.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
        const regex = new RegExp(escapedLink, "i");
        if (regex.test(content)) {
          console.log(`URL detected: ${link}`);
          urlDetected = true;
          flaggedFor.push("URL/Advertising");
        }

35-35: 🛠️ Refactor suggestion

Avoid logging user-generated content to console in production

Logging the content received from users may expose sensitive or personal information in server logs. Consider removing this log statement or ensuring logs are appropriately sanitized and secured.

Apply this diff:

-          console.log("Received content:", content);
api/routes/report.js (1)

43-47: ⚠️ Potential issue

Prevent SQL Injection by Using Parameterized Queries

The SQL query at line 45 directly interpolates reportedId, which can lead to SQL injection vulnerabilities if reportedId contains malicious input.

Apply this diff to use parameterized queries:

- let dbQuery = `SELECT * FROM reports WHERE reportedId=${reportedId};`;
+ let dbQuery = `SELECT * FROM reports WHERE reportedId = ?;`;
+ await getReports(dbQuery, [reportedId]);

Also, modify the getReports function to accept parameters:

- function getReports(dbQuery) {
+ function getReports(dbQuery, params = []) {
    return new Promise((resolve, reject) => {
-     db.query(dbQuery, function (error, results, fields) {
+     db.query(dbQuery, params, function (error, results, fields) {
        // Rest of the code...

Committable suggestion skipped: line range outside the PR's diff.

api/routes/vault.js (1)

43-44: ⚠️ Potential issue

Prevent SQL Injection by Using Parameterized Queries

The SQL query at line 43 directly interpolates vaultId, which can lead to SQL injection vulnerabilities if vaultId contains malicious input.

Update the query to use parameterized queries:

- let dbQuery = `SELECT * FROM vault WHERE vaultId=${vaultId};`;
+ let dbQuery = `SELECT * FROM vault WHERE vaultId = ?;`;
+ getVault(dbQuery, [vaultId]);

Modify the getVault function to accept parameters:

- function getVault(dbQuery) {
+ function getVault(dbQuery, params = []) {
    db.query(dbQuery, params, function (error, results, fields) {
        // Rest of the code...

Committable suggestion skipped: line range outside the PR's diff.

api/routes/bridge.js (6)

99-127: ⚠️ Potential issue

Add Rate Limiting and Authentication to Sensitive Endpoint

The /api/bridge/clear endpoint truncates the bridge table without any rate limiting or authentication. This could be exploited to disrupt service.

Implement authentication checks and rate limiting to secure this endpoint. Only authorized users should have access to this operation.

🧰 Tools
🪛 GitHub Check: CodeQL

[failure] 99-127: Missing rate limiting
This route handler performs a database access, but is not rate-limited.


129-157: ⚠️ Potential issue

Secure Endpoint with Rate Limiting and Authentication

The /api/bridge/server/get endpoint lacks rate limiting and authentication, potentially exposing server status information to unauthorized users.

Add authentication checks and rate limiting to protect this endpoint from abuse and unauthorized access.

🧰 Tools
🪛 GitHub Check: CodeQL

[failure] 129-157: Missing rate limiting
This route handler performs a database access, but is not rate-limited.


159-191: ⚠️ Potential issue

Implement Authentication and Input Validation

The /api/bridge/server/update endpoint allows updating server status without authentication or proper input validation.

Ensure that only authenticated and authorized users can access this endpoint, and validate inputs to prevent malicious data from being saved.

🧰 Tools
🪛 GitHub Check: CodeQL

[failure] 159-191: Missing rate limiting
This route handler performs a database access, but is not rate-limited.


205-221: ⚠️ Potential issue

Correct Parameter Mismatch in Query

At line 207, there is a mismatch between the number of placeholders in the SQL query and the provided parameters.

Adjust the parameters to match the placeholders:

db.query(
    `DELETE FROM bridge WHERE bridgeId = ?;`,
-   [1, bridgeId],
+   [bridgeId],
    function (error, results, fields) {
        // Rest of the code...
📝 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.

      db.query(
        `DELETE FROM bridge WHERE bridgeId=?;`,
        [bridgeId],
        function (error, results, fields) {
          if (error) {
            return res.send({
              success: false,
              message: `${error}`,
            });
          }

          return res.send({
            success: true,
            message: `Bridge ID ${bridgeId} has been executed.`,
          });
        }
      );

42-44: ⚠️ Potential issue

Prevent SQL Injection by Using Parameterized Queries

The SQL query at line 42 directly interpolates bridgeId, introducing a potential SQL injection vulnerability.

Use parameterized queries to safeguard against SQL injection:

- let dbQuery = `SELECT * FROM bridge WHERE bridgeId=${bridgeId};`;
+ let dbQuery = `SELECT * FROM bridge WHERE bridgeId = ?;`;
+ getBridge(dbQuery, [bridgeId]);

Update the getBridge function to accept parameters:

- function getBridge(dbQuery) {
+ function getBridge(dbQuery, params = []) {
    db.query(dbQuery, params, function (error, results, fields) {
        // Rest of the code...

Committable suggestion skipped: line range outside the PR's diff.


47-50: ⚠️ Potential issue

Prevent SQL Injection by Using Parameterized Queries

The SQL query at line 48 directly interpolates targetServer, which can lead to SQL injection if targetServer contains malicious input.

Modify the query to use parameterization:

- let dbQuery = `SELECT * FROM bridge WHERE targetServer='${targetServer}' AND processed=0;`;
+ let dbQuery = `SELECT * FROM bridge WHERE targetServer = ? AND processed=0;`;
+ getBridge(dbQuery, [targetServer]);

Ensure getBridge accepts parameters as shown previously.

Committable suggestion skipped: line range outside the PR's diff.

commands/bridge.mjs (1)

148-149: ⚠️ Potential issue

Add input validation for user-provided inputs to enhance security

The command and targetedServer inputs are directly used in the API request body without validation or sanitization. This could lead to security vulnerabilities, such as injection attacks or processing invalid data.

Consider adding input validation to ensure the inputs meet expected formats and to prevent potential security risks.

Also applies to: 159-160

api/internal_redirect/report.js (2)

9-16: ⚠️ Potential issue

Await asynchronous operations to ensure proper execution order

The postAPIRequest function is likely asynchronous, but it's not awaited. Additionally, res.redirect is called immediately after, which may cause the redirection to occur before the API request completes.

Apply this diff to await the postAPIRequest and ensure the API request completes before redirecting:

+    await postAPIRequest(
       `${process.env.siteAddress}/api/report/create`,
       req.body,
       `${process.env.siteAddress}/report`,
       res
     );

-    res.redirect(`${process.env.siteAddress}/`);

Alternatively, if postAPIRequest handles the response and redirection internally, you may remove the redundant res.redirect call.

Committable suggestion skipped: line range outside the PR's diff.


7-7: 💡 Codebase verification

Add authentication checks to protect the report creation endpoint

The route is in the internal_redirect section which bypasses the global authentication middleware. While hasPermission is imported, it's not being used. You should add both session validation and permission checks:

app.post(baseEndpoint + "/create", hasPermission('report.create'), async function (req, res) {
  if (!req.session?.user?.username) {
    return res.status(401).send({ error: 'Unauthorized' });
  }
  req.body.reporterUser = req.session.user.username;
  // ... rest of the code
});
🔗 Analysis chain

Add null checks for session user data to prevent runtime errors

The code assumes that req.session.user.username exists. If req.session or req.session.user is undefined (e.g., if the user is not authenticated), this will cause a runtime error.

Consider adding checks to ensure req.session.user is defined before accessing username. For example:

if (req.session.user && req.session.user.username) {
  req.body.reporterUser = req.session.user.username;
} else {
  // Handle the case where the user is not authenticated
}
🏁 Scripts executed

The following scripts were executed for the analysis:

Error: No shell scripts found


Script:

#!/bin/bash
# Check the content of the report.js file
cat api/internal_redirect/report.js

# Look for authentication middleware patterns
ast-grep --pattern 'app.use($middleware)'

# Search for session checks patterns
rg "req\.session" -A 2

# Look for similar username access patterns
rg "req\.session\.user\.username" -A 2

Length of output: 12655


Script:

#!/bin/bash
# Check for route protection patterns in report.js
ast-grep --pattern 'app.post($_, hasPermission($$$))'

# Look for other middleware usage in report.js
rg "hasPermission" api/internal_redirect/report.js -A 2

Length of output: 239


Script:

#!/bin/bash
# Check for global middleware setup in app.js
cat app.js

# Look for route protection patterns in the main application setup
rg "app.use\(.*hasPermission" -A 2

Length of output: 4328

api/internal_redirect/bridge.js (1)

12-19: ⚠️ Potential issue

Await asynchronous operations to ensure proper execution order

The postAPIRequest function is likely asynchronous but is not awaited. Calling res.redirect immediately after may cause the redirection to occur before the API request completes.

Apply this diff to await the postAPIRequest and ensure the API request completes before redirecting:

+    await postAPIRequest(
       `${process.env.siteAddress}/api/bridge/command/add`,
       req.body,
       `${process.env.siteAddress}/dashboard/bridge`,
       res
     );

-    res.redirect(`${process.env.siteAddress}/dashboard/bridge`);

Alternatively, if postAPIRequest handles the response and redirection internally, you may remove the redundant res.redirect call.

Committable suggestion skipped: line range outside the PR's diff.

commands/ranks.mjs (1)

19-31: ⚠️ Potential issue

Add missing import statement for features.json.

The code references features but is missing the import statement.

Add this import at the top of the file:

+import features from "../features.json" assert { type: "json" };
📝 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.

import features from "../features.json" assert { type: "json" };

    if (!features.ranks) {
      const errorEmbed = new EmbedBuilder()
        .setTitle("Feature Disabled")
        .setDescription(
          `This feature has been disabled by your System Administrator.`
        )
        .setColor(Colors.Red);

      return interaction.reply({
        embeds: [errorEmbed],
        ephemeral: true,
      });      
    }
commands/vault.mjs (2)

40-43: ⚠️ Potential issue

Fix typo in ephemeral property.

The 'ephemeral' property is misspelled as 'empheral'.

    interaction.reply({
      embeds: [embed],
-      empheral: false,
+      ephemeral: false,
    });
📝 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.

    interaction.reply({
      embeds: [embed],
      ephemeral: false,
    });

5-45: ⚠️ Potential issue

Fix class name to match functionality.

The class name RanksCommand does not match its functionality. The command handles vault operations, not ranks.

-export class RanksCommand extends Command {
+export class VaultCommand extends Command {
📝 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.

export class VaultCommand extends Command {
  constructor(context, options) {
    super(context, { ...options });
  }

  registerApplicationCommands(registry) {
    registry.registerChatInputCommand((builder) =>
      builder
        .setName("vault")
        .setDescription("Display link to view link to the map vault.")
    );
  }

  async chatInputRun(interaction) {
    if (!features.vault) {
      const errorEmbed = new EmbedBuilder()
        .setTitle("Feature Disabled")
        .setDescription(
          `This feature has been disabled by your System Administrator.`
        )
        .setColor(Colors.Red);

      return interaction.reply({
        embeds: [errorEmbed],
        ephemeral: true,
      });      
    }

    const embed = new EmbedBuilder()
      .setTitle(`Vault`)
      .setDescription(
        `Explore the Vault to discover and download our previous maps and explore the worlds and builds of our community: ${process.env.siteAddress}/vault`
      )
      .setColor(Colors.DarkGold);

    interaction.reply({
      embeds: [embed],
      empheral: false,
    });
  }
}
cron/discordStatsUpdateCron.js (2)

11-13: ⚠️ Potential issue

Review API key exposure in headers.

The API key is exposed in request headers. Consider using more secure authentication methods or moving sensitive data to environment variables.


21-26: 🛠️ Refactor suggestion

Add error handling for JSON parsing.

The JSON.parse() operation could throw an error if statusInfo is not valid JSON. Consider adding try-catch block:

 apiData.data.forEach((server) => {
+  try {
     const serverInfo = JSON.parse(server.statusInfo);
     serverInfo.forEach((status) => {
       totalPlayers += status.playerCount;
     });
+  } catch (error) {
+    console.error(`Failed to parse statusInfo for server: ${error}`);
+  }
 });
📝 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.

      apiData.data.forEach((server) => {
        try {
          const serverInfo = JSON.parse(server.statusInfo);
          serverInfo.forEach((status) => {
            totalPlayers += status.playerCount;
          });
        } catch (error) {
          console.error(`Failed to parse statusInfo for server: ${error}`);
        }
      });
cron/cakeDayUserCheck.js (1)

19-34: 🛠️ Refactor suggestion

Implement batch processing for webhook messages.

Currently, webhook messages are sent individually for each user. Consider batching them to reduce API calls:

-        results.forEach((user) => {
-          const joinDate = moment(user.joined);
-          const years = moment().diff(joinDate, "years");
+        const batchSize = 10;
+        for (let i = 0; i < results.length; i += batchSize) {
+          const batch = results.slice(i, i + batchSize);
+          const messages = batch.map(user => {
+            const joinDate = moment(user.joined);
+            const years = moment().diff(joinDate, "years");
+            return new MessageBuilder()
+              .setTitle(`🎂 Happy cake day to ${user.username}! :tada:`)
+              .setDescription(
+                `Celebrating ${years} year(s) with ${config.siteConfiguration.siteName}`
+              )
+              .setColor(Colors.Blurple)
+              .setFooter(
+                `To get your cake day mention, make sure you are a member on our website.`
+              );
+          });
+          await Promise.all(messages.map(msg => welcomeHook.send(msg)));
+        }

Committable suggestion skipped: line range outside the PR's diff.

views/vault.ejs (1)

34-36: 💡 Codebase verification

Security vulnerabilities found in vault download implementation

Several critical security issues need to be addressed:

  • SQL Injection vulnerability in /api/vault/get endpoint where vaultId is directly concatenated into the query
  • No validation or sanitization of redirectUrl before storing or rendering
  • Insufficient permission validation for vault access
  • Direct embedding of user-provided URLs in href attributes

Please implement:

  1. Use parameterized queries for all SQL operations
  2. Add URL validation and sanitization for redirectUrl
  3. Implement proper permission checks before serving vault content
  4. Consider using Content Security Policy (CSP) headers
🔗 Analysis chain

Verify download URL security.

The redirectUrl is directly embedded in the href attribute. Ensure that:

  1. The URL is properly sanitized to prevent XSS attacks
  2. The download endpoint validates user permissions
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for URL sanitization in the codebase
rg -A 5 "redirectUrl.*vault"

Length of output: 30


Script:

#!/bin/bash
# Find vault-related files
fd vault

# Search for download or redirect handling
rg -i "download.*vault" -A 5
rg -i "redirect.*vault" -A 5

# Look for URL processing/sanitization
ast-grep --pattern 'app.get("/vault", $$$)'
ast-grep --pattern 'router.get("/vault", $$$)'

Length of output: 5400


Script:

#!/bin/bash
# Check vault API implementation
cat api/routes/vault.js

# Look for URL sanitization
rg -i "sanitize.*url" -A 5
rg -i "validate.*url" -A 5

# Check vault route implementation
ast-grep --pattern 'router.get("/api/vault", $$$)'
ast-grep --pattern 'app.get("/api/vault", $$$)'

Length of output: 71141

api/internal_redirect/vault.js (2)

6-22: 🛠️ Refactor suggestion

Refactor to reduce code duplication.

The three route handlers share identical structure. Consider refactoring to a common handler:

+ function createVaultHandler(operation) {
+   return async function(req, res) {
+     if (!hasPermission("zander.web.vault", req, res)) return;
+     req.body.actioningUser = req.session.user.userId;
+     postAPIRequest(
+       `${process.env.siteAddress}/api/vault/${operation}`,
+       req.body,
+       `${process.env.siteAddress}/dashboard/vault`,
+       res
+     );
+     res.redirect(`${process.env.siteAddress}/dashboard/vault`);
+     return res;
+   };
+ }

- app.post(baseEndpoint + "/create", async function (req, res) {
-   // ... duplicate code ...
- });
+ app.post(baseEndpoint + "/create", createVaultHandler("create"));

- app.post(baseEndpoint + "/edit", async function (req, res) {
-   // ... duplicate code ...
- });
+ app.post(baseEndpoint + "/edit", createVaultHandler("edit"));

- app.post(baseEndpoint + "/delete", async function (req, res) {
-   // ... duplicate code ...
- });
+ app.post(baseEndpoint + "/delete", createVaultHandler("delete"));

Also applies to: 24-40, 42-58


12-22: ⚠️ Potential issue

Improve error handling in postAPIRequest.

The current implementation:

  1. Sends a redirect response
  2. Then tries to redirect again
  3. Returns the response object

This could lead to "Cannot set headers after they are sent" errors.

- postAPIRequest(
-   `${process.env.siteAddress}/api/vault/create`,
-   req.body,
-   `${process.env.siteAddress}/dashboard/vault`,
-   res
- );
- res.redirect(`${process.env.siteAddress}/dashboard/vault`);
- return res;
+ await postAPIRequest(
+   `${process.env.siteAddress}/api/vault/create`,
+   req.body,
+   `${process.env.siteAddress}/dashboard/vault`,
+   res
+ );
+ return;
📝 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.

    await postAPIRequest(
      `${process.env.siteAddress}/api/vault/create`,
      req.body,
      `${process.env.siteAddress}/dashboard/vault`,
      res
    );
    return;
  });
listeners/filter.js (1)

39-56: 🛠️ Refactor suggestion

Enhance error handling and user feedback.

The current implementation has several areas for improvement:

  1. No rate limiting for repeat offenders
  2. No escalation for multiple violations
  3. Message deletion delay could be configurable

Consider implementing a more robust solution:

 if (dataResponse.success === false) {
+  // Track violation count
+  const violations = await getViolationCount(message.author.id);
+  
   // Create an embed to warn the user
   const embed = new EmbedBuilder()
     .setTitle(`Prohibited content detected!`)
     .setDescription(
-      `\`${message.author.username}\`, please refrain from using prohibited content/phrases. Continued violations may result in penalties.`
+      `\`${message.author.username}\`, this is violation #${violations}. Please refrain from using prohibited content/phrases. ${violations >= 3 ? 'Further violations will result in a temporary mute.' : 'Continued violations may result in penalties.'}`
     )
     .setColor(`#ff3333`);

   // Send the embed to the channel
   await message.reply({ embeds: [embed] });

+  // Apply penalties for repeat offenders
+  if (violations >= 3) {
+    await timeoutUser(message.member, 300000); // 5-minute timeout
+  }

   // Delete the message after a short delay to ensure the embed is sent first
   setTimeout(async () => {
     if (message.deletable) {
       await message.delete();
     }
-  }, 500); // Delay of 500ms before deleting the message
+  }, process.env.MESSAGE_DELETE_DELAY || 500);
 }

Committable suggestion skipped: line range outside the PR's diff.

routes/dashboard/dashboard.js (1)

59-82: ⚠️ Potential issue

Enhance error handling in bridge route.

The current implementation lacks proper error handling for API failures and doesn't validate the response data.

 app.get("/dashboard/bridge", async function (req, res) {
   if (!hasPermission("zander.web.bridge", req, res, features)) return;

-  const fetchURL = `${process.env.siteAddress}/api/bridge/get`;
-  const response = await fetch(fetchURL, {
-    headers: { "x-access-token": process.env.apiKey },
-  });
-  const apiData = await response.json();
-
-  console.log(apiData);
+  try {
+    const fetchURL = `${process.env.siteAddress}/api/bridge/get`;
+    const response = await fetch(fetchURL, {
+      headers: { "x-access-token": process.env.apiKey },
+    });
+    
+    if (!response.ok) {
+      throw new Error(`API request failed with status ${response.status}`);
+    }
+    
+    const apiData = await response.json();
+    if (!Array.isArray(apiData)) {
+      throw new Error('Invalid response format');
+    }

+    return res.view("dashboard/bridge", {
+      pageTitle: `Dashboard - Bridge`,
+      config: config,
+      apiData: apiData,
+      features: features,
+      req: req,
+      globalImage: getGlobalImage(),
+      moment: moment,
+      announcementWeb: await getWebAnnouncement(),
+    });
+  } catch (error) {
+    console.error('Bridge API Error:', error);
+    return res.status(500).view("error", {
+      pageTitle: "Error",
+      error: "Failed to load bridge data",
+      config: config,
+      features: features,
+      req: req,
+      globalImage: getGlobalImage(),
+    });
+  }
-  res.view("dashboard/bridge", {
-    pageTitle: `Dashboard - Bridge`,
-    config: config,
-    apiData: apiData,
-    features: features,
-    req: req,
-    globalImage: getGlobalImage(),
-    moment: moment,
-    announcementWeb: await getWebAnnouncement(),
-  });
-
-  return res;
 });
📝 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.

app.get("/dashboard/bridge", async function (req, res) {
  if (!hasPermission("zander.web.bridge", req, res, features)) return;

  try {
    const fetchURL = `${process.env.siteAddress}/api/bridge/get`;
    const response = await fetch(fetchURL, {
      headers: { "x-access-token": process.env.apiKey },
    });
    
    if (!response.ok) {
      throw new Error(`API request failed with status ${response.status}`);
    }
    
    const apiData = await response.json();
    if (!Array.isArray(apiData)) {
      throw new Error('Invalid response format');
    }

    return res.view("dashboard/bridge", {
      pageTitle: `Dashboard - Bridge`,
      config: config,
      apiData: apiData,
      features: features,
      req: req,
      globalImage: getGlobalImage(),
      moment: moment,
      announcementWeb: await getWebAnnouncement(),
    });
  } catch (error) {
    console.error('Bridge API Error:', error);
    return res.status(500).view("error", {
      pageTitle: "Error",
      error: "Failed to load bridge data",
      config: config,
      features: features,
      req: req,
      globalImage: getGlobalImage(),
    });
  }
});
views/report.ejs (2)

19-24: ⚠️ Potential issue

Sanitize alert content before rendering.

The alert content from cookies is rendered directly in the template, which could lead to XSS vulnerabilities.

 <% if (req.cookies.alertType) { %>
     <%- include("../partials/alert.ejs", {
       alertType: req.cookies.alertType,
-      content: req.cookies.alertContent
+      content: sanitizeHtml(req.cookies.alertContent)
     }) %>
 <% } %>

Committable suggestion skipped: line range outside the PR's diff.


26-58: ⚠️ Potential issue

Add CSRF protection and input validation.

The form submission lacks CSRF protection and client-side input validation.

-<form method="post" action="<%= process.env.siteAddress %>/redirect/report/create">
+<form method="post" action="<%= process.env.siteAddress %>/redirect/report/create" onsubmit="return validateForm()">
+  <%- include("partials/form/csrfToken.ejs") %>
   
   <%- include("partials/form/inputHidden.ejs", {
     elementName: "reportPlatform",
     required: true,
     elementValue: "WEB"
   }) %>

   <label for="reportedUser">User to Report</label>
   <%- include("partials/form/inputText.ejs", {
     elementName: "reportedUser",
     required: true,
-    elementValue: null
+    elementValue: null,
+    pattern: "^[a-zA-Z0-9_]{3,16}$",
+    title: "Username must be 3-16 characters long and contain only letters, numbers, and underscores"
   }) %>
   <br>

   <label for="reportReason">Report Reason</label>
   <%- include("partials/form/inputText.ejs", {
     elementName: "reportReason",
     required: true,
-    elementValue: null
+    elementValue: null,
+    minlength: "10",
+    maxlength: "200"
   }) %>
   <br>

   <label for="reportReasonEvidence">Report Reason Evidence</label>
   <%- include("partials/form/inputTextArea.ejs", {
     elementName: "reportReasonEvidence",
     required: false,
-    elementValue: null
+    elementValue: null,
+    maxlength: "1000"
   }) %>
   <br><br>

   <button type="submit" class="btn btn-primary">Submit Report</button>
 </form>

+<script>
+function validateForm() {
+  const reportedUser = document.getElementsByName("reportedUser")[0].value;
+  const reportReason = document.getElementsByName("reportReason")[0].value;
+  
+  if (!/^[a-zA-Z0-9_]{3,16}$/.test(reportedUser)) {
+    alert("Invalid username format");
+    return false;
+  }
+  
+  if (reportReason.length < 10 || reportReason.length > 200) {
+    alert("Report reason must be between 10 and 200 characters");
+    return false;
+  }
+  
+  return true;
+}
+</script>
📝 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.

      <form method="post" action="<%= process.env.siteAddress %>/redirect/report/create" onsubmit="return validateForm()">
        <%- include("partials/form/csrfToken.ejs") %>
        
        <%- include("partials/form/inputHidden.ejs", {
            elementName: "reportPlatform",
            required: true,
            elementValue: "WEB"
        }) %>

        <label for="reportedUser">User to Report</label>
        <%- include("partials/form/inputText.ejs", {
            elementName: "reportedUser",
            required: true,
            elementValue: null,
            pattern: "^[a-zA-Z0-9_]{3,16}$",
            title: "Username must be 3-16 characters long and contain only letters, numbers, and underscores"
        }) %>
        <br>

        <label for="reportReason">Report Reason</label>
        <%- include("partials/form/inputText.ejs", {
            elementName: "reportReason",
            required: true,
            elementValue: null,
            minlength: "10",
            maxlength: "200"
        }) %>
        <br>

        <label for="reportReasonEvidence">Report Reason Evidence</label>
        <%- include("partials/form/inputTextArea.ejs", {
            elementName: "reportReasonEvidence",
            required: false,
            elementValue: null,
            maxlength: "1000"
        }) %>
        <br><br>

        <button type="submit" class="btn btn-primary">Submit Report</button>
      </form>

<script>
function validateForm() {
  const reportedUser = document.getElementsByName("reportedUser")[0].value;
  const reportReason = document.getElementsByName("reportReason")[0].value;
  
  if (!/^[a-zA-Z0-9_]{3,16}$/.test(reportedUser)) {
    alert("Invalid username format");
    return false;
  }
  
  if (reportReason.length < 10 || reportReason.length > 200) {
    alert("Report reason must be between 10 and 200 characters");
    return false;
  }
  
  return true;
}
</script>
routes/dashboard/applications.js (1)

66-66: 💡 Codebase verification

Update is correct, but needs consistency across the file

The change to use id as the query parameter is correct and matches the API implementation. However, there's another usage of this endpoint in the same file that still uses applicationId as the parameter name, which should be updated for consistency.

  • routes/dashboard/applications.js: Update the other instance using applicationId query parameter to use id instead
🔗 Analysis chain

Verify API endpoint compatibility.

The query parameter has been changed from applicationId to id. Please ensure this change is consistent with the API endpoint implementation.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if the API endpoint accepts both old and new parameter names
# to avoid breaking changes

rg -g "*.js" -A 5 "application/get\?(?:application)?[iI]d="

Length of output: 519


Script:

#!/bin/bash
# Find the API route implementation
ast-grep --pattern 'router.$_("/api/application/get", $_)'

# Find any other fetch calls to this endpoint
rg -g "*.js" "fetch.*api/application/get"

# Look for route handler implementations
rg -g "*.js" "/api/application/get.*req.*res"

Length of output: 467


Script:

#!/bin/bash
# Find potential API implementation files
fd -e js application

# Look for route handlers with different patterns
ast-grep --pattern 'app.$_("/api/application/get", $_)'

# Search for API documentation
fd -e md -e txt -e yaml -e json api

Length of output: 214


Script:

#!/bin/bash
# Check contents of potential implementation files
cat api/routes/application.js
cat api/internal_redirect/application.js

# Additional search for route handler with specific parameter
rg -g "*.js" "req\.query\.(applicationId|id)"

Length of output: 8673

routes/dashboard/announcement.js (1)

69-70: 💡 Codebase verification

Bug found in announcement GET endpoint implementation

The parameter change from announcementSlug to announcementId is consistent with the API implementation. However, there's a critical bug in the endpoint that prevents it from working:

  • In api/routes/announcement.js, line 45: if (req.query === "announcementId") is incorrect and should be if (announcementId)
🔗 Analysis chain

Verify API endpoint compatibility.

The query parameter has been changed from announcementSlug to announcementId. Please ensure this change is consistent with the API endpoint implementation.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if the API endpoint accepts both old and new parameter names
# to avoid breaking changes

rg -g "*.js" -A 5 "announcement/get\?(?:announcement)?(?:[sS]lug|[iI]d)="

Length of output: 544


Script:

#!/bin/bash
# Find the API endpoint implementation
ast-grep --pattern 'app.$_("/api/announcement/get", $_)'

# Search for any historical usage with 'slug'
rg -g "*.js" "announcement/get.*[sS]lug"

# Find any route handlers for this endpoint
ast-grep --pattern 'router.$_("/announcement/get", $_)'

Length of output: 159


Script:

#!/bin/bash
# Find announcement-related route handlers and API implementations
fd "announcement" --type f --exec rg -l "router\.|app\." {} \;

# Search for files handling announcement query parameters
rg -g "*.js" "req\.query\.announcement(?:Id|Slug)"

# Look for announcement-related API files
fd "announcement" --type f --exec rg -l "module\.exports.*=.*\{" {} \;

Length of output: 382


Script:

#!/bin/bash
# Check the contents of API implementation files
echo "=== API Routes Implementation ==="
cat "./api/routes/announcement.js"

echo -e "\n=== Internal Redirect Implementation ==="
cat "./api/internal_redirect/announcement.js"

Length of output: 8747

routes/dashboard/vault.js (2)

25-30: 🛠️ Refactor suggestion

Add error handling for API response.

The API response handling is missing error checks. Consider adding proper error handling:

     const fetchURL = `${process.env.siteAddress}/api/vault/get`;
     const response = await fetch(fetchURL, {
       headers: { "x-access-token": process.env.apiKey },
     });
+    if (!response.ok) {
+      return res.status(response.status).send(`API Error: ${response.statusText}`);
+    }
     const apiData = await response.json();
+    if (!apiData.success) {
+      return res.status(400).send(apiData.message || 'Failed to fetch vault data');
+    }
📝 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.

    const fetchURL = `${process.env.siteAddress}/api/vault/get`;
    const response = await fetch(fetchURL, {
      headers: { "x-access-token": process.env.apiKey },
    });
    if (!response.ok) {
      return res.status(response.status).send(`API Error: ${response.statusText}`);
    }
    const apiData = await response.json();
    if (!apiData.success) {
      return res.status(400).send(apiData.message || 'Failed to fetch vault data');
    }

63-64: 🛠️ Refactor suggestion

Add input validation for vaultId.

The vaultId query parameter is used without validation. Consider adding validation to prevent potential issues:

+    if (!req.query.vaultId) {
+      return res.status(400).send('Missing vaultId parameter');
+    }
     const vaultId = req.query.vaultId;

Committable suggestion skipped: line range outside the PR's diff.

commands/profile.mjs (1)

50-50: 🛠️ Refactor suggestion

Add error handling for getProfilePicture.

The profile picture retrieval lacks error handling. Consider adding a fallback:

-    let profilePicture = await getProfilePicture(apiData.data.profileData.username);
+    let profilePicture;
+    try {
+      profilePicture = await getProfilePicture(apiData.data.profileData.username);
+    } catch (error) {
+      console.error('Failed to fetch profile picture:', error);
+      profilePicture = 'default_avatar_url';  // Add a default avatar URL
+    }

Also applies to: 64-64

views/dashboard/vault/vault-list.ejs (2)

51-55: ⚠️ Potential issue

Enhance security of the delete form submission.

The form submission could be vulnerable to CSRF attacks. Consider adding CSRF protection tokens.

Add CSRF token to the form:

 <form action="<%= process.env.siteAddress %>/redirect/vault/delete" method="post">
+    <input type="hidden" name="_csrf" value="<%= csrfToken %>">
     <input type="hidden" name="vaultId" value="<%= vault.vaultId %>">
     <input type="hidden" name="actioningUser" value="<%= req.session.user.userId %>">
     <button class="btn btn-sm btn-danger" type="submit">Delete</button>
 </form>

Committable suggestion skipped: line range outside the PR's diff.


46-46: ⚠️ Potential issue

Prevent potential XSS in vault display name.

The vault display name is rendered without escaping, which could lead to XSS attacks.

Escape the display name:

-<td><%= vault.displayName %></td>
+<td><%- htmlEscape(vault.displayName) %></td>

Committable suggestion skipped: line range outside the PR's diff.

commands/status.mjs (2)

47-48: ⚠️ Potential issue

Add error handling for JSON parsing.

The JSON parsing could throw an error if the data is malformed.

Wrap the JSON parsing in try-catch:

-const statusInfo = JSON.parse(serverStatus.statusInfo);
-statusInfo.forEach((server) => {
+try {
+  const statusInfo = JSON.parse(serverStatus.statusInfo);
+  statusInfo.forEach((server) => {
+} catch (error) {
+  console.error('Error parsing status info:', error);
+  return;
+}
📝 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.

        try {
          const statusInfo = JSON.parse(serverStatus.statusInfo);
          statusInfo.forEach((server) => {
        } catch (error) {
          console.error('Error parsing status info:', error);
          return;
        }

24-27: 🛠️ Refactor suggestion

Enhance security of API key transmission.

The API key is sent as a header without additional security measures.

Consider:

  1. Using HTTPS for all API calls
  2. Adding request signing or additional authentication measures
  3. Implementing rate limiting
 const response = await fetch(fetchURL, {
   headers: { 
     "x-access-token": process.env.apiKey,
+    "Content-Type": "application/json",
+    "User-Agent": "StatusCommand/1.0"
   },
+  timeout: 5000
 });
📝 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.

      const fetchURL = `${process.env.siteAddress}/api/bridge/server/get`;
      const response = await fetch(fetchURL, {
        headers: { 
          "x-access-token": process.env.apiKey,
          "Content-Type": "application/json",
          "User-Agent": "StatusCommand/1.0"
        },
        timeout: 5000
      });
views/dashboard/bridge.ejs (2)

24-50: ⚠️ Potential issue

Add input validation for command submission.

The command input lacks validation, which could lead to command injection or unauthorized commands being executed.

Add input validation:

  1. Implement client-side validation
  2. Add pattern matching for allowed commands
  3. Consider adding a dropdown for predefined commands instead of free text
 <div class="form-group col">
     <label for="command">Command</label>
     <%- include("../partials/form/inputText.ejs", {
         elementName: "command",
         elementValue: null,
-        required: true
+        required: true,
+        pattern: "^[a-zA-Z0-9_\-]+$",
+        maxlength: "50",
+        onkeypress: "return validateCommandInput(event)"
     }) %>
 </div>

Committable suggestion skipped: line range outside the PR's diff.


81-82: ⚠️ Potential issue

Escape command output in table display.

The command output is displayed without escaping, which could lead to XSS attacks.

Escape the command output:

-<td><code><%= bridge.command %></code></td>
+<td><code><%- htmlEscape(bridge.command) %></code></td>

Committable suggestion skipped: line range outside the PR's diff.

app.js (1)

42-45: ⚠️ Potential issue

Remove duplicate imports.

The following imports are duplicated:

  • discordController.js (lines 23 and 42)
  • bridgeCleanupCron.js (lines 25 and 44)

Apply this diff to remove the duplicates:

-import("./controllers/discordController.js");
-import("./cron/userCodeExpiryCron.js");
-import("./cron/bridgeCleanupCron.js");
-import("./cron/discordStatsUpdateCron.js");

Committable suggestion skipped: line range outside the PR's diff.

routes/profileRoutes.js (1)

54-58: ⚠️ Potential issue

Potential security vulnerability in report fetching.

The userId is directly interpolated into the URL without sanitization. While the API might handle sanitization, it's safer to encode URL parameters.

Apply this diff to encode the userId:

-const fetchReportsURL = `${process.env.siteAddress}/api/report/get?reporterId=${profileApiData.data[0].userId}`;
+const fetchReportsURL = `${process.env.siteAddress}/api/report/get?reporterId=${encodeURIComponent(profileApiData.data[0].userId)}`;
📝 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.

        const fetchReportsURL = `${process.env.siteAddress}/api/report/get?reporterId=${encodeURIComponent(profileApiData.data[0].userId)}`;
        const reportsResponse = await fetch(fetchReportsURL, {
          headers: { "x-access-token": process.env.apiKey },
        });
        const profileReportsApiData = await reportsResponse.json();
api/routes/shopdirectory.js (2)

39-53: 🛠️ Refactor suggestion

Improve error handling for external API calls.

The Craftdex API call could benefit from timeout and retry logic to handle temporary failures.

Consider implementing:

  1. Request timeout
  2. Retry logic with exponential backoff
  3. Circuit breaker pattern for API health

118-122: ⚠️ Potential issue

Critical: SQL injection vulnerability in material parameter.

Direct string interpolation in SQL queries is unsafe. Use parameterized queries instead.

Apply this diff to use parameterized queries:

-const dbQuery = material
-  ? `SELECT * FROM shoppingDirectory WHERE item LIKE '%${material}%' OR item LIKE '%${material
-      .toUpperCase()
-      .replace(/ /g, "_")}%';`
-  : `SELECT * FROM shoppingDirectory;`;
+const dbQuery = material
+  ? {
+      text: 'SELECT * FROM shoppingDirectory WHERE item LIKE $1 OR item LIKE $2',
+      values: [`%${material}%`, `%${material.toUpperCase().replace(/ /g, "_")}%`]
+    }
+  : 'SELECT * FROM shoppingDirectory';
📝 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.

      const dbQuery = material
        ? {
            text: 'SELECT * FROM shoppingDirectory WHERE item LIKE $1 OR item LIKE $2',
            values: [`%${material}%`, `%${material.toUpperCase().replace(/ /g, "_")}%`]
          }
        : 'SELECT * FROM shoppingDirectory';
routes/index.js (1)

153-153: 🛠️ Refactor suggestion

Remove debug console.log statement.

Production code should not contain debug console.log statements.

-    console.log(shopApiData.data[0].userData);
views/dashboard/vault/vault-editor.ejs (1)

108-121: 🛠️ Refactor suggestion

Add type validation for position field.

The position field should be restricted to numeric values for proper ordering.

-                              elementName: "position",
-                              elementValue: null,
-                              required: true
+                              elementName: "position",
+                              elementValue: null,
+                              required: true,
+                              type: "number",
+                              min: "0"
📝 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.

                      <% if (type === 'create') { %>
                          <%- include("../../partials/form/inputText.ejs", {
                              elementName: "position",
                              elementValue: null,
                              required: true,
                              type: "number",
                              min: "0"
                          }) %>
                      <% } %>
                      <% if (type === 'edit') { %>
                          <%- include("../../partials/form/inputText.ejs", {
                              elementName: "position",
                              elementValue: vaultApiData.position,
                              required: true
                          }) %>
                      <% } %>
views/modules/profile/profile.ejs (1)

153-179: 💡 Codebase verification

Add defensive checks for reports data to prevent potential errors

The reports feature needs additional error handling:

  • Add null check for profileReportsApiData.data before passing to view
  • Provide fallback empty array when data is undefined
  • Validate API response structure

Example:

const profileReportsApiData = {
  data: (await reportsResponse.json())?.data || []
};
🔗 Analysis chain

Consider enhancing the reports table with additional features.

The reports table implementation is good, but could benefit from:

  1. Pagination for large datasets
  2. Sorting capabilities
  3. Status indicators for reports
  4. Action buttons for report management

Also, verify that profileReportsApiData.data is properly handled when empty or undefined.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for proper error handling in the reports API route

# Search for error handling in the reports API route
ast-grep --pattern 'app.get($_, function($_, $_) {
  $$$
  if (!results || !results.length) {
    $$$
  }
  $$$
})'

Length of output: 117


Script:

#!/bin/bash
# Find relevant API route files
echo "=== Searching for API route files ==="
fd -e js -e ts "routes|api" --type f

echo -e "\n=== Searching for profileReportsApiData usage ==="
rg "profileReportsApiData" -l

echo -e "\n=== Searching for reports-related API endpoints ==="
rg "app\.(get|post).*reports.*" -A 5

Length of output: 610


Script:

#!/bin/bash
# Examine profileRoutes.js content
echo "=== Checking profileRoutes.js content ==="
cat routes/profileRoutes.js

echo -e "\n=== Looking for reports-related code blocks ==="
rg -A 10 -B 5 "profileReportsApiData" routes/profileRoutes.js

Length of output: 6430

api/routes/user.js (2)

129-130: ⚠️ Potential issue

Fix SQL injection vulnerability in user queries.

The queries are vulnerable to SQL injection. Use parameterized queries consistently.

- `SELECT * FROM users WHERE discordId=?;`
+ `SELECT * FROM users WHERE discordId = ?;`

- `SELECT * FROM users WHERE userId=?;`
+ `SELECT * FROM users WHERE userId = ?;`

Also applies to: 154-155


127-170: 🛠️ Refactor suggestion

Refactor duplicate error handling logic.

The error handling code is duplicated across the discordId and userId query handlers. Consider extracting this into a reusable function.

+ function handleQueryError(error, results, res, lang) {
+   if (error) {
+     return res.send({
+       success: false,
+       message: error,
+     });
+   }
+
+   if (!results || !results.length) {
+     return res.send({
+       success: false,
+       message: lang.api.userDoesNotExist,
+     });
+   }
+
+   return res.send({
+     success: true,
+     data: results,
+   });
+ }

  // Then use it in each query:
  db.query(
    `SELECT * FROM users WHERE discordId=?;`,
    [discordId],
-   function (error, results, fields) {
-     if (error) {
-       return res.send({
-         success: false,
-         message: error,
-       });
-     }
-
-     if (!results || !results.length) {
-       return res.send({
-         success: false,
-         message: lang.api.userDoesNotExist,
-       });
-     }
-
-     return res.send({
-       success: true,
-       data: results,
-     });
-   }
+   (error, results, fields) => handleQueryError(error, results, res, lang)
  );
📝 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.

      } else if (discordId) {
        function handleQueryError(error, results, res, lang) {
          if (error) {
            return res.send({
              success: false,
              message: error,
            });
          }

          if (!results || !results.length) {
            return res.send({
              success: false,
              message: lang.api.userDoesNotExist,
            });
          }

          return res.send({
            success: true,
            data: results,
          });
        }

        db.query(
          `SELECT * FROM users WHERE discordId=?;`,
          [discordId],
          (error, results, fields) => handleQueryError(error, results, res, lang)
        );
      } else if (userId) {
        db.query(
          `SELECT * FROM users WHERE userId=?;`,
          [userId],
          (error, results, fields) => handleQueryError(error, results, res, lang)
        );

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.

1 participant