Skip to content
Merged

js->ts #3112

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion server/src/config/controllers.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export const initializeControllers = (services) => {
});
controllers.queueController = new QueueController(services.jobQueue);
controllers.logController = new LogController(commonDependencies);
controllers.statusPageController = new StatusPageController(commonDependencies);
controllers.statusPageController = new StatusPageController(services.db);
controllers.notificationController = new NotificationController(services.notificationService, services.db);
controllers.diagnosticController = new DiagnosticController(commonDependencies, {
diagnosticService: services.diagnosticService,
Expand Down
108 changes: 0 additions & 108 deletions server/src/controllers/v1/statusPageController.js

This file was deleted.

109 changes: 109 additions & 0 deletions server/src/controllers/v1/statusPageController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import { Request, Response, NextFunction } from "express";

import { createStatusPageBodyValidation, getStatusPageParamValidation, getStatusPageQueryValidation, imageValidation } from "@/validation/joi.js";
import { AppError } from "@/utils/AppError.js";

const SERVICE_NAME = "statusPageController";

class StatusPageController {
static SERVICE_NAME = SERVICE_NAME;
private db: any;
constructor(db: any) {
this.db = db;
}
Comment on lines +10 to +13
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Type the db parameter properly instead of using any.

Using any defeats the purpose of TypeScript migration. The db object has a known structure with a statusPageModule property. Consider creating a proper interface or type for the database dependency.

🔷 Suggested type-safe approach

Create a type or interface for the database:

+interface Database {
+  statusPageModule: {
+    createStatusPage: (params: any) => Promise<any>;
+    updateStatusPage: (body: any, file: any) => Promise<any>;
+    getStatusPage: () => Promise<any>;
+    getStatusPageByUrl: (url: string) => Promise<any>;
+    getStatusPagesByTeamId: (teamId: string) => Promise<any>;
+    deleteStatusPage: (url: string) => Promise<void>;
+  };
+}
+
 class StatusPageController {
 	static SERVICE_NAME = SERVICE_NAME;
-	private db: any;
-	constructor(db: any) {
+	private db: Database;
+	constructor(db: Database) {
 		this.db = db;
 	}
📝 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
private db: any;
constructor(db: any) {
this.db = db;
}
interface Database {
statusPageModule: {
createStatusPage: (params: any) => Promise<any>;
updateStatusPage: (body: any, file: any) => Promise<any>;
getStatusPage: () => Promise<any>;
getStatusPageByUrl: (url: string) => Promise<any>;
getStatusPagesByTeamId: (teamId: string) => Promise<any>;
deleteStatusPage: (url: string) => Promise<void>;
};
}
class StatusPageController {
static SERVICE_NAME = SERVICE_NAME;
private db: Database;
constructor(db: Database) {
this.db = db;
}
🤖 Prompt for AI Agents
In @server/src/controllers/v1/statusPageController.ts around lines 10 - 13,
Replace the loose any on the controller's db property and constructor parameter
by declaring a proper interface/type that models the DB dependency (e.g., an
interface with a statusPageModule field or the existing StatusPageModule type if
available) and use that type for the private db property and constructor param
in the StatusPageController class; define the interface in a shared types file
or at top of this module and import existing module types if present so
references to statusPageModule methods are type-checked.

Comment on lines +8 to +13
Copy link

Choose a reason for hiding this comment

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

P1 | Confidence: High

The controller's constructor signature has been changed from accepting commonDependencies to a single db: any parameter. This is a breaking change to the public API of StatusPageController. The instantiation in controllers.js confirms this change (new StatusPageController(services.db)). Any existing code that instantiates this controller directly (e.g., in tests, other configs, or via dynamic imports) will now fail because it expects the old signature. Furthermore, the class no longer extends BaseController, stripping it of all inherited functionality (like this.errorService, this.stringService, and the centralized asyncHandler). This loss is evident in the new method implementations, which now handle errors and responses manually. The change violates the established architectural pattern (dependency injection via commonDependencies and inheritance from BaseController) used by all other controllers in the codebase.

Suggested change
class StatusPageController {
static SERVICE_NAME = SERVICE_NAME;
private db: any;
constructor(db: any) {
this.db = db;
}
import BaseController from "./baseController.js"; // Ensure the import path is correct
const SERVICE_NAME = "statusPageController";
class StatusPageController extends BaseController {
static SERVICE_NAME = SERVICE_NAME;
constructor(commonDependencies) {
super(commonDependencies);
}

Comment on lines +10 to +13
Copy link

Choose a reason for hiding this comment

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

P2 | Confidence: High

The db dependency is typed as any, which defeats a primary benefit of converting to TypeScript: type safety and improved developer experience (autocompletion, refactoring). The db object likely has a defined shape (e.g., containing a statusPageModule). Using any means the compiler cannot catch typos (e.g., statusPageModuel) or validate the structure of calls to this.db.statusPageModule.*. This increases the risk of runtime errors that would have been caught at compile time with proper typing.

Code Suggestion:

interface DatabaseService {
    statusPageModule: {
        createStatusPage: (params: any) => Promise<any>;
        updateStatusPage: (data: any, file: any) => Promise<any | null>;
        // ... other methods
    };
}

private db: DatabaseService;
constructor(db: DatabaseService) {
    this.db = db;
}


get serviceName() {
return StatusPageController.SERVICE_NAME;
}

createStatusPage = async (req: Request, res: Response, next: NextFunction) => {
Copy link

Choose a reason for hiding this comment

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

P2 | Confidence: High

The method directly destructures req.user ({ _id, teamId }) without any prior null/type checking. While this may be safe if an authentication middleware always populates req.user, TypeScript does not know this. The Request type from express does not have a standard user property. This creates a type safety issue; the code will compile but could fail at runtime if the middleware chain changes. It also reduces code clarity for developers unfamiliar with the project's middleware setup.

Code Suggestion:

// Option: Type assertion (if confident)
const user = req.user as { _id: string; teamId: string }; // or a proper User type
const { _id, teamId } = user;
// Or, a runtime check for safety in development
if (!req.user) {
    throw new Error('Authentication middleware failed to set req.user');
}

Evidence: method:createStatusPage, search:req.user

try {
await createStatusPageBodyValidation.validateAsync(req.body);
await imageValidation.validateAsync(req.file);

const { _id, teamId } = req.user;
const statusPage = await this.db.statusPageModule.createStatusPage({
statusPageData: req.body,
image: req.file,
userId: _id,
teamId,
});
return res.status(200).json({
msg: "Status page created successfully",
data: statusPage,
});
} catch (error) {
next(error);
}
};
Comment on lines +19 to +38
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Extend the Express Request type to include the user property.

Line 24 accesses req.user._id and req.user.teamId, but the Express Request type doesn't include a user property by default. TypeScript won't catch runtime errors if req.user is undefined.

🔷 Add type declarations for req.user

Create a type declaration file (e.g., server/src/types/express.d.ts):

import { Request } from 'express';

declare module 'express-serve-static-core' {
  interface Request {
    user?: {
      _id: string;
      teamId: string;
      // Add other user properties as needed
    };
  }
}

Then ensure this declaration is included in your tsconfig.json.

🤖 Prompt for AI Agents
In @server/src/controllers/v1/statusPageController.ts around lines 19 - 38, The
controller uses req.user in createStatusPage but Express Request lacks a user
property; add a module augmentation file (e.g., express.d.ts) that extends the
'express-serve-static-core' Request interface to include user?: { _id: string;
teamId: string; /* other props */ }, ensure the file is included by
tsconfig.json (or add it to the "types"/"include"), then recompile to let
TypeScript validate accesses to req.user in createStatusPage and other handlers.


updateStatusPage = async (req: Request, res: Response, next: NextFunction) => {
Copy link

Choose a reason for hiding this comment

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

P1 | Confidence: High

The original asyncHandler utility (from BaseController) provided a standard, centralized mechanism for error handling and logging. Its removal introduces inconsistent, manual try-catch blocks that must be replicated in every method. More critically, it has broken the structured error logging and service attribution. The old asyncHandler took SERVICE_NAME and the method name ("updateStatusPage") as parameters, which was presumably used for logging and error context. This information is now lost, making debugging and observability significantly harder. The error messages are also hardcoded (e.g., "Status page not found"), deviating from the pattern of using a string service (this.stringService) for internationalization or centralized message management.

try {
await createStatusPageBodyValidation.validateAsync(req.body);
await imageValidation.validateAsync(req.file);

const statusPage = await this.db.statusPageModule.updateStatusPage(req.body, req.file);
if (statusPage === null) {
throw new AppError({ message: "Status page not found", status: 404 });
}
return res.status(200).json({
msg: "Status page updated successfully",
data: statusPage,
});
} catch (error) {
next(error);
}
};

getStatusPage = async (req: Request, res: Response, next: NextFunction) => {
try {
const statusPage = await this.db.statusPageModule.getStatusPage();
return res.status(200).json({
msg: "Status page retrieved successfully",
data: statusPage,
});
} catch (error) {
next(error);
}
};

getStatusPageByUrl = async (req: Request, res: Response, next: NextFunction) => {
try {
await getStatusPageParamValidation.validateAsync(req.params);
await getStatusPageQueryValidation.validateAsync(req.query);

const statusPage = await this.db.statusPageModule.getStatusPageByUrl(req.params.url);
return res.status(200).json({
msg: "Status page retrieved successfully",
data: statusPage,
});
} catch (error) {
next(error);
}
};
Comment on lines +70 to +83
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find the statusPageModule implementation
fd -t f -e ts -e js | grep -i "module\|service" | grep -i "status" | head -20

Repository: bluewave-labs/Checkmate

Length of output: 165


🏁 Script executed:

#!/bin/bash
# Search for statusPageModule definition and getStatusPageByUrl implementation
rg "statusPageModule|getStatusPageByUrl" -t ts --max-count 20

Repository: bluewave-labs/Checkmate

Length of output: 975


🏁 Script executed:

#!/bin/bash
# Look at the controllers directory structure
find server/src/controllers -type f -name "*.ts" | head -10

Repository: bluewave-labs/Checkmate

Length of output: 308


🏁 Script executed:

#!/bin/bash
# Read the statusPageModule to understand getStatusPageByUrl implementation
cat server/src/db/v1/modules/statusPageModule.js

Repository: bluewave-labs/Checkmate

Length of output: 7100


🏁 Script executed:

#!/bin/bash
# Read the statusPageController to see validation definitions and full context
cat server/src/controllers/v1/statusPageController.ts

Repository: bluewave-labs/Checkmate

Length of output: 3147


🏁 Script executed:

#!/bin/bash
# Find the validation definitions
fd -t f -name "joi.js" -o -name "joi.ts"

Repository: bluewave-labs/Checkmate

Length of output: 297


🏁 Script executed:

#!/bin/bash
# Search for getStatusPageQueryValidation definition
rg "getStatusPageQueryValidation" -A 5 -B 2

Repository: bluewave-labs/Checkmate

Length of output: 2437


Query parameters validated but never used.

Line 73 validates req.query expecting required type ("uptime") and optional timeFrame parameters, but these values are never passed to this.db.statusPageModule.getStatusPageByUrl() on line 75. The method only receives req.params.url.

Either pass the query parameters to the module method or remove the unnecessary validation on line 73.

🤖 Prompt for AI Agents
In @server/src/controllers/v1/statusPageController.ts around lines 70 - 83, The
getStatusPageByUrl handler validates req.query (getStatusPageQueryValidation)
but never uses it; either pass the validated query values (e.g., type and
optional timeFrame from req.query) into
this.db.statusPageModule.getStatusPageByUrl (update the call and, if needed, the
module method signature in statusPageModule to accept { url, type, timeFrame })
or remove the getStatusPageQueryValidation.validateAsync(req.query) line and any
related query schema if the query is not needed; ensure you reference the
validated values (use the result of validateAsync or req.query) when calling
getStatusPageByUrl so validation is not wasted.

getStatusPagesByTeamId = async (req: Request, res: Response, next: NextFunction) => {
try {
const teamId = req.user.teamId;
const statusPages = await this.db.statusPageModule.getStatusPagesByTeamId(teamId);

return res.status(200).json({
msg: "Status pages retrieved successfully",
data: statusPages,
});
} catch (error) {
next(error);
}
};
deleteStatusPage = async (req: Request, res: Response, next: NextFunction) => {
try {
await this.db.statusPageModule.deleteStatusPage(req.params.url);
return res.status(200).json({
msg: "Status page deleted successfully",
});
} catch (error) {
next(error);
}
};
Comment on lines +97 to +106
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Missing parameter validation in deleteStatusPage.

Line 99 uses req.params.url without validation, while other methods like getStatusPageByUrl (line 72) validate parameters before use. This inconsistency could lead to issues if req.params.url is undefined or invalid.

➕ Add parameter validation
 deleteStatusPage = async (req: Request, res: Response, next: NextFunction) => {
 	try {
+		await getStatusPageParamValidation.validateAsync(req.params);
 		await this.db.statusPageModule.deleteStatusPage(req.params.url);
 		return res.status(200).json({
 			msg: "Status page deleted successfully",
 		});
 	} catch (error) {
 		next(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.

Suggested change
deleteStatusPage = async (req: Request, res: Response, next: NextFunction) => {
try {
await this.db.statusPageModule.deleteStatusPage(req.params.url);
return res.status(200).json({
msg: "Status page deleted successfully",
});
} catch (error) {
next(error);
}
};
deleteStatusPage = async (req: Request, res: Response, next: NextFunction) => {
try {
await getStatusPageParamValidation.validateAsync(req.params);
await this.db.statusPageModule.deleteStatusPage(req.params.url);
return res.status(200).json({
msg: "Status page deleted successfully",
});
} catch (error) {
next(error);
}
};
🤖 Prompt for AI Agents
In @server/src/controllers/v1/statusPageController.ts around lines 97 - 106,
deleteStatusPage uses req.params.url without validating it; mirror the
validation from getStatusPageByUrl by checking req.params.url before calling
this.db.statusPageModule.deleteStatusPage. If url is missing or invalid, return
a 400 response (or call next with a validation error) and avoid calling
deleteStatusPage; otherwise proceed with the deletion and the existing 200
response. Use the same validation helper or logic used by getStatusPageByUrl to
keep behavior consistent.

Comment on lines +97 to +106
Copy link

Choose a reason for hiding this comment

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

P2 | Confidence: Medium

Speculative: The deleteStatusPage method does not validate req.params.url before passing it to the database module. The old JavaScript file showed that getStatusPageByUrl used getStatusPageParamValidation.validateAsync(req.params). It's plausible that deleteStatusPage should also validate its parameter (the URL) for format, length, or safety to prevent malformed queries or potential injection vectors at the application layer. The absence of validation here is inconsistent with the pattern used in getStatusPageByUrl and could lead to unexpected database errors or behavior.

Code Suggestion:

deleteStatusPage = async (req: Request, res: Response, next: NextFunction) => {
    try {
        await getStatusPageParamValidation.validateAsync(req.params); // Re-use the same validation
        await this.db.statusPageModule.deleteStatusPage(req.params.url);
        return res.status(200).json({
            msg: "Status page deleted successfully",
        });
    } catch (error) {
        next(error);
    }
};

Evidence: method:deleteStatusPage

}

export default StatusPageController;