From c45fa16f4b3271107940b180e2f45a51b19dfd5a Mon Sep 17 00:00:00 2001 From: ksjaay Date: Wed, 25 Mar 2026 18:11:59 +0000 Subject: [PATCH] Updates regex for discord url and fixes auto not working --- openapi.json | 885 +++++++++++++++++++++ server/database/queries/heartbeat.js | 8 +- server/middleware/monitor/add.js | 3 + server/utils/status.js | 2 +- shared/validators/notifications/discord.js | 2 +- 5 files changed, 896 insertions(+), 4 deletions(-) create mode 100644 openapi.json diff --git a/openapi.json b/openapi.json new file mode 100644 index 00000000..a5d2f0b9 --- /dev/null +++ b/openapi.json @@ -0,0 +1,885 @@ +{ + "openapi": "3.1.0", + "info": { + "title": "Lunalytics API", + "version": "1.0.0" + }, + "paths": { + "/api/auth/config/update": { + "post": { + "summary": "Update authentication configuration", + "description": "Endpoint to handle updating authentication configuration", + "tags": [ + "Auth" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/auth/config": { + "get": { + "summary": "Retrieve authentication configuration", + "description": "Endpoint for retrieving authentication configuration", + "tags": [ + "Auth" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/auth/callback/twitch": { + "get": { + "summary": "Twitch OAuth callback", + "description": "Endpoint to handle Twitch OAuth provider callback", + "tags": [ + "Auth" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/auth/callback/slack": { + "get": { + "summary": "Slack OAuth callback", + "description": "Endpoint to handle Slack OAuth provider callback", + "tags": [ + "Auth" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/auth/callback/google": { + "get": { + "summary": "Google OAuth callback", + "description": "Endpoint to handle Google OAuth provider callback", + "tags": [ + "Auth" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/auth/callback/github": { + "get": { + "summary": "GitHub OAuth callback", + "description": "Endpoint to handle GitHub OAuth provider callback", + "tags": [ + "Auth" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/auth/callback/discord": { + "get": { + "summary": "Discord OAuth callback", + "description": "Endpoint to handle Discord OAuth provider callback", + "tags": [ + "Auth" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/auth/callback/custom": { + "get": { + "summary": "Custom OAuth callback", + "description": "Endpoint to handle custom OAuth provider callback", + "tags": [ + "Auth" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/auth/user/exists": { + "post": { + "summary": "User exists", + "description": "Endpoint to check if the user exists", + "tags": [ + "Auth" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/auth/register": { + "post": { + "summary": "Register user", + "description": "Endpoint to handle user registration with given information", + "tags": [ + "Auth" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/auth/setup": { + "post": { + "summary": "Setup Lunalytics", + "description": "Endpoint to handle initial Lunalytics setup", + "tags": [ + "Auth" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/auth/login": { + "post": { + "summary": "Login with credentials", + "description": "Endpoint to handle user login with credentials", + "tags": [ + "Auth" + ], + "deprecated": false, + "query": { + "def": { + "type": "object", + "shape": { + "redirect": { + "def": { + "type": "optional", + "innerType": { + "def": { + "type": "string", + "checks": [ + { + "def": { + "type": "string", + "format": "url", + "check": "string_format", + "abort": false + }, + "type": "string", + "format": "url", + "minLength": null, + "maxLength": null + } + ] + }, + "type": "string", + "format": "url", + "minLength": null, + "maxLength": null + } + }, + "type": "optional" + } + } + }, + "type": "object" + }, + "header": { + "def": { + "type": "object", + "shape": { + "user-agent": { + "def": { + "type": "optional", + "innerType": { + "def": { + "type": "string" + }, + "type": "string", + "format": null, + "minLength": null, + "maxLength": null + } + }, + "type": "optional" + } + } + }, + "type": "object" + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "email": { + "type": "string", + "format": "email", + "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$" + }, + "password": { + "type": "string", + "minLength": 8 + } + }, + "required": [ + "email", + "password" + ] + } + } + } + }, + "responses": {} + } + }, + "/api/auth/logout": { + "get": { + "summary": "Logout current user", + "description": "Endpoint to handle user logout", + "tags": [ + "Auth" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/auth/platform/{provider}": { + "get": { + "summary": "Redirect to OAuth provider", + "description": "Endpoint to handle redirection to the selected OAuth provider", + "tags": [ + "Auth" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/auth/setup/exists": { + "get": { + "summary": "Check if setup exists", + "description": "Endpoint to check if Lunalytics has already been setup", + "tags": [ + "Auth" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/user": { + "get": { + "summary": "Get User", + "description": "Fetches the current session user details. Useful for authentication and user profile display.", + "tags": [ + "User" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/user/exists": { + "post": { + "summary": "User Exists", + "description": "Checks if a user exists based on the provided email. Useful for registration, invitations, and validation.", + "tags": [ + "User" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/user/workspaces": { + "get": { + "summary": "Get Workspaces", + "description": "Fetches all workspaces associated with the current user. Useful for workspace management and navigation.", + "tags": [ + "User" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/user/connections": { + "get": { + "summary": "Get Connections", + "description": "Fetches the current user’s connection status and details. Useful for monitoring linked accounts or integrations.", + "tags": [ + "User" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/user/update/avatar": { + "post": { + "summary": "Update Avatar", + "description": "Allows users to update their profile avatar image. Useful for personalization and user identity.", + "tags": [ + "User" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/user/access/remove": { + "post": { + "summary": "Remove User", + "description": "Allows admins to remove a user from the application, revoking their access and permissions. Useful for managing team membership and security.", + "tags": [ + "User" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/user/delete/account": { + "post": { + "summary": "Delete Account", + "description": "Allows users to permanently delete their account and all associated data. Useful for privacy and user autonomy.", + "tags": [ + "User" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/user/access/decline": { + "post": { + "summary": "Decline Access", + "description": "Allows admins to decline user access requests, preventing them from joining the application. Useful for controlling who can become a member.", + "tags": [ + "User" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/user/access/approve": { + "post": { + "summary": "Approve Access", + "description": "Allows admins to approve user access requests, granting permissions to join the application. Useful for onboarding and managing team membership.", + "tags": [ + "User" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/user/update/username": { + "post": { + "summary": "Update Username", + "description": "Allows users to change their username. Useful for personalization and user identity.", + "tags": [ + "User" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/user/update/password": { + "post": { + "summary": "Update Password", + "description": "Allows users to change their account password. Useful for security and password management.", + "tags": [ + "User" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/user/update/settings": { + "post": { + "summary": "Update Settings", + "description": "Allows users to update their account preferences and settings. Useful for customizing user experience.", + "tags": [ + "User" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/user/connection/create": { + "post": { + "summary": "Create Connection", + "description": "Allows users to create a new connection to external services or accounts. Useful for integrations and expanding functionality.", + "tags": [ + "User" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/user/connection/delete": { + "post": { + "summary": "Delete Connection", + "description": "Allows users to remove a connection to external services or accounts. Useful for managing integrations and privacy.", + "tags": [ + "User" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/user/permission/update": { + "post": { + "summary": "Update Permissions", + "description": "Allows administrators to modify user permissions, granting or restricting access to features. Useful for managing roles and security.", + "tags": [ + "User" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/user/transfer/ownership": { + "post": { + "summary": "Transfer Ownership", + "description": "Allows administrators to transfer account or workspace ownership to another user. Useful for changing responsibility or leadership.", + "tags": [ + "User" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/tokens/create": { + "post": { + "summary": "Create token", + "description": "Handle creation of a new API token", + "tags": [ + "Tokens" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/tokens/delete": { + "post": { + "summary": "Delete token", + "description": "Handle deletion of an API token", + "tags": [ + "Tokens" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/tokens/update": { + "post": { + "summary": "Update token", + "description": "Handle updating of an API token", + "tags": [ + "Tokens" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/monitor/pause": { + "post": { + "summary": "Pause Monitor", + "description": "Pauses monitoring checks for a workspace. Useful for maintenance windows or troubleshooting.", + "tags": [ + "Monitor" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/monitor/delete": { + "get": { + "summary": "Delete Monitor", + "description": "Deletes a monitor from a workspace. Useful for removing unused or obsolete monitoring checks.", + "tags": [ + "Monitor" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/monitor/edit": { + "post": { + "summary": "Edit Monitor", + "description": "Edits an existing monitor. Useful for updating monitoring parameters or settings.", + "tags": [ + "Monitor" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/monitor/add": { + "post": { + "summary": "Add Monitor", + "description": "Adds a new monitor to a workspace. Useful for tracking uptime, performance, or service health.", + "tags": [ + "Monitor" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/monitor/status": { + "get": { + "summary": "Monitor Status", + "description": "Retrieves the status of a specific monitor. Useful for checking uptime, performance, or alerts.", + "tags": [ + "Monitor" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/monitor/id": { + "get": { + "summary": "Get Monitor By ID", + "description": "Retrieves a monitor by its ID. Useful for viewing details and status of a specific monitoring check.", + "tags": [ + "Monitor" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/invites/all": { + "get": { + "summary": "Get All Invites", + "description": "Retrieves all invites for a workspace. Useful for tracking pending, accepted, or declined invitations.", + "tags": [ + "Invites" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/invites/create": { + "post": { + "summary": "Create Invite", + "description": "Creates a new invite for a workspace. Useful for onboarding new members or collaborators.", + "tags": [ + "Invites" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/invites/pause": { + "post": { + "summary": "Pause Invite", + "description": "Pauses an invite for a workspace. Useful for temporarily disabling access without deleting the invitation.", + "tags": [ + "Invites" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/invites/delete": { + "post": { + "summary": "Delete Invite", + "description": "Deletes an invite from a workspace. Useful for managing access and removing unused invitations.", + "tags": [ + "Invites" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/incident/create": { + "post": { + "summary": "Create Incident", + "description": "Creates a new incident record. Useful for tracking issues, outages, or events in the system.", + "tags": [ + "Incident" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/incident/delete": { + "post": { + "summary": "Delete Incident", + "description": "Deletes an incident by ID. Useful for removing resolved or irrelevant incidents from the system.", + "tags": [ + "Incident" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/incident/update": { + "post": { + "summary": "Update Incident", + "description": "Updates an existing incident record. Useful for modifying status, details, or resolution information.", + "tags": [ + "Incident" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/incident/messages/create": { + "post": { + "summary": "Create Incident Message", + "description": "Creates a new message for an incident. Useful for communication and updates during incident resolution.", + "tags": [ + "Incident" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/incident/messages/delete": { + "post": { + "summary": "Delete Incident Message", + "description": "Deletes a message from an incident. Useful for removing outdated or incorrect communications.", + "tags": [ + "Incident" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/incident/messages/update": { + "post": { + "summary": "Update Incident Message", + "description": "Updates an existing message for an incident. Useful for correcting or clarifying communication during incident resolution.", + "tags": [ + "Incident" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/provider/delete": { + "post": { + "summary": "Delete OAuth provider", + "description": "Endpoint to handle the deletion of an OAuth provider", + "tags": [ + "Provider" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/provider/configure": { + "post": { + "summary": "Configure OAuth providers", + "description": "Endpoint to handle the configuration of OAuth providers", + "tags": [ + "Provider" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/provider": { + "get": { + "summary": "Get providers", + "description": "Endpoint to handle the retrieval of all current providers", + "tags": [ + "Provider" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/workspace/create": { + "post": { + "summary": "Create Workspace", + "description": "Creates a new workspace. Useful for organizing projects, teams, or environments.", + "tags": [ + "Workspace" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/workspace/members/@me": { + "get": { + "summary": "Get Current Member", + "description": "Fetches information about the current workspace member. Useful for user profile and permissions.", + "tags": [ + "Workspace" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/workspace/members": { + "get": { + "summary": "Get Workspace Members", + "description": "Retrieves all members of a workspace. Useful for team management and collaboration.", + "tags": [ + "Workspace" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/workspace/monitors": { + "get": { + "summary": "Get Workspace Monitors", + "description": "Retrieves all monitors for a workspace. Useful for tracking uptime, performance, or service health.", + "tags": [ + "Workspace" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/workspace/notifications": { + "get": { + "summary": "Get Workspace Notifications", + "description": "Retrieves all notifications for a workspace. Useful for alerting teams about important events or updates.", + "tags": [ + "Workspace" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/workspace/incidents": { + "get": { + "summary": "Get Workspace Incidents", + "description": "Retrieves all incidents for a workspace. Useful for monitoring issues, outages, or events.", + "tags": [ + "Workspace" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/workspace/status-pages": { + "get": { + "summary": "Get Workspace Status Pages", + "description": "Retrieves all status pages for a workspace. Useful for public communication of system health and incidents.", + "tags": [ + "Workspace" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/workspace/api-tokens": { + "get": { + "summary": "Get Workspace API Tokens", + "description": "Retrieves all API tokens for a workspace. Useful for integration, automation, and access control.", + "tags": [ + "Workspace" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/status-pages/id": { + "get": { + "summary": "Get Status Page By ID", + "description": "Retrieves a status page by its ID. Useful for viewing details and history of a specific public page.", + "tags": [ + "Status-pages" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/status-pages/create": { + "post": { + "summary": "Create Status Page", + "description": "Creates a new status page. Useful for public communication of system health, incidents, or maintenance.", + "tags": [ + "Status-pages" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/status-pages/update": { + "post": { + "summary": "Update Status Page", + "description": "Updates an existing status page. Useful for communicating new incidents, maintenance, or system changes.", + "tags": [ + "Status-pages" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/status-pages/delete": { + "post": { + "summary": "Delete Status Page", + "description": "Deletes a status page by ID. Useful for removing outdated or unnecessary public pages.", + "tags": [ + "Status-pages" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/notification/id": { + "get": { + "summary": "Get Notification By ID", + "description": "Retrieves a notification by its ID. Useful for viewing specific alert details.", + "tags": [ + "Notification" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/notification/create": { + "post": { + "summary": "Create Notification", + "description": "Creates a new notification. Useful for alerting users or teams about important events or updates.", + "tags": [ + "Notification" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/notification/edit": { + "post": { + "summary": "Edit Notification", + "description": "Edits an existing notification. Useful for updating alert content or settings.", + "tags": [ + "Notification" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/notification/delete": { + "get": { + "summary": "Delete Notification", + "description": "Deletes a notification. Useful for removing outdated or irrelevant alerts from the system.", + "tags": [ + "Notification" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/notification/toggle": { + "get": { + "summary": "Toggle Notification", + "description": "Enables or disables a notification. Useful for managing alert status and user preferences.", + "tags": [ + "Notification" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/notification/test": { + "post": { + "summary": "Test Notification", + "description": "Tests a notification to ensure it works correctly. Useful for verifying alert delivery and configuration.", + "tags": [ + "Notification" + ], + "deprecated": false, + "responses": {} + } + }, + "/api/providers": { + "get": { + "summary": "Get providers", + "description": "Endpoint to handle the retrieval of all current providers", + "tags": [ + "Provider" + ], + "deprecated": false, + "responses": {} + } + } + } +} \ No newline at end of file diff --git a/server/database/queries/heartbeat.js b/server/database/queries/heartbeat.js index 7ed100b1..a81570e1 100644 --- a/server/database/queries/heartbeat.js +++ b/server/database/queries/heartbeat.js @@ -43,13 +43,17 @@ export const isMonitorDown = async (monitorId, limit = 1) => { const limitHeartbeats = heartbeats.slice(0, newLimit); const allDown = limitHeartbeats.every((heartbeat) => heartbeat.isDown); - if (oldestHeartbeat.isDown && allDown) { + if (!oldestHeartbeat) { + return false; + } + + if (oldestHeartbeat?.isDown && allDown) { return false; } if ( limitHeartbeats.every((heartbeat) => heartbeat.isDown) && - !oldestHeartbeat.isDown + !oldestHeartbeat?.isDown ) { return limitHeartbeats[0]; } diff --git a/server/middleware/monitor/add.js b/server/middleware/monitor/add.js index af82eb45..5b1cfb70 100644 --- a/server/middleware/monitor/add.js +++ b/server/middleware/monitor/add.js @@ -7,6 +7,7 @@ import { cleanMonitor } from '../../class/monitor/index.js'; import { createMonitor } from '../../database/queries/monitor.js'; import { fetchHeartbeats } from '../../database/queries/heartbeat.js'; import { fetchCertificate } from '../../database/queries/certificate.js'; +import statusCache from '../../cache/status.js'; const stringifyJson = (obj, asArray = false) => { try { @@ -146,6 +147,8 @@ const monitorAdd = async (request, response) => { cert, }); + await statusCache.loadMonitorData(monitor.monitorId).catch(() => false); + return response.json(monitor); } catch (error) { return handleError(error, response); diff --git a/server/utils/status.js b/server/utils/status.js index f0ae4639..b9e0b91a 100644 --- a/server/utils/status.js +++ b/server/utils/status.js @@ -1,7 +1,7 @@ const layoutCheck = (layout) => { return ( (layout.type === 'metrics' && layout.autoAdd) || - (layout.type === 'uptime' && layout.graphType !== 'Basic' && layout.autoAdd) + (layout.type === 'uptime' && layout.autoAdd) ); }; diff --git a/shared/validators/notifications/discord.js b/shared/validators/notifications/discord.js index 007cf7eb..0834a308 100644 --- a/shared/validators/notifications/discord.js +++ b/shared/validators/notifications/discord.js @@ -8,7 +8,7 @@ import { NotificationValidatorError } from '../../utils/errors.js'; const friendlyNameRegex = /^[a-zA-Z0-9_-]+$/; const messageTypes = ['basic', 'pretty', 'nerdy']; const tokenRegex = - /^https:\/\/(?:discord\.com|discordapp\.com)\/api\/webhooks\/[0-9]+\/[0-9a-zA-Z_.-]+$/; + /^https:\/\/(?:discord\.com|discordapp\.com)\/api\/webhooks\/[0-9]+\/[0-9a-zA-Z_.-]+(\?thread_id=[0-9]+)?$/; const usernameRegex = /^[a-zA-Z0-9_]{1,32}$/; const Discord = ({