Compare commits

...

1144 Commits

Author SHA1 Message Date
Nawaz Dhandala
975c20a788 fix: improve error handling in verification endpoints by adding NextFunction support 2025-10-07 20:49:27 +01:00
Nawaz Dhandala
948e2d93c1 fix: handle errors in resend verification code endpoint and add NextFunction support 2025-10-07 20:38:11 +01:00
Nawaz Dhandala
6de3c93745 fix: refactor error handling for WhatsApp verification and resend code modals 2025-10-07 20:31:47 +01:00
Nawaz Dhandala
1f63110561 feat: enhance WhatsApp verification code handling with error logging and improved response management 2025-10-07 20:25:32 +01:00
Nawaz Dhandala
8a94c35450 feat: enforce template key requirement for WhatsApp messages and add debug logging for API requests 2025-10-07 16:59:53 +01:00
Nawaz Dhandala
fdc97284a5 fix: update DEFAULT_META_WHATSAPP_API_VERSION to v23.0 2025-10-07 16:48:22 +01:00
Nawaz Dhandala
792ecfdbdc fix: remove 'From' column from WhatsApp logs table 2025-10-07 16:04:24 +01:00
Nawaz Dhandala
121b01dc08 feat: add WhatsApp notifications toggle to notification settings 2025-10-07 16:01:27 +01:00
Nawaz Dhandala
1aa49071eb feat: add UserWhatsAppAPI for handling WhatsApp verification and resend functionality 2025-10-07 15:57:06 +01:00
Nawaz Dhandala
66bef1284a feat: add documentation for canceling all GitHub actions jobs using GitHub CLI 2025-10-07 15:53:37 +01:00
Nawaz Dhandala
1526f708de fix: enhance error handling and logging for WhatsApp message sending failures 2025-10-07 15:49:34 +01:00
Nawaz Dhandala
53198e4486 fix: improve error logging for WhatsApp message sending failures 2025-10-07 15:39:10 +01:00
Nawaz Dhandala
a66bf2df2a feat: enhance WhatsApp icon SVG with stroke properties and viewBox 2025-10-07 15:31:20 +01:00
Nawaz Dhandala
34422721c3 feat: add WhatsApp logs table to Notification Logs 2025-10-07 15:19:36 +01:00
Nawaz Dhandala
b5008c2363 Merge branch 'whatsapp' 2025-10-07 15:15:48 +01:00
Nawaz Dhandala
1cbab2be08 refactor: remove unused dashboard link and update WhatsApp message template variables 2025-10-07 15:15:25 +01:00
Simon Larsen
1bdcfd71f7 Merge pull request #2026 from OneUptime/whatsapp
Whatsapp
2025-10-07 15:05:58 +01:00
Nawaz Dhandala
792271b146 Merge branch 'whatsapp' of https://github.com/OneUptime/oneuptime into whatsapp 2025-10-07 15:02:44 +01:00
Nawaz Dhandala
3de5fbd35c fix: correct variable assignment for templateKey in test message route 2025-10-07 15:02:19 +01:00
Nawaz Dhandala
6f8dc1ed59 feat: update test message sending functionality to use predefined template and language 2025-10-07 15:00:41 +01:00
Nawaz Dhandala
92309d8fb2 feat: add test notification template and implement test message sending functionality in WhatsApp setup 2025-10-07 14:57:00 +01:00
Simon Larsen
d3070975cb Merge branch 'whatsapp' of github.com:OneUptime/oneuptime into whatsapp 2025-10-07 14:37:26 +01:00
Simon Larsen
69c0da5b17 refactor: remove WhatsApp high-risk cost variable and update related configurations 2025-10-07 14:37:23 +01:00
Nawaz Dhandala
1736690f01 feat: enhance WhatsApp setup markdown with detailed instructions for Business Account ID, App ID, and App Secret 2025-10-07 14:22:43 +01:00
Nawaz Dhandala
78a92905e9 feat: update WhatsApp setup markdown for clarity and improved instructions 2025-10-07 14:21:24 +01:00
Nawaz Dhandala
801c1b4ccb feat: remove header from WhatsApp setup markdown for improved clarity 2025-10-07 14:15:37 +01:00
Nawaz Dhandala
703b1dca51 feat: enhance WhatsApp setup markdown formatting for better structure and clarity 2025-10-07 14:12:55 +01:00
Nawaz Dhandala
e5ef97abd9 feat: improve WhatsApp setup markdown formatting for clarity and structure 2025-10-07 13:58:28 +01:00
Nawaz Dhandala
3053856990 feat: enhance WhatsApp setup markdown formatting for improved readability 2025-10-07 13:45:12 +01:00
Nawaz Dhandala
b8e82e2801 feat: add WhatsApp configuration fields to GlobalConfig migration and update template IDs for type safety 2025-10-07 13:36:21 +01:00
Nawaz Dhandala
2187fe63f0 feat: refactor WhatsApp template IDs and messages for improved type safety and export structure 2025-10-07 13:29:36 +01:00
Nawaz Dhandala
b1f842f9e1 feat: enhance WhatsApp setup documentation with prerequisites and structured steps 2025-10-07 13:25:52 +01:00
Nawaz Dhandala
43e03fb61c feat: update WhatsApp icon SVG path for improved rendering 2025-10-07 13:19:29 +01:00
Nawaz Dhandala
2327ab84c2 feat: add migration for WhatsApp configuration fields in GlobalConfig 2025-10-07 13:16:19 +01:00
Nawaz Dhandala
93034b6018 refactor: update WhatsApp migration and enhance type definitions in WhatsApp settings 2025-10-07 13:10:54 +01:00
Nawaz Dhandala
e45022b5cb feat: add migration for WhatsApp notifications and related database changes 2025-10-07 13:06:47 +01:00
Nawaz Dhandala
c022b70e6d Merge branch 'whatsapp' of https://github.com/OneUptime/oneuptime into whatsapp 2025-10-07 13:06:11 +01:00
Nawaz Dhandala
5615ba2df7 Merge branch 'master' into whatsapp 2025-10-07 13:05:46 +01:00
Nawaz Dhandala
268960bc5b refactor: simplify destructuring of connection settings in getTransporter method 2025-10-07 12:58:19 +01:00
Nawaz Dhandala
508a713ecf refactor: enhance connection handling in TransporterPool for improved email server configuration 2025-10-07 12:57:43 +01:00
Simon Larsen
36e50b591a refactor: update WhatsApp template messages for improved clarity and conciseness 2025-10-07 12:17:04 +01:00
Nawaz Dhandala
05c1f95ba4 fix: enable tool-cache in release workflow for improved build efficiency 2025-10-07 11:52:13 +01:00
Simon Larsen
9d355691ae refactor: rename scheduled_maintenance_number to scheduled_event_number in WhatsApp templates and notification jobs for consistency 2025-10-07 11:32:54 +01:00
Simon Larsen
73c58186b6 refactor: enhance WhatsApp template messages with scheduled maintenance number for better context 2025-10-06 20:23:11 +01:00
Simon Larsen
eb8d3e4dfd refactor: update WhatsApp template messages for clarity and conciseness 2025-10-06 20:17:38 +01:00
Nawaz Dhandala
987f30e5c7 feat: add PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD environment variable to Dockerfiles for improved build performance 2025-10-06 19:45:46 +01:00
Simon Larsen
b7a0dbf81b refactor: replace action_link with corresponding link variables in notification templates 2025-10-06 18:25:59 +01:00
Simon Larsen
7368a0eb7c Refactor notification links in WhatsApp templates and services
- Updated variable names for links in various services to improve consistency:
  - Changed `*_link_on_dashboard` to `*_link` across multiple services including MonitorService, OnCallDutyPolicyEscalationRuleService, and UserNotificationRuleService.

- Modified WhatsApp template messages to reflect the new link variable names, ensuring that the notifications sent to users contain the correct links.

- Adjusted the link variable names in the WhatsAppTemplateUtil to match the updated naming convention.

- Ensured all related notification jobs (e.g., SendCreatedResourceNotification, SendNotePostedNotification) are updated to use the new link variables for better clarity and maintainability.
2025-10-06 18:07:39 +01:00
Nawaz Dhandala
ef0b6f3e14 refactor: replace docker login actions with retry mechanism for improved reliability 2025-10-06 16:36:28 +01:00
Simon Larsen
af8691f61d refactor: remove Meta WhatsApp API version handling and update documentation 2025-10-06 16:31:29 +01:00
Simon Larsen
f7aee2e253 feat: add Meta WhatsApp settings page and configuration options 2025-10-06 16:26:05 +01:00
Nawaz Dhandala
0a1b74d911 fix: update @simplewebauthn/server to version 13.2.2 in package.json and package-lock.json 2025-10-06 16:13:53 +01:00
Nawaz Dhandala
ab48da447d refactor: add test jobs to push-release-tags workflow for enhanced testing coverage 2025-10-06 16:08:43 +01:00
Nawaz Dhandala
1cc0630939 feat: add APP_TAG pinning to versioned release in config.env 2025-10-06 16:06:16 +01:00
Simon Larsen
5391ff4688 Add dashboard links to WhatsApp notifications for various resources
- Added `monitor_link_on_dashboard` to MonitorService notifications.
- Added `on_call_policy_link_on_dashboard` to OnCallDutyPolicyEscalationRule services.
- Added `on_call_schedule_link_on_dashboard` to OnCallDutyPolicyScheduleService.
- Added `probe_link_on_dashboard` to ProbeService notifications.
- Enhanced UserNotificationRuleService to include `alert_link_on_dashboard` and `incident_link_on_dashboard`.
- Updated WhatsApp templates to include links to the OneUptime dashboard for alerts, incidents, monitors, probes, scheduled maintenance, and status pages.
- Added `status_page_link_on_dashboard` to StatusPageOwners notifications.
2025-10-06 15:56:12 +01:00
Nawaz Dhandala
f6d96676fe refactor: update dependencies for test-e2e-release-saas job to include publish-mcp-server 2025-10-06 15:54:50 +01:00
Simon Larsen
cf02842ab1 Refactor WhatsApp message handling across services
- Updated `createWhatsAppMessageFromTemplate` to return a typed `WhatsAppMessagePayload`.
- Added type annotations for `WhatsAppTemplateId` in various services to improve type safety.
- Refactored WhatsApp message creation in `ProbeService`, `UserNotificationRuleService`, `UserNotificationSettingService`, and other worker jobs to ensure consistent typing.
- Enhanced readability and maintainability by using explicit types for event types and WhatsApp message payloads.
- Improved the structure of WhatsApp template ID definitions for better clarity and type inference.
2025-10-06 15:12:29 +01:00
Nawaz Dhandala
b63fcf6b99 refactor: update push-release-tags job dependencies for improved workflow order 2025-10-06 14:54:08 +01:00
Nawaz Dhandala
36069c1b4e refactor: update job dependencies for push-release-tags and github-release in release workflow 2025-10-06 14:48:05 +01:00
Simon Larsen
d2846decce refactor: improve code formatting and readability across multiple files 2025-10-06 14:47:15 +01:00
Simon Larsen
60a8a3f052 feat: include alert and incident identifiers in SMS notifications for better context 2025-10-06 14:45:46 +01:00
Simon Larsen
e2d15dc2e7 Merge branch 'master' of github.com:OneUptime/oneuptime into whatsapp 2025-10-06 14:39:00 +01:00
Simon Larsen
7cdefdeccd feat: add incident and alert numbers to WhatsApp notification templates and related services 2025-10-06 14:37:45 +01:00
Simon Larsen
684b8822af feat: simplify WhatsApp template messages for clarity and user engagement 2025-10-06 14:20:52 +01:00
Nawaz Dhandala
231bc47942 refactor: improve type annotations and formatting in TeamService for clarity 2025-10-06 14:14:11 +01:00
Nawaz Dhandala
965a497be3 feat: implement SCIM mutation checks for team creation and deletion 2025-10-06 14:12:19 +01:00
Nawaz Dhandala
f50a7fb99b refactor: adjust job dependencies and increase timeout for E2E tests in release workflows 2025-10-06 14:08:50 +01:00
Simon Larsen
50a5e75d1a feat: enhance WhatsApp template messages with additional information for user guidance 2025-10-06 14:00:01 +01:00
Simon Larsen
84e838a055 refactor: streamline WhatsApp message creation by removing unused template string imports 2025-10-04 13:49:12 +01:00
Simon Larsen
6d6c78e974 feat: Integrate WhatsApp notifications for various alert and incident events
- Added WhatsApp message creation for alert owner notifications in SendCreatedResourceNotification, SendNotePostedNotification, SendOwnerAddedNotification, and SendStateChangeNotification jobs.
- Implemented WhatsApp message functionality for incident owner notifications in SendCreatedResourceNotification, SendNotePostedNotification, SendOwnerAddedNotification, and SendStateChangeNotification jobs.
- Enhanced monitor owner notifications with WhatsApp messages in SendCreatedResourceNotification, SendOwnerAddedNotification, and SendStatusChangeNotification jobs.
- Included WhatsApp message creation for scheduled maintenance owner notifications in SendCreatedResourceNotification, SendNotePostedNotification, SendOwnerAddedNotification, and SendStateChangeNotification jobs.
- Added WhatsApp message functionality for status page owner notifications in SendAnnouncementCreatedNotification, SendCreatedResourceNotification, and SendOwnerAddedNotification jobs.
- Introduced WhatsAppTemplateUtil to manage WhatsApp message templates and creation logic.
2025-10-04 13:22:56 +01:00
Simon Larsen
778d5b7c6b feat: add UserWhatsAppService and integrate WhatsApp verification code template 2025-10-03 19:25:53 +01:00
Nawaz Dhandala
8051146f41 refactor: enhance type safety and improve variable naming in OnCallDutyScheduleSettings 2025-10-03 19:19:46 +01:00
Simon Larsen
86a359a230 feat: enhance WhatsApp notification handling with template support and error logging 2025-10-03 19:17:10 +01:00
Simon Larsen
c16dac65cc feat: add WhatsApp template IDs and rendering logic for notifications 2025-10-03 19:16:04 +01:00
Simon Larsen
437c9ecdbc feat: add support for WhatsApp notifications in project and user notification settings 2025-10-03 19:04:33 +01:00
Nawaz Dhandala
bf4eec2bdf refactor: improve code formatting and comments for better readability in SCIM and TimePicker components 2025-10-03 19:00:49 +01:00
Nawaz Dhandala
08367f3c7f refactor: update onDuplicateSuccess type to support Promise and implement duplication logic in OnCallDutyScheduleSettings 2025-10-03 18:23:29 +01:00
Simon Larsen
f5d077956a feat: integrate UserWhatsApp into notification settings and management 2025-10-03 18:12:07 +01:00
Simon Larsen
ca74005262 feat: add WhatsApp phone display in NotificationMethodView and create WhatsApp management component 2025-10-03 18:11:10 +01:00
Simon Larsen
52c936935e feat: add UserWhatsApp relation and ID to UserOnCallLogTimeline model 2025-10-03 17:51:34 +01:00
Simon Larsen
2951600ed9 feat: add UserWhatsApp relation and ID to UserNotificationRule model 2025-10-03 17:51:15 +01:00
Simon Larsen
d12c8c778c feat: Add UserWhatsApp and WhatsAppLog models with associated services
- Created UserWhatsApp model to manage WhatsApp numbers linked to users and projects.
- Implemented WhatsAppLog model to log messages sent via WhatsApp, including details like recipient, status, and associated incidents or alerts.
- Developed WhatsAppLogService for managing WhatsApp log entries, including automatic deletion of old logs if billing is enabled.
- Introduced WhatsAppService for sending WhatsApp messages with various contextual options.
2025-10-03 17:28:31 +01:00
Simon Larsen
77d4527a00 feat: add WhatsApp integration with API and configuration support 2025-10-03 17:24:59 +01:00
Nawaz Dhandala
1ef3353155 fix: update modelId retrieval to correctly use parameter for OnCallDutyScheduleSettings 2025-10-03 17:11:04 +01:00
Nawaz Dhandala
2c635c0d1e refactor: add modulePathIgnorePatterns to jest config for build directory 2025-10-03 16:17:45 +01:00
Nawaz Dhandala
44799f4625 Merge branch 'master' of https://github.com/OneUptime/oneuptime into timepkr 2025-10-03 15:39:38 +01:00
Nawaz Dhandala
4bc6e625d2 refactor: adjust spacing for timezone label display in TimePicker component 2025-10-03 15:38:49 +01:00
Nawaz Dhandala
9ff773dd81 refactor: update timezone label display in TimePicker component for improved clarity 2025-10-03 15:37:09 +01:00
Nawaz Dhandala
bf2c2afbb8 refactor: simplify browser locale detection for 12-hour format and enhance timezone label display 2025-10-03 15:01:26 +01:00
Simon Larsen
3efa3b374f feat: add push-release-tags job to create Docker release tags after GitHub release 2025-10-03 14:59:29 +01:00
Nawaz Dhandala
f76b004e2a refactor: implement browser locale detection for 12-hour time format preference 2025-10-03 14:55:47 +01:00
Nawaz Dhandala
cb535bd114 refactor: add type annotations for improved type safety in TimePicker component 2025-10-03 14:45:56 +01:00
Simon Larsen
9b09082d13 feat: add settings page for on-call duty schedule with duplication functionality 2025-10-03 14:37:20 +01:00
Nawaz Dhandala
5ac6e7ba7b refactor: improve code readability in Input and TimePicker components 2025-10-03 14:32:51 +01:00
Nawaz Dhandala
044183dbd7 refactor: enhance TimePicker input size and icon styling for improved usability 2025-10-03 14:27:23 +01:00
Nawaz Dhandala
fb13609c37 refactor: add margin to TimePicker component for improved layout 2025-10-03 14:23:36 +01:00
Nawaz Dhandala
1c602b1ad3 refactor: adjust TimePicker input size and improve clickability indication 2025-10-03 14:18:52 +01:00
Nawaz Dhandala
1affb76085 refactor: update TimePicker component layout and styling for improved usability 2025-10-03 14:16:40 +01:00
Nawaz Dhandala
0e02083dec refactor: implement modal for time selection in TimePicker component 2025-10-03 14:13:51 +01:00
Nawaz Dhandala
a442b02337 refactor: update TimePicker import path to use the correct index file 2025-10-03 14:07:33 +01:00
Simon Larsen
3ab47328f6 refactor: enhance SCIM group schema compatibility by including SCIM 1.1 core schema 2025-10-03 14:01:42 +01:00
Nawaz Dhandala
57ff8c007b refactor: integrate TimePicker component for time selection in FormField and adjust Input spellCheck logic 2025-10-03 13:59:30 +01:00
Nawaz Dhandala
c83d1babcd refactor: update jest configuration to support TypeScript and improve coverage collection 2025-10-03 13:54:50 +01:00
Simon Larsen
1bc96d04fa refactor: enhance artifact naming in release workflows to include job and run attempt 2025-10-03 13:51:52 +01:00
Nawaz Dhandala
e03a9b52ae refactor: remove InputType.TIME usage and implement TimePicker component for time selection 2025-10-03 13:43:53 +01:00
Simon Larsen
a307a68ec1 refactor: implement retry logic for E2E tests in release workflows 2025-10-03 13:36:19 +01:00
Nawaz Dhandala
1032f01278 refactor: enhance query execution logic to handle query parameters more effectively 2025-10-02 19:43:52 +01:00
Nawaz Dhandala
83286aa34d refactor: remove maxmemory-policy from redis config and set to noeviction in values.yaml 2025-10-02 19:31:53 +01:00
Nawaz Dhandala
543757a2b5 refactor: update helm template command to specify chart directory for improved clarity 2025-10-02 16:46:04 +01:00
Nawaz Dhandala
d0bbb12f92 refactor: improve formatting and readability of Microsoft Teams installation instructions 2025-10-02 16:44:40 +01:00
Nawaz Dhandala
066e785a53 refactor: add Microsoft Teams navigation links and integration documentation 2025-10-02 15:15:38 +01:00
Nawaz Dhandala
2f46eadcd4 refactor: add call scheduling feature with inline calendar integration to support page 2025-10-02 15:00:32 +01:00
Nawaz Dhandala
c472555f55 refactor: update installation instructions to include app approval status for Microsoft Teams integration 2025-10-02 14:56:30 +01:00
Nawaz Dhandala
83c9255b98 refactor: enhance Microsoft Teams integration with billing conditionals and installation instructions 2025-10-02 14:47:54 +01:00
Nawaz Dhandala
1748a198c7 refactor: update Microsoft Teams permissions documentation for clarity and conciseness 2025-10-02 14:42:35 +01:00
Nawaz Dhandala
a4841f4b6e refactor: remove ts-ignore and update axios mock type for improved type safety 2025-10-02 13:15:57 +01:00
Nawaz Dhandala
1c750d274e refactor: replace ts-ignore with ts-expect-error for improved type safety in Handlebars helpers and test mocks 2025-10-02 12:18:10 +01:00
Nawaz Dhandala
2dda68c462 refactor: update comment styles for consistency across ProjectAPI, DataPoint, and ComponentCode 2025-10-02 11:57:39 +01:00
Nawaz Dhandala
6d5bc111ba Refactor comments across multiple files to improve clarity and consistency
- Updated comments in Probe/Config.ts to use block comments for proxy configuration.
- Refactored comments in PortMonitor.ts, SyntheticMonitor.ts, and OnlineCheck.ts to block comments for better readability.
- Adjusted comments in ProbeIngest/API/Monitor.ts and ProbeIngest/API/Probe.ts to block comments for clarity.
- Standardized comments in various data migration scripts to block comments for consistency.
- Modified eslint.config.js to enforce multiline comment style as an error.
2025-10-02 11:53:55 +01:00
Nawaz Dhandala
0e1c8df7ab refactor: simplify logging and formatting in Microsoft Teams actions 2025-10-02 11:40:47 +01:00
Nawaz Dhandala
4b55cb3d84 feat: refine incident action handling to improve clarity and flow 2025-10-02 11:36:45 +01:00
Nawaz Dhandala
eb66b57939 feat: prevent duplicate message processing for new incident and scheduled maintenance submissions 2025-10-02 11:33:53 +01:00
Nawaz Dhandala
b53119d249 feat: implement separate handling for new scheduled maintenance creation in Microsoft Teams 2025-10-02 11:32:24 +01:00
Nawaz Dhandala
95c2db5ace feat: streamline incident and scheduled maintenance confirmation by sending messages after hiding form cards 2025-10-02 11:23:41 +01:00
Nawaz Dhandala
8c4bc48c44 feat: add incident and scheduled maintenance links in response messages 2025-10-02 11:18:56 +01:00
Nawaz Dhandala
28f3b98d44 feat: implement new incident and scheduled maintenance submission handling in Microsoft Teams 2025-10-02 11:13:31 +01:00
Nawaz Dhandala
742da27d87 feat: add commands for creating new incidents and scheduled maintenance in Microsoft Teams 2025-10-02 10:55:28 +01:00
Nawaz Dhandala
e2ba7dff8d refactor: update KEDA autoscaling configuration comments for clarity 2025-10-02 09:25:47 +01:00
Simon Larsen
3f6a58a087 Merge pull request #2024 from calvinbui/master
helm: allow removing the auto-generated date label
2025-10-02 09:25:07 +01:00
Calvin Bui
3f2005bd34 fix jinja 2025-10-02 11:38:16 +10:00
Calvin Bui
651ba4247e fix: remove auto-generated date label causing constant updates 2025-10-02 11:36:07 +10:00
Nawaz Dhandala
ba71cf5980 fix: Remove system status command from Microsoft Teams bot responses 2025-10-01 20:57:55 +01:00
Nawaz Dhandala
442a4b935b feat: Enhance incident and maintenance messages with severity icons and dashboard links 2025-10-01 20:52:39 +01:00
Nawaz Dhandala
5caf5e8991 fix: Update command list formatting in bot help message for consistency 2025-10-01 20:50:00 +01:00
Nawaz Dhandala
b5e4545193 fix: Simplify conditional check for TOTP and WebAuthn lists in login function 2025-10-01 20:38:01 +01:00
Nawaz Dhandala
ce4cc70815 refactor: Improve type annotations for error handling and channel name mapping in WorkspaceNotificationRuleService 2025-10-01 20:32:19 +01:00
Nawaz Dhandala
426dda60fe refactor: Enhance two-factor authentication verification logic and improve placeholder formatting in NotificationRuleForm 2025-10-01 20:28:18 +01:00
Nawaz Dhandala
c6be5a9ebf fix: Update placeholder text for existing channel input based on workspace type in NotificationRuleForm 2025-10-01 20:21:33 +01:00
Nawaz Dhandala
eb19b8926b docs: Update setup instructions for Microsoft Teams integration 2025-10-01 18:29:21 +01:00
Nawaz Dhandala
955a3f784d refactor: Remove duplicate code for handling Adaptive Card submits in MicrosoftTeamsUtil 2025-10-01 18:25:11 +01:00
Nawaz Dhandala
756dc88289 refactor: Simplify error message mapping and improve readability in WorkspaceNotificationRuleService 2025-10-01 18:12:08 +01:00
Nawaz Dhandala
c116f9adc1 refactor: Update existing channel handling to use WorkspaceChannel objects and include teamId for Microsoft Teams integration 2025-10-01 18:07:36 +01:00
Nawaz Dhandala
324f29edc3 fix: Improve error messages for missing teamId in Microsoft Teams channel resolution and message sending 2025-10-01 17:56:38 +01:00
Nawaz Dhandala
03064308ef feat: Add teamId to WorkspaceChannel interface and update MicrosoftTeamsUtil to include teamId in channel creation 2025-10-01 14:11:59 +01:00
Nawaz Dhandala
9dd4ac6f6b feat: Add error handling for message sending in Microsoft Teams and Slack integrations 2025-10-01 14:08:29 +01:00
Nawaz Dhandala
8b8f1ba530 fix: Ensure teamId is required for resolving channel names and sending messages in Microsoft Teams 2025-10-01 14:02:47 +01:00
Nawaz Dhandala
31e77d2208 feat: Add optional teamId to WorkspaceMessagePayload for Microsoft Teams integration 2025-10-01 13:53:38 +01:00
Nawaz Dhandala
c6e78a3264 fix: Update Buffer handling to use Uint8Array and ArrayBuffer for improved type safety 2025-10-01 12:51:16 +01:00
Nawaz Dhandala
c474d3b1e5 chore: update eslint-plugin-react to version 7.37.5 2025-09-30 20:30:54 +01:00
Nawaz Dhandala
3afbbfae5a fix: Update schema to allow null values for various string and object properties 2025-09-30 20:12:33 +01:00
Nawaz Dhandala
92c3c619ee refactor: Clean up code formatting and improve readability in Microsoft Teams components 2025-09-30 19:58:52 +01:00
Nawaz Dhandala
b65e28684e fix: Improve KEDA autoscaler condition checks for probes and enhance values.yaml structure 2025-09-30 19:53:51 +01:00
Nawaz Dhandala
dabf1464f2 fix: Update nodeSelector handling in multiple deployment templates for improved clarity and consistency 2025-09-30 19:33:51 +01:00
Nawaz Dhandala
9f618acc31 fix: Refactor security context handling for pods and containers to improve clarity and maintainability 2025-09-30 19:27:32 +01:00
Nawaz Dhandala
3dd4caeae9 feat: Update security context handling for various deployments 2025-09-30 19:23:18 +01:00
Nawaz Dhandala
1345693175 feat: Add pod and container security context support for PostgreSQL, ClickHouse, and Redis 2025-09-30 18:21:54 +01:00
Nawaz Dhandala
b5a422e8aa fix: Change oneuptimeSecret type from object to string and adjust encryptionSecret definition 2025-09-30 18:16:56 +01:00
Nawaz Dhandala
122773b0ba fix: Update comment for oneuptimeSecret to clarify string requirement 2025-09-30 18:16:16 +01:00
Nawaz Dhandala
cfc1d5d820 Merge branch 'master' of https://github.com/OneUptime/oneuptime 2025-09-30 18:12:14 +01:00
Nawaz Dhandala
18d1df86d5 feat: Add pod and container security context support across deployments
- Updated deployment templates to merge podSecurityContext and containerSecurityContext from values.
- Enhanced the values schema and default values to include podSecurityContext and containerSecurityContext for various services.
- Ensured that security contexts are applied consistently across all relevant templates including accounts, admin-dashboard, api-reference, app, dashboard, docs, fluent-ingest, home, incoming-request-ingest, isolated-vm, nginx, open-telemetry-ingest, otel-collector, probe-ingest, server-monitor-ingest, status-page, test-server, worker, and workflow.
2025-09-30 18:12:11 +01:00
Nawaz Dhandala
4b65366080 feat: Merge nodeSelector values for various deployments in Helm templates 2025-09-30 17:52:19 +01:00
Nawaz Dhandala
74bb6acd62 feat: Add resources and nodeSelector fields to various components in values.yaml and values.schema.json 2025-09-30 17:47:06 +01:00
Simon Larsen
a47b0fe21d Merge pull request #2023 from calvinbui/master
helm: add deployment update strategy
2025-09-30 17:43:30 +01:00
Nawaz Dhandala
349e64e3a2 Merge branch 'yubikey-auth' 2025-09-30 17:14:39 +01:00
Nawaz Dhandala
0f64b49f9c refactor: Clean up code formatting and improve readability in various files 2025-09-30 17:14:23 +01:00
Nawaz Dhandala
1f8d4967ee refactor: Improve logging for email verification process 2025-09-30 17:07:44 +01:00
Nawaz Dhandala
e8b580ab4c refactor: Ensure TOTP and WebAuthn lists are always returned as arrays in fetchTotpAuthList 2025-09-30 17:04:32 +01:00
Nawaz Dhandala
866183d95a refactor: Rename two-factor authentication API endpoints to use TOTP terminology 2025-09-30 17:00:24 +01:00
Nawaz Dhandala
ca64843de0 refactor: Add validation for email and password in login function 2025-09-30 16:50:18 +01:00
Calvin Bui
d7427b9abe update values.schema.json 2025-09-30 23:43:05 +10:00
Calvin Bui
77537999b0 add deployment update strategy 2025-09-30 23:24:17 +10:00
Nawaz Dhandala
263c341792 refactor: Update login function to handle multiple two-factor authentication methods 2025-09-30 13:47:00 +01:00
Nawaz Dhandala
45b1dcbb7e refactor: Update WebAuthn verification API to use initial values and remove unused endpoint 2025-09-30 13:38:20 +01:00
Nawaz Dhandala
43e344dfb1 refactor: Update login function to check for both TOTP and WebAuthn verification 2025-09-30 13:34:20 +01:00
Nawaz Dhandala
e5b2e70ce2 refactor: Rename verifyTwoFactorAuth to verifyTotpAuth for consistency 2025-09-30 13:31:38 +01:00
Nawaz Dhandala
602383a720 refactor: Rename UserTwoFactorAuth table to UserTotpAuth in migration 2025-09-30 13:18:28 +01:00
Nawaz Dhandala
c10e691bf5 refactor: Remove UserTwoFactorAuth model and service in favor of TotpAuth implementation 2025-09-30 13:15:09 +01:00
Nawaz Dhandala
ab7c8c1df9 feat: Add UserWebAuthnAPI routes for authentication verification and update API paths 2025-09-30 13:13:45 +01:00
Nawaz Dhandala
72236b025f refactor: replace TwoFactorAuth with TotpAuth and update related implementations 2025-09-30 13:08:58 +01:00
Nawaz Dhandala
1c5c9ee3ad refactor: update UserWebAuthnAPI and UserWebAuthnService for improved type definitions and clarity 2025-09-30 13:06:46 +01:00
Nawaz Dhandala
352c923e74 refactor: rename UserTwoFactorAuth to UserTotpAuth and update related services, APIs, and models
- Replaced all instances of UserTwoFactorAuth with UserTotpAuth across the codebase.
- Updated API endpoints to reflect the new naming convention.
- Modified database models and services to accommodate the changes.
- Adjusted frontend components to utilize the new UserTotpAuth model.
- Ensured all references, imports, and exports are consistent with the new naming.
2025-09-30 13:02:32 +01:00
Nawaz Dhandala
d3ebbf1c88 feat: Add migration to remove two-factor authentication columns from User table 2025-09-30 12:51:09 +01:00
Simon Larsen
411a455d54 Merge branch 'yubikey-auth' of github.com:OneUptime/oneuptime into yubikey-auth 2025-09-30 12:47:44 +01:00
Nawaz Dhandala
4570d617b2 feat: Implement WebAuthn authentication verification and update login function to support multiple verification methods 2025-09-30 12:46:19 +01:00
Nawaz Dhandala
305094a8b2 feat: Refactor UserWebAuthnAPI and UserWebAuthnService to utilize service methods for registration and authentication options generation 2025-09-30 12:43:13 +01:00
Simon Larsen
b02d1da202 refactor: Simplify master admin status assignment in LoginUtil 2025-09-30 12:29:43 +01:00
Simon Larsen
1407a0fce9 feat: Replace loading spinner and error message with reusable components in LoginPage 2025-09-30 11:58:31 +01:00
Simon Larsen
836fb91cbe refactor: Update terminology to "Security Key-Based Two-Factor Authentication" for clarity and consistency 2025-09-30 11:55:07 +01:00
Simon Larsen
4c819ca906 feat: Move "Add Security Key" button to the right side of the security keys table for improved UI layout 2025-09-30 11:52:57 +01:00
Simon Larsen
3bd7fb7c59 refactor: Update terminology to "Authenticator Based Two Factor Authentication" for clarity and consistency 2025-09-30 11:51:38 +01:00
Simon Larsen
99e7193961 feat: Integrate Base64 encoding for WebAuthn challenge and credential handling in UserWebAuthnAPI and LoginPage 2025-09-30 11:49:23 +01:00
Simon Larsen
db10bba4d3 feat: Implement Base64 utility functions and integrate them into WebAuthn registration process 2025-09-30 11:47:48 +01:00
Simon Larsen
31a425d34d fix: Ensure security keys are always verified by default in UserWebAuthnService 2025-09-30 11:31:54 +01:00
Simon Larsen
7870406295 feat: Integrate WebAuthn verification in two-factor authentication checks 2025-09-30 11:31:40 +01:00
Simon Larsen
77de0a1116 feat: Upgrade Node.js base image to version 24.9-alpine3.21 in multiple Dockerfiles and remove debugging flag from nodemon configurations 2025-09-30 11:27:03 +01:00
Simon Larsen
f2da31a6f9 feat: Update package-lock.json to add new dependencies for testing and TypeScript 2025-09-29 21:40:34 +01:00
Simon Larsen
d36577069f feat: Add UserWebAuthn migration for WebAuthn credential management 2025-09-29 20:54:24 +01:00
Simon Larsen
4cdf959a01 fix: Update channel name comparison logic to use displayName for accurate matching 2025-09-29 20:21:12 +01:00
Simon Larsen
62adb3fd76 fix: Ensure team selection is required for Microsoft Teams integration in notification rules 2025-09-29 20:13:56 +01:00
Simon Larsen
5bc6e67c21 refactor: Enhance button styles and update team item display in TeamsAvailableModal 2025-09-29 20:07:13 +01:00
Simon Larsen
d7e86a56e6 refactor: Remove search functionality and simplify team list display in TeamsAvailableModal 2025-09-29 19:56:19 +01:00
Simon Larsen
c2b569c13a feat: Implement TeamsAvailableModal for improved team selection in Microsoft Teams integration 2025-09-29 19:41:06 +01:00
Simon Larsen
6d6555396a fix: Correct API URL handling in Microsoft Teams integration 2025-09-29 19:19:42 +01:00
Simon Larsen
13018c8169 refactor: Update API URL handling for Microsoft Teams integration 2025-09-29 19:14:53 +01:00
Simon Larsen
82f462785b feat: Add modal to display and refresh Microsoft Teams list in integration 2025-09-29 19:08:44 +01:00
Simon Larsen
9f21725949 refactor: Simplify teams retrieval by removing redundant checks and updating refresh logic 2025-09-29 17:37:19 +01:00
Nawaz Dhandala
82bee44933 Refactor Microsoft Teams integration code for improved type safety and clarity
- Added type annotations for various variables and constants in Incident, Monitor, ScheduledMaintenance, and MicrosoftTeams actions.
- Enhanced type definitions for JSON-related imports and improved handling of JSON values.
- Updated methods to ensure consistent use of types across the Microsoft Teams integration.
- Refactored message formatting in incident and monitor details to explicitly define message types.
- Improved readability and maintainability of the code by restructuring some conditional statements and variable declarations.
- Ensured proper handling of project authentication and access tokens with clearer logging and error handling.
2025-09-29 17:22:05 +01:00
Nawaz Dhandala
b67c702d4d Refactor Microsoft Teams integration components and improve code readability
- Removed unnecessary blank lines in Workspace.ts and MicrosoftTeamsIntegration.tsx.
- Reformatted function parameters for better readability in WorkspaceType.ts.
- Simplified imports in MicrosoftTeamsIntegration.tsx and NotificationRuleForm.tsx.
- Enhanced conditional checks for better clarity in NotificationRuleForm.tsx and NotificationRuleViewElement.tsx.
- Improved formatting and consistency in MicrosoftTeamsIntegrationDocumentation.tsx.
- Updated state management and conditional rendering in WorkspaceNotificationRulesTable.tsx.
- Ensured consistent use of line breaks and indentation across various components for improved maintainability.
2025-09-29 16:59:33 +01:00
Simon Larsen
e5017908ae Merge pull request #2006 from OneUptime/v4-ms-teams
feat: Add Microsoft Teams integration with configuration and action t…
2025-09-29 16:58:25 +01:00
Simon Larsen
eff87e1705 feat: Implement method to retrieve user's joined teams in Microsoft Teams integration 2025-09-29 16:50:27 +01:00
Simon Larsen
9fabdbbb2b feat: Add method to refresh teams list for a user in Microsoft Teams integration 2025-09-29 16:43:48 +01:00
Simon Larsen
56908674bb feat: Add endpoint to refresh teams list in Microsoft Teams API 2025-09-29 16:43:02 +01:00
Simon Larsen
9fe3fb041e feat: Add refresh teams functionality and loading state in Microsoft Teams integration 2025-09-29 16:35:19 +01:00
Simon Larsen
46d3bf527b feat: Update team selection titles and labels in NotificationRuleForm for clarity 2025-09-29 16:28:30 +01:00
Simon Larsen
06e11aa8d7 feat: Update card title for admin consent status in Microsoft Teams integration 2025-09-29 16:23:58 +01:00
Simon Larsen
acd9a87694 feat: Update card title for manual app installation prompt in Microsoft Teams 2025-09-29 16:21:42 +01:00
Simon Larsen
9a4f799145 feat: Remove channelCache from MicrosoftTeamsMiscData interface 2025-09-29 15:29:45 +01:00
Simon Larsen
edd067f79f feat: Add tenantId handling in Microsoft Teams request and auth token retrieval 2025-09-29 14:45:20 +01:00
Simon Larsen
36b6b8423a feat: Simplify Microsoft Teams data loading by removing unnecessary try-catch block 2025-09-29 14:41:01 +01:00
Simon Larsen
81c558dc21 feat: Refactor Microsoft Teams integration to use MicrosoftTeamsTeam type for improved type safety 2025-09-29 14:38:18 +01:00
Simon Larsen
a23d585ec6 feat: Update teamId handling in Microsoft Teams channel existence check 2025-09-29 14:32:27 +01:00
Simon Larsen
c193bd71d7 feat: Replace SlackMiscData with WorkspaceMiscData in refreshAuthToken method 2025-09-29 14:27:36 +01:00
Simon Larsen
b1791602c8 feat: Define WorkspaceMiscData type to unify Slack and Microsoft Teams miscellaneous data structures 2025-09-29 14:26:40 +01:00
Simon Larsen
e98059e4ee feat: Add optional teamId parameter to workspace channel methods for improved Microsoft Teams integration 2025-09-29 14:26:32 +01:00
Simon Larsen
371ba8f414 feat: Enhance Microsoft Teams integration by adding teamId support and fetching available teams 2025-09-29 14:20:19 +01:00
Simon Larsen
a271ba2cd9 feat: Add MicrosoftTeamsTeam interface and availableTeams property to MicrosoftTeamsMiscData 2025-09-29 14:10:26 +01:00
Simon Larsen
a337065c4f feat: Remove unused imports from MicrosoftTeamsIntegration component 2025-09-29 13:42:47 +01:00
Simon Larsen
22479a6f9e feat: Add teamId support for notification channels and update related interfaces 2025-09-29 13:41:56 +01:00
Simon Larsen
29051c3010 feat: Simplify Microsoft Teams integration by removing team selection logic and updating connection messages 2025-09-29 13:40:36 +01:00
Simon Larsen
aaa29c87ca feat: Rename microsoftTeams prop to microsoftTeamsTeams for clarity 2025-09-29 13:38:46 +01:00
Simon Larsen
0385dc7ac8 feat: Add Microsoft Teams team selection for existing and new channel creation in notification rules 2025-09-29 13:38:31 +01:00
Simon Larsen
d55e354a07 feat: Update instructions for building and compiling with proper formatting and clarity 2025-09-29 13:35:08 +01:00
Nawaz Dhandala
20716b7c7e feat: Refactor WebAuthn integration in login and user management components for improved readability and maintainability 2025-09-29 13:07:48 +01:00
Nawaz Dhandala
86143d1585 feat: Add WebAuthn support to the login page for enhanced two-factor authentication 2025-09-29 13:01:57 +01:00
Nawaz Dhandala
214915528b feat: Implement UserWebAuthn model and API for WebAuthn credential management 2025-09-29 13:00:52 +01:00
Nawaz Dhandala
9b24ff50ec feat: Enhance two-factor authentication by integrating WebAuthn support and updating fetch function to return webAuthnList 2025-09-29 12:55:33 +01:00
Nawaz Dhandala
9caeb34f63 feat: Add WebAuthn support for two-factor authentication
- Introduced new API paths for generating and verifying WebAuthn authentication options.
- Integrated UserWebAuthnAPI into the BaseAPI feature set.
- Added UserWebAuthn model to the database models.
- Implemented UserWebAuthnService for handling WebAuthn-related database operations.
- Updated the User Profile page to include functionality for managing WebAuthn security keys.
- Added UI components for registering and displaying WebAuthn security keys.
- Included necessary dependencies for WebAuthn functionality in package.json and package-lock.json.
2025-09-29 12:54:44 +01:00
Simon Larsen
368f33db24 feat: Enhance bot message handling by checking for direct messages and mentions 2025-09-29 11:40:26 +01:00
Simon Larsen
5d7a18cbe2 feat: Integrate getWorkspaceTypeDisplayName for improved workspace type display in notification rules 2025-09-29 11:21:25 +01:00
Simon Larsen
19f663d0fd feat: Simplify form card hiding by using deleteActivity method in Microsoft Teams actions 2025-09-29 11:16:49 +01:00
Simon Larsen
d857024fbe feat: Update activity text to ensure proper form card hiding in Microsoft Teams actions 2025-09-29 10:51:45 +01:00
Simon Larsen
027d766e03 feat: Hide form card after adding notes and executing actions in Microsoft Teams alert, incident, and scheduled maintenance actions 2025-09-29 10:49:19 +01:00
Simon Larsen
e7599c2202 feat: Add null handling for on-call policy cards in MicrosoftTeamsAlertActions and MicrosoftTeamsIncidentActions 2025-09-29 10:42:12 +01:00
Simon Larsen
703b525310 feat: Enhance Microsoft Teams actions with new incident and alert functionalities, including viewing and submitting notes, executing on-call policies, and changing states 2025-09-29 09:52:27 +01:00
Simon Larsen
534882bc17 feat: Add new incident note actions for viewing and submitting notes in MicrosoftTeamsIncidentActionType 2025-09-27 13:12:05 +01:00
Simon Larsen
bdb2170663 refactor: Consolidate incident action handling into MicrosoftTeamsIncidentActions class 2025-09-27 13:04:24 +01:00
Simon Larsen
dffb11b304 fix: Correct casing for incident action type values in MicrosoftTeamsIncidentActionType enum 2025-09-27 12:44:51 +01:00
Simon Larsen
4e5386fccf fix: Normalize incident action type values and improve logging in MicrosoftTeamsUtil 2025-09-27 12:09:01 +01:00
Simon Larsen
6c11fbf850 fix: Remove fallback text for accessibility in adaptive card message handling 2025-09-27 11:50:08 +01:00
Simon Larsen
f713eb7546 fix: Improve error handling for missing AAD Object ID in Teams bot invoke activity 2025-09-27 11:22:24 +01:00
Simon Larsen
0d1f07b5ae feat: Add method to extract action type and value from Teams Adaptive Card submits 2025-09-27 11:15:38 +01:00
Simon Larsen
ffcaaf213f fix: Simplify action type mapping in MicrosoftTeamsUtil and improve logging 2025-09-27 11:08:25 +01:00
Simon Larsen
b05a0619fb feat: Enhance MicrosoftTeamsUtil to support adaptive card actions and improve message handling 2025-09-27 10:59:20 +01:00
Simon Larsen
cbd4d26189 feat: Add helm template command to generate Kubernetes manifests in release workflow 2025-09-27 10:30:06 +01:00
Simon Larsen
1f24a79a8a fix: Update JSON schema to include commonConfiguration and allow additional properties for cert-manager 2025-09-27 10:24:03 +01:00
Simon Larsen
94227a103d feat: Add comprehensive JSON schema for OneUptime Helm chart configuration, defining properties for various components including global settings, database configurations, and service parameters. 2025-09-27 10:20:22 +01:00
Simon Larsen
cd1bf5befe fix: Enhance markdown handling in MicrosoftTeamsUtil to clean up links and bold markers for better rendering 2025-09-26 18:02:22 +01:00
Simon Larsen
e50e75b009 fix: Replace LIMIT_PER_PROJECT with a fixed limit of 10 for query results in MicrosoftTeams utility 2025-09-26 17:50:13 +01:00
Simon Larsen
9656fbdae4 feat: Enhance Microsoft Teams bot with new commands for active incidents, scheduled maintenance, ongoing maintenance, and active alerts 2025-09-26 17:45:24 +01:00
Simon Larsen
1004251175 fix: Update Microsoft Teams app type to single-tenant and adjust bot adapter initialization for tenant-specific handling 2025-09-26 15:08:53 +01:00
Simon Larsen
5c70aea851 fix: Update import for ConfigurationBotFrameworkAuthenticationOptions and type definition for authConfig in MicrosoftTeamsUtil 2025-09-26 15:02:51 +01:00
Simon Larsen
25d5cc2a47 refactor: Remove HomeClientUrl export from EnvironmentConfig for code clarity 2025-09-26 14:38:40 +01:00
Simon Larsen
b6802bf949 Merge branch 'master' into v4-ms-teams 2025-09-26 14:23:46 +01:00
Simon Larsen
5af14af52a feat: Add EnableWorkflow decorator to OnCallDutyPolicy models for enhanced access control 2025-09-26 14:20:47 +01:00
Nawaz Dhandala
fffe84526e refactor: Clean up logging statements in ScheduledMaintenanceService.ts for improved clarity 2025-09-25 19:46:39 +01:00
Simon Larsen
e776186070 feat: Enhance logging for scheduled maintenance notifications with detailed debug information 2025-09-25 19:45:51 +01:00
Nawaz Dhandala
0f4f974d04 refactor: Remove unnecessary blank lines in SCIM.ts and TeamMemberService.ts for improved code clarity 2025-09-25 19:35:41 +01:00
Simon Larsen
22ecea6381 Merge branch 'master' of github.com:OneUptime/oneuptime 2025-09-25 19:32:22 +01:00
Simon Larsen
d9ad2e3036 refactor: Remove ignoreHooks property from team member operations and adjust SCIM checks for root users 2025-09-25 19:32:19 +01:00
Nawaz Dhandala
42e60213c6 refactor: Improve code formatting and readability in SCIM.ts 2025-09-25 19:22:27 +01:00
Simon Larsen
6817a0759a feat: Add enablePushGroups option to SCIM configuration for enhanced group management 2025-09-25 19:21:49 +01:00
Nawaz Dhandala
963e2ffc6d refactor: Update SCIM check function signature and type annotations in TeamView and Users components 2025-09-25 16:36:25 +01:00
Nawaz Dhandala
5c2c50ecc6 refactor: Improve code formatting and readability in ScheduledMaintenanceService, TeamMemberService, TeamView, and Users components 2025-09-25 16:32:38 +01:00
Simon Larsen
1bb4e77075 Merge branch 'scim-push' 2025-09-25 16:26:30 +01:00
Simon Larsen
1f22c231d6 feat: Update SCIM error modal title for clarity on user management 2025-09-25 15:21:00 +01:00
Simon Larsen
4b3baa9ad3 feat: Add SCIM error modal to prevent user invitations when SCIM is enabled 2025-09-25 15:14:07 +01:00
Simon Larsen
503728345f feat: Implement SCIM checks in TeamView and Teams to restrict member invitations and deletions when SCIM is enabled 2025-09-25 15:11:19 +01:00
Nawaz Dhandala
958b6bf72c fix: Improve notification settings handling for scheduled maintenance updates 2025-09-25 14:40:27 +01:00
Simon Larsen
6bb5ec12f5 feat: Implement SCIM checks in TeamMemberService to prevent member invitations and deletions when SCIM is enabled 2025-09-25 14:23:30 +01:00
Simon Larsen
30b2b63b9b feat: Enhance user creation process in SCIM by integrating auto-provisioning and team assignment logic 2025-09-25 13:56:37 +01:00
Simon Larsen
6c37290f49 feat: Refactor user existence checks in SCIM team operations for improved clarity and efficiency 2025-09-25 13:26:30 +01:00
Nawaz Dhandala
b19302f8b9 feat: Refactor SCIM member handling to use SCIMMember type for improved type safety 2025-09-25 13:03:06 +01:00
Nawaz Dhandala
acd0629791 chore: update eslint and typescript-eslint dependencies to latest versions 2025-09-25 12:54:45 +01:00
Nawaz Dhandala
3fa1aebceb chore: update eslint and eslint-plugin-unused-imports dependencies
- upgraded eslint-plugin-unused-imports from 3.2.0 to 4.1.0
- downgraded eslint from 9.36.0 to 8.57.1
- updated typescript-eslint packages to use scoped package names
2025-09-25 12:51:54 +01:00
Simon Larsen
1d6a645241 chore: update dependencies for eslint and typescript
- upgraded eslint from ^8.57.0 to ^9.36.0
- upgraded typescript from ^5.8.3 to ^5.9.2
- upgraded typescript-eslint from ^8.33.1 to ^8.44.1
2025-09-25 12:47:23 +01:00
Simon Larsen
922e30f162 feat: Add MigrationName1758798730753 to schema migrations 2025-09-25 12:13:02 +01:00
Simon Larsen
73beb80056 feat: Add enablePushGroups column to ProjectSCIM for SCIM provisioning 2025-09-25 12:12:48 +01:00
Simon Larsen
72fd74862c feat: Add conditional display for Teams based on enablePushGroups setting in SCIM configuration 2025-09-25 12:02:42 +01:00
Simon Larsen
13d403ecf8 feat: Add enablePushGroups option to SCIM configuration for group provisioning 2025-09-25 12:02:29 +01:00
Simon Larsen
30bca619dd feat: Add enablePushGroups configuration to ProjectSCIM for SCIM provisioning 2025-09-25 11:59:08 +01:00
Simon Larsen
f969adba9c fix: Update Okta integration instructions for SCIM application setup 2025-09-25 11:46:58 +01:00
Simon Larsen
b42abc6e42 Merge branch 'master' into scim-push 2025-09-25 11:26:10 +01:00
Simon Larsen
7ec183f9e9 fix: Add Cert-Manager configuration instructions for Let's Encrypt in README 2025-09-25 11:18:16 +01:00
Simon Larsen
d33c739372 fix: Correct secretName assignment logic for TLS hosts in ingress template 2025-09-24 21:55:07 +01:00
Simon Larsen
d8e12daec5 fix: Update ingress and cluster-issuer templates for ACME challenge handling 2025-09-24 21:26:55 +01:00
Simon Larsen
2a54cfc527 fix: Add ACME certificate profile to ClusterIssuer configuration 2025-09-24 20:51:15 +01:00
Simon Larsen
c0241a2e20 refactor: Update Cert-Manager integration in README and templates for clarity and consistency 2025-09-24 11:32:45 +01:00
Simon Larsen
e32d7cb368 fix: Add note to enable cert-manager before Let's Encrypt configuration 2025-09-24 11:28:16 +01:00
Simon Larsen
4dec1290e8 refactor: Update cert-manager configuration to use new naming convention for Let's Encrypt settings 2025-09-24 11:21:41 +01:00
Simon Larsen
04f6493a6d Refactor code structure for improved readability and maintainability 2025-09-24 11:19:15 +01:00
Simon Larsen
f113e84aa5 fix: Remove cert-manager CRDs installation option from values.yaml 2025-09-24 11:09:45 +01:00
Simon Larsen
57f764b92a refactor: Update cert-manager configuration to use index for improved clarity 2025-09-24 11:03:03 +01:00
Simon Larsen
c4c7d10d16 Add ClusterIssuer configuration for cert-manager with Let's Encrypt support
- Introduced a new template for ClusterIssuer in the Helm chart.
- Configured ACME server and email for Let's Encrypt.
- Set up HTTP01 solver with ingress class from values.
2025-09-24 10:47:34 +01:00
Simon Larsen
00ba94f372 feat: Include status page ID in SEO response for improved tracking 2025-09-24 10:35:52 +01:00
Simon Larsen
f85f41ffa9 fix: Ensure non-null assertion for repeatableJobs when adding job 2025-09-23 21:45:51 +01:00
Simon Larsen
21dfcdfa63 Merge pull request #2017 from OneUptime/queue-readd
feat: Implement repeatable job handling on queue reconnection; add lo…
2025-09-23 21:13:12 +01:00
Nawaz Dhandala
c818decfc8 refactor: Enhance type definitions and error handling in reconnect listener setup 2025-09-23 21:04:55 +01:00
Simon Larsen
1386fef470 refactor: Extract reconnect listener setup into a separate method for improved readability and error handling 2025-09-23 21:01:25 +01:00
Simon Larsen
4c0cbc17a2 refactor: Update queue event listener to use async IIFE for improved readability and reliability 2025-09-23 20:53:38 +01:00
Simon Larsen
3c42447b41 Merge branch 'master' into queue-readd 2025-09-23 20:44:26 +01:00
Simon Larsen
e31f616dc1 Merge pull request #2016 from OneUptime/sp-rss
Sp rss
2025-09-23 20:39:23 +01:00
Nawaz Dhandala
cfbb65f7ae refactor: Replace inline type definitions with RSSItem type for improved readability and consistency 2025-09-23 20:31:54 +01:00
Nawaz Dhandala
52c42dae1e refactor: Enhance type annotations for getStatusPageData and handleRSS functions; streamline code structure 2025-09-23 20:30:21 +01:00
Simon Larsen
d838d377a0 feat: Add HomeClientUrl to EnvironmentConfig and update RSS feed URL generation to use HttpProtocol 2025-09-23 20:22:56 +01:00
Simon Larsen
3a6f8b4c95 feat: Add atom link and guid to RSS feed XML generation 2025-09-23 20:17:37 +01:00
Simon Larsen
1039bd9f0b feat: Implement RSS feed handling and status page data retrieval logic 2025-09-23 20:16:20 +01:00
Nawaz Dhandala
5f84c7195c feat: Implement repeatable job handling on queue reconnection; add logging for job re-addition 2025-09-23 20:13:54 +01:00
Nawaz Dhandala
9597f66ab1 refactor: Improve type annotations for TeamComplianceStatusTable component; remove unnecessary line in SendStateChangeNotification 2025-09-23 19:37:16 +01:00
Simon Larsen
1562f8ee6a fix: Update @oneuptime/common and axios versions in package-lock.json; modify compile command to include npm update 2025-09-23 19:34:01 +01:00
Simon Larsen
11b0477cd6 fix: Add validation for projectId and scheduledMaintenanceId in getDashboardUrl method; include projectId in scheduled maintenance selection 2025-09-23 19:30:41 +01:00
Simon Larsen
174694e040 fix: Update axios version in package-lock.json and clean up unused imports in TeamComplianceStatusTable and TeamView 2025-09-23 19:24:12 +01:00
Simon Larsen
f5664116b9 Merge branch 'release' of github.com:OneUptime/oneuptime into release 2025-09-23 18:14:41 +01:00
Simon Larsen
0eb502b77a Merge branch 'master' into release 2025-09-23 18:14:20 +01:00
Nawaz Dhandala
1460521dc0 fix: Add type annotation for complianceStatusTableRef in TeamView 2025-09-23 18:14:03 +01:00
Nawaz Dhandala
e73c8bca16 refactor: Improve code readability by formatting user name assignment and useImperativeHandle in TeamComplianceStatusTable and TeamView 2025-09-23 18:10:43 +01:00
Simon Larsen
a0fef8df3d feat: Update notification method labels for clarity in TeamView 2025-09-23 17:16:43 +01:00
Simon Larsen
1821377dfa feat: Add refresh functionality to TeamComplianceStatusTable and integrate with TeamView 2025-09-23 17:13:22 +01:00
Simon Larsen
ac2c501058 feat: Refactor TeamComplianceStatusTable rendering and integrate Card component for better UI structure 2025-09-23 17:04:28 +01:00
Simon Larsen
3ce3d1ee65 feat: Improve loading and empty state handling in TeamComplianceStatusTable 2025-09-23 17:00:52 +01:00
Simon Larsen
ad17e49177 feat: Enhance user name retrieval in compliance status to include email as fallback 2025-09-23 16:58:50 +01:00
Simon Larsen
18389a0c31 feat: Update ComplianceRuleType enum values for consistency and clarity 2025-09-23 13:19:49 +01:00
Nawaz Dhandala
9d04975759 feat: Update migration for ruleType column in TeamComplianceSetting and improve notification rule labels in TeamView 2025-09-23 13:14:13 +01:00
Simon Larsen
3bb9b3d78b feat: Replace migration for ruleType column in TeamComplianceSetting and update OnCallDutyPolicyScheduleLayer defaults 2025-09-23 13:13:35 +01:00
Simon Larsen
ddfae282e6 Merge branch 'team-compliacne-rules' 2025-09-23 13:09:56 +01:00
Simon Larsen
02663bb33a feat: Change ruleType column type to LongText for improved compliance rule descriptions 2025-09-23 13:08:35 +01:00
Simon Larsen
6cb1a49128 feat: Update notification rule labels in TeamView for clarity 2025-09-23 13:07:53 +01:00
Nawaz Dhandala
4bcddf860c feat: Enhance TeamComplianceAPI and TeamComplianceService to include user compliance statuses and improve type safety 2025-09-23 13:05:34 +01:00
Nawaz Dhandala
d8d4593d38 feat: Refactor code for improved readability and maintainability across multiple files 2025-09-23 12:57:28 +01:00
Simon Larsen
f76381525f Merge pull request #2015 from OneUptime/team-compliacne-rules
Team compliance rules
2025-09-23 12:55:47 +01:00
Simon Larsen
91abb2318b feat: Add migration for unique index on teamId and ruleType in TeamComplianceSetting and update OnCallDutyPolicyScheduleLayer defaults 2025-09-23 12:55:14 +01:00
Simon Larsen
3a9e695336 feat: Add unique index on teamId and ruleType in TeamComplianceSetting and implement validation in TeamComplianceSettingService 2025-09-23 12:54:25 +01:00
Simon Larsen
e270c5d70a feat: Enhance compliance checks by aggregating missing notification rules for incident and alert severities 2025-09-23 12:45:27 +01:00
Simon Larsen
b40e88e8ec feat: Add userProfilePictureId to UserComplianceStatus and update rendering in TeamComplianceStatusTable 2025-09-23 12:38:26 +01:00
Simon Larsen
2173ed288a feat: Refactor TeamComplianceStatusTable to use LocalTable component for improved rendering 2025-09-23 12:28:44 +01:00
Simon Larsen
82669c8f23 feat: Update API endpoint for fetching team compliance status and add common headers 2025-09-23 12:25:51 +01:00
Simon Larsen
b093a730ab feat: Add migration for TeamComplianceSetting table and related constraints 2025-09-23 12:15:20 +01:00
Simon Larsen
8700068468 feat: Enhance compliance checks by integrating incident and alert severity notification rules 2025-09-23 12:08:47 +01:00
Simon Larsen
13e322944b feat: Implement TeamComplianceService for managing team compliance status and user notifications 2025-09-23 12:02:11 +01:00
Simon Larsen
62aecc6e9f feat: Add Team Compliance Settings and Status tables to TeamView 2025-09-23 12:00:13 +01:00
Nawaz Dhandala
61eca28545 Merge branch 'release' of https://github.com/OneUptime/oneuptime into release 2025-09-23 11:56:14 +01:00
Nawaz Dhandala
c289027efc fix: Implement retry mechanism for npm prerun command in workflow files to enhance reliability 2025-09-23 11:55:46 +01:00
Simon Larsen
ecdae56ffa feat: Add TeamComplianceStatusTable component for displaying team compliance status 2025-09-23 11:49:40 +01:00
Nawaz Dhandala
005536633c fix: Implement retry mechanism for Dockerfile generation to enhance reliability 2025-09-23 11:48:52 +01:00
Simon Larsen
d1ec1d6936 feat: Implement TeamComplianceAPI for retrieving team compliance status 2025-09-23 11:48:07 +01:00
Simon Larsen
6f68629f29 feat: Add TeamComplianceAPI to BaseAPIFeatureSet for compliance management 2025-09-23 11:46:18 +01:00
Simon Larsen
8af4bece10 feat: Add TeamComplianceSetting model and service with compliance rule types 2025-09-23 11:25:16 +01:00
Simon Larsen
18a0d6ab26 Merge branch 'master' into v4-ms-teams 2025-09-22 12:58:52 +01:00
Simon Larsen
6d73bb8a12 Merge branch 'release' of github.com:OneUptime/oneuptime into release 2025-09-22 12:56:31 +01:00
Simon Larsen
85b0f47be1 feat: Add Code of Conduct page and link in legal section 2025-09-22 12:55:41 +01:00
Simon Larsen
cc3596fc8e fix: Enhance adaptive card sending via Bot Framework with improved error handling and accessibility features 2025-09-22 11:26:02 +01:00
Simon Larsen
654367dbd8 fix: Refactor bot user ID handling and ensure fallback for undefined user ID in message sending 2025-09-22 10:44:08 +01:00
Simon Larsen
e5ccdc1a56 fix: Enhance access token expiration handling in Microsoft Teams utility 2025-09-22 10:33:27 +01:00
Simon Larsen
84389212f6 fix: Update Microsoft Teams app version to 1.1.0 in API manifest 2025-09-21 19:52:10 +01:00
Simon Larsen
5b01c202eb fix: Update Microsoft Teams API permissions for channel message and creation handling 2025-09-21 19:47:42 +01:00
Simon Larsen
153ee4fc20 fix: Update Microsoft Teams API permissions to include ChannelMessage.Send and additional channel-related permissions 2025-09-21 16:56:57 +01:00
Simon Larsen
03e176794e fix: Update Microsoft Teams app version to 2.1.0 and enhance permissions for message team members 2025-09-21 16:08:46 +01:00
Simon Larsen
9783f4897c fix: Correct indentation for Group.Read.All permission in Microsoft Teams integration documentation 2025-09-21 16:03:39 +01:00
Simon Larsen
928f6457bc fix: Update Microsoft Teams API to use Teams API for fetching available teams and add documentation for Group.Read.All permission 2025-09-21 16:00:39 +01:00
Simon Larsen
b628bd3ad1 fix: Enhance Microsoft Teams integration by updating project connection logic and handling admin consent state 2025-09-21 15:46:29 +01:00
Simon Larsen
dba0d69f63 fix: Update admin consent installation instructions for clarity in Microsoft Teams integration 2025-09-21 15:34:12 +01:00
Simon Larsen
64c203259a fix: Move admin consent card rendering to the bottom for improved UI flow in Microsoft Teams integration 2025-09-21 15:32:06 +01:00
Simon Larsen
25ec5c8df0 fix: Update admin consent button titles for clarity in Microsoft Teams integration 2025-09-21 15:31:46 +01:00
Simon Larsen
22270c62f4 fix: Remove admin consent reminder UI when consent is completed in Microsoft Teams integration 2025-09-21 15:31:14 +01:00
Simon Larsen
bb7176252c fix: Ensure loading state is updated correctly in error handling of Microsoft Teams integration 2025-09-21 15:30:53 +01:00
Simon Larsen
97f62b1458 fix: Remove unnecessary return statement in error handling of Microsoft Teams integration 2025-09-21 15:28:13 +01:00
Simon Larsen
5eb333ccfc feat: Update admin consent handling in Microsoft Teams integration 2025-09-21 15:15:33 +01:00
Simon Larsen
bd05afb0a7 feat: Implement admin consent handling and UI updates in Microsoft Teams integration 2025-09-21 14:57:53 +01:00
Simon Larsen
96e6780e7b feat: Enhance project auth token handling in Microsoft Teams integration 2025-09-21 14:54:06 +01:00
Simon Larsen
5e8ed144ae feat: Update title for manual app installation in Microsoft Teams integration 2025-09-21 14:39:27 +01:00
Simon Larsen
588ff245ec feat: Add admin consent callback URL to Microsoft Teams integration documentation 2025-09-21 14:38:17 +01:00
Simon Larsen
02cef807ea feat: Add scope parameter to admin consent URL for Microsoft Teams integration 2025-09-21 14:36:23 +01:00
Simon Larsen
041ffdfbe0 Merge pull request #2014 from OneUptime/master
Release
2025-09-21 14:31:24 +01:00
Simon Larsen
e2c362a5b3 feat: Add admin consent flow for Microsoft Teams integration with callback handling 2025-09-21 13:13:37 +01:00
Simon Larsen
000ff4ad45 refactor: Update API post call to use object syntax for improved clarity 2025-09-21 12:52:54 +01:00
Simon Larsen
064e16cc6f refactor: Improve API call syntax and remove unnecessary comments for better readability 2025-09-21 12:37:10 +01:00
Simon Larsen
fbc6b8fa48 fix: Correct typos and improve clarity in Microsoft Teams integration documentation 2025-09-20 10:05:01 +01:00
Simon Larsen
4e0935873d Merge branch 'master' into v4-ms-teams 2025-09-20 09:50:20 +01:00
Nawaz Dhandala
35a40a431e fix: Correct balance display logic to ensure accurate message for negative balances 2025-09-20 09:24:02 +01:00
Simon Larsen
d24c245b4a refactor: Update API post calls to use structured object syntax for improved readability 2025-09-20 09:23:11 +01:00
Simon Larsen
99f59f2f1e refactor: Update API call to use object destructuring for contributors request 2025-09-20 09:20:11 +01:00
Simon Larsen
c558eb578f refactor: Update API call syntax to use object destructuring for improved readability 2025-09-19 23:01:16 +01:00
Simon Larsen
97380a5410 Merge branch 'master' into v4-ms-teams 2025-09-19 22:58:45 +01:00
Simon Larsen
462f40680e fix: Update balance display logic to show message for negative balances 2025-09-19 22:58:16 +01:00
Simon Larsen
8a11dbe35b fix: Change sort order of createdAt to descending in StatusPageAPI 2025-09-19 22:47:34 +01:00
Nawaz Dhandala
d2c1467a07 refactor: Simplify API call syntax and improve code readability across multiple files 2025-09-19 22:37:25 +01:00
Simon Larsen
ff57061190 Refactor API calls to use new request structure
- Updated API.post and API.get calls across multiple components to use the new object structure for requests, including specifying `url`, `data`, and `headers` explicitly.
- This change improves code readability and consistency in how API requests are made throughout the application.
2025-09-19 22:36:05 +01:00
Simon Larsen
63f1034f4a refactor: Update API calls to use object destructuring for parameters in multiple components 2025-09-19 22:19:50 +01:00
Simon Larsen
87ddec9e6c refactor: Update API method calls to use object destructuring for parameters 2025-09-19 22:15:02 +01:00
Simon Larsen
b2ea52e549 refactor: Simplify API method signatures by consolidating parameters into a single options object 2025-09-19 22:01:25 +01:00
Simon Larsen
92bb753cbf fix: Update API call in Billing settings to use undefined for data parameter 2025-09-19 21:51:36 +01:00
Simon Larsen
7428e75643 refactor: Remove inheritance from BaseAPI and restructure BillingAPI class 2025-09-19 21:45:29 +01:00
Simon Larsen
0a2ed040f0 feat: Add discountPercent field to Project model and corresponding migration 2025-09-19 21:33:48 +01:00
Simon Larsen
ded0aba399 feat: Implement customer balance retrieval in BillingAPI and update Billing settings page to display balance 2025-09-19 21:25:52 +01:00
Simon Larsen
0658248535 refactor: Remove dark mode styles from BarChart and LineChart components for consistency 2025-09-19 21:10:41 +01:00
Simon Larsen
6cf2d842ad Merge branch 'master' of github.com:OneUptime/oneuptime 2025-09-19 21:02:46 +01:00
Simon Larsen
4a6bf95ef3 refactor: Simplify styles in LogsViewer and LegendItem components for cleaner UI 2025-09-19 21:02:41 +01:00
Nawaz Dhandala
af7835fc8a refactor: Improve code formatting and readability in various service and utility files 2025-09-19 20:58:32 +01:00
Nawaz Dhandala
5e7ed2be73 Merge branch 'master' of https://github.com/OneUptime/oneuptime 2025-09-19 20:57:56 +01:00
Nawaz Dhandala
11894d0ba5 Refactor API calls to use unified request structure
- Updated MicrosoftTeams and Slack classes to use the new API.post structure with url and data properties.
- Refactored API utility methods to accept a single options object for HTTP requests, improving consistency across the codebase.
- Modified various service and utility classes (e.g., CopilotPullRequestService, CodeRepositoryUtil, and ApiMonitor) to align with the new API request format.
- Enhanced error handling and logging for API responses.
- Updated tests to accommodate changes in API method signatures.
2025-09-19 20:57:54 +01:00
Simon Larsen
8bb92bbeb3 docs: Clarify server restart instructions in Microsoft Teams integration documentation 2025-09-19 15:58:58 +01:00
Simon Larsen
a50af21ce0 docs: Revise Microsoft Teams integration documentation for app manifest upload steps and server restart instructions 2025-09-19 15:57:55 +01:00
Simon Larsen
df29ed2b78 docs: Update Microsoft Teams integration documentation to clarify app registration details and configuration steps 2025-09-19 15:44:39 +01:00
Simon Larsen
58cba788fc docs: Update Microsoft Teams integration documentation to correct bot service creation steps 2025-09-19 15:37:02 +01:00
Simon Larsen
760044f603 docs: Add clarification on secret value usage in Microsoft Teams integration documentation 2025-09-19 15:36:08 +01:00
Simon Larsen
fb099a1c3e refactor: Remove redundant notes on Microsoft Teams permissions for clarity 2025-09-19 15:35:41 +01:00
Simon Larsen
32692bc939 docs: Enhance Microsoft Teams integration documentation with detailed permissions information 2025-09-19 15:35:21 +01:00
Simon Larsen
c514b9cb74 refactor: Update Microsoft Teams integration documentation for clarity and accuracy 2025-09-19 15:17:12 +01:00
Simon Larsen
5d44943958 fix: Update Microsoft Teams app registration details for clarity and accuracy 2025-09-19 15:15:44 +01:00
Simon Larsen
df4138e5d3 Merge branch 'master' into v4-ms-teams 2025-09-19 14:56:04 +01:00
Simon Larsen
51777709f3 Revert "refactor: Enhance email template styles and structure for improved readability and aesthetics"
This reverts commit 43b6fdf45d.
2025-09-19 13:47:49 +01:00
Simon Larsen
f7416bb0a6 feat: Add alertId and incidentId to notification payloads for enhanced tracking 2025-09-19 12:46:53 +01:00
Simon Larsen
43b6fdf45d refactor: Enhance email template styles and structure for improved readability and aesthetics 2025-09-19 12:35:44 +01:00
Simon Larsen
818995a6ae feat: enhance bot authentication configuration for multi-tenant Microsoft Teams apps 2025-09-18 21:02:48 +01:00
Simon Larsen
38fa1ba9f8 refactor: streamline message handling by utilizing TurnContext for sending messages in Microsoft Teams 2025-09-18 20:51:48 +01:00
Simon Larsen
b862ac113f feat: Refactor Bot Framework activity handling to use botbuilder SDK's adapter for improved processing 2025-09-18 20:40:57 +01:00
Simon Larsen
a2526a5a99 refactor: change logging level from info to debug for bot message operations 2025-09-18 20:22:18 +01:00
Simon Larsen
4124e7e9f7 chore: add botbuilder dependency to Common package.json 2025-09-18 20:16:51 +01:00
Simon Larsen
05d3e73b83 docs: Update Microsoft Teams integration documentation for bot messaging endpoint configuration 2025-09-18 20:10:21 +01:00
Simon Larsen
907f0149d8 Merge branch 'master' into v4-ms-teams 2025-09-18 16:56:57 +01:00
Simon Larsen
54f4c63a51 feat: Add dynamic loading of Mermaid.js for rendering diagrams in blog posts 2025-09-18 16:56:27 +01:00
Simon Larsen
80388adb59 refactor: Implement JWT validation for Bot Framework authentication in MicrosoftTeamsAPI 2025-09-17 21:23:32 +01:00
Simon Larsen
ca0792a7f7 refactor: Enhance Bot Framework authentication validation and access token retrieval logging 2025-09-17 21:15:40 +01:00
Simon Larsen
7c1e42cff9 refactor: Add validation for Bot Framework service URLs and implement access token retrieval method 2025-09-17 21:09:08 +01:00
Simon Larsen
e9904c0e74 refactor: Move Bot Framework activity handlers to MicrosoftTeamsUtil for better organization and maintainability 2025-09-17 20:58:00 +01:00
Simon Larsen
dd9d296e62 refactor: Enhance logging and add test endpoint for Bot Framework integration in MicrosoftTeamsAPI 2025-09-17 20:54:50 +01:00
Simon Larsen
c0005618fe refactor: Implement Bot Framework messaging endpoint and activity handlers in MicrosoftTeamsAPI 2025-09-17 20:47:11 +01:00
Simon Larsen
feb503b448 refactor: Update image path handling and fix URL formatting in MicrosoftTeamsIntegration 2025-09-17 20:32:29 +01:00
Simon Larsen
d2c411c9d6 refactor: Simplify icon path handling by removing fallback checks in MicrosoftTeamsAPI 2025-09-17 20:26:59 +01:00
Simon Larsen
562e930ca0 refactor: Replace fs with LocalFile utility for icon file handling in MicrosoftTeamsAPI 2025-09-17 20:26:13 +01:00
Simon Larsen
e97b2904e8 refactor: Simplify icon handling logic by removing fallback checks for Microsoft Teams icons 2025-09-17 16:47:01 +01:00
Simon Larsen
fd07d70575 refactor: Update Teams app manifest version and improve icon handling logic 2025-09-17 16:44:35 +01:00
Simon Larsen
3e6c8b3f68 refactor: Remove fallback for MicrosoftTeamsAppClientId and add packageName to manifest 2025-09-17 16:23:26 +01:00
Simon Larsen
ad772c4a5b refactor: Remove fallback for MicrosoftTeamsAppClientId in webApplicationInfo 2025-09-17 16:22:10 +01:00
Simon Larsen
5396c0c9cf refactor: Specify type for archive variable in MicrosoftTeamsAPI 2025-09-17 16:13:56 +01:00
Simon Larsen
c6e6ae5be0 refactor: Update Microsoft Teams app manifest version and improve icon directory handling 2025-09-17 16:13:32 +01:00
Simon Larsen
82397fec5a refactor: Update Teams app manifest versioning logic and add client ID to docker-compose 2025-09-17 16:04:32 +01:00
Simon Larsen
6301e24c02 refactor: Add Microsoft Teams app client ID and secret to configuration files 2025-09-17 15:37:03 +01:00
Simon Larsen
23b3a4d9dd refactor: Add Microsoft Teams app configuration to environment and docker-compose files 2025-09-17 15:34:50 +01:00
Simon Larsen
e6c158b2b5 refactor: Use AppVersion for Teams app manifest versioning 2025-09-17 15:30:43 +01:00
Simon Larsen
5a4f2e0744 refactor: Add validation for Microsoft Teams App Client ID in getTeamsAppManifest method 2025-09-17 15:29:34 +01:00
Simon Larsen
545335a74d refactor: Update nodemon.json files to ignore node_modules and public directories 2025-09-17 14:50:58 +01:00
Simon Larsen
5ab49052fe Merge branch 'master' into v4-ms-teams 2025-09-17 14:22:25 +01:00
Simon Larsen
2985b7675d refactor: Remove unused Button import and clean up API call parameters in SlackChannelCacheModal component 2025-09-17 14:01:18 +01:00
Simon Larsen
49cd04c5d5 refactor: Simplify channel cache management by replacing rows with a dictionary component in SlackChannelCacheModal 2025-09-17 13:54:56 +01:00
Simon Larsen
2a23d1a962 refactor: Update API call to include common headers and remove unnecessary modal properties in SlackChannelCacheModal component 2025-09-17 13:48:56 +01:00
Nawaz Dhandala
13f19adc13 refactor: Enhance type annotations and improve code clarity in SlackAPI, CopyTextButton, LogItem, LogsViewer, and SlackChannelCacheModal components 2025-09-17 13:20:05 +01:00
Nawaz Dhandala
3e79bbf55f refactor: Improve code formatting and readability across multiple components 2025-09-17 13:10:30 +01:00
Nawaz Dhandala
b400e89a3b Merge branch 'release' 2025-09-17 13:09:59 +01:00
Nawaz Dhandala
fc8362e7e5 feat: Implement SlackChannelCacheModal for viewing and editing cached Slack channels 2025-09-17 13:09:29 +01:00
Nawaz Dhandala
8b209e82d5 feat: Add endpoint to fetch and cache all Slack channels for the current tenant's project 2025-09-17 13:08:09 +01:00
Simon Larsen
6fb4fdf698 Merge pull request #2009 from OneUptime/better-log-ui
refactor: Enhance LogsViewer with improved scroll handling and UI upd…
2025-09-17 13:04:07 +01:00
Simon Larsen
9551e64b16 refactor: Replace SVG icons with Icon component for consistency in CopyTextButton and LogItem components 2025-09-17 12:58:36 +01:00
Simon Larsen
232f938d2c refactor: Update log message styling for improved readability in LogItem component 2025-09-17 12:51:25 +01:00
Simon Larsen
0dc24b36e6 refactor: Consolidate copy button logic for log message display in LogItem component 2025-09-17 12:46:38 +01:00
Simon Larsen
a77eaf214f refactor: Remove wrapLines toggle and associated UI elements for a cleaner toolbar in LogsViewer component 2025-09-17 12:44:45 +01:00
Simon Larsen
5ab08fbfcb refactor: Simplify click handling for collapse button in LogItem component 2025-09-17 12:34:16 +01:00
Simon Larsen
a73c2eb32c refactor: Update CopyTextButton integration in LogItem component with improved props and styling 2025-09-17 12:31:19 +01:00
Nawaz Dhandala
8f9e6d5bec refactor: Increase maxPages limit for improved pagination in SlackUtil 2025-09-17 12:30:34 +01:00
Nawaz Dhandala
790e26d608 refactor: Increase limit for faster searches in requestBody 2025-09-17 12:30:22 +01:00
Simon Larsen
79b96bcce8 refactor: Simplify severity badge handling and improve log body rendering in LogItem component 2025-09-17 12:22:27 +01:00
Simon Larsen
592a7f893e refactor: Enhance LogItem and LogsViewer components with improved styling and functionality 2025-09-17 12:18:48 +01:00
Simon Larsen
7408f9c204 Merge pull request #2008 from OneUptime/snyk-fix-649148701c5ddf7371d98302b082212e
[Snyk] Security upgrade axios from 1.7.7 to 1.12.0
2025-09-17 11:47:38 +01:00
Nawaz Dhandala
307de42434 feat: Add generateGroupsListResponse function for SCIM group list responses 2025-09-17 11:28:41 +01:00
Nawaz Dhandala
d1ae7f67c4 refactor: Adjust indentation for @CaptureSpan() annotation in updateChannelCache method 2025-09-17 11:07:30 +01:00
Simon Larsen
b4616885b2 refactor: Implement bulk channel cache updates and improve cache handling in SlackUtil 2025-09-17 11:05:50 +01:00
Nawaz Dhandala
7fb7f3719f refactor: Update lastUpdated timestamp format and clean up code style in SlackUtil 2025-09-17 10:56:59 +01:00
Nawaz Dhandala
d1d1d1935d refactor: Enhance LogsViewer with improved scroll handling and UI updates 2025-09-17 10:55:05 +01:00
Simon Larsen
5b54f66821 refactor: Add projectId parameter to getAllWorkspaceChannels and implement bulk cache update 2025-09-17 10:54:04 +01:00
Simon Larsen
e99a954387 refactor: Add projectId parameter to getAllWorkspaceChannels method 2025-09-17 10:50:40 +01:00
Simon Larsen
93fd9ce3cc refactor: Limit API pagination to maxPages and adjust channel search limit 2025-09-17 10:46:26 +01:00
Simon Larsen
3a3e510b11 refactor: Exclude archived channels in Slack API requests 2025-09-17 10:43:44 +01:00
Simon Larsen
cbaf1edf89 refactor: Increase maxPages limit for API search in SlackUtil 2025-09-17 10:41:59 +01:00
snyk-bot
94b21b5fc3 fix: Common/package.json & Common/package-lock.json to reduce vulnerabilities
The following vulnerabilities are fixed with an upgrade:
- https://snyk.io/vuln/SNYK-JS-AXIOS-12613773
2025-09-17 01:44:58 +00:00
Simon Larsen
f60cc4c199 fix: Correct configurationUrl formatting in MicrosoftTeamsAPI 2025-09-16 21:37:24 +01:00
Simon Larsen
81b1f120ee refactor: Remove manifest state and props from MicrosoftTeamsIntegration and MicrosoftTeamsIntegrationDocumentation components 2025-09-16 21:30:49 +01:00
Simon Larsen
48a0036023 feat: Add manual app installation instructions and download option for app manifest in MicrosoftTeamsIntegration 2025-09-16 21:25:34 +01:00
Simon Larsen
9925a6a5a5 feat: Update MicrosoftTeamsAPI and EnvironmentConfig to improve app manifest handling and add HomeClientUrl 2025-09-16 21:17:51 +01:00
Simon Larsen
e5ca287dcb feat: Update MicrosoftTeamsAPI to read pre-resized icons from the filesystem instead of using placeholder images 2025-09-16 21:12:51 +01:00
Simon Larsen
1a5962c430 feat: Refactor token validation in MicrosoftTeamsUtil to enhance error handling and refresh logic 2025-09-16 21:05:55 +01:00
Simon Larsen
ba5a664fca feat: Update MicrosoftTeamsAPI to disable notification-only mode and extract tenant ID from JWT for improved token validation 2025-09-16 21:05:43 +01:00
Simon Larsen
f7d05c642f feat: Refactor MicrosoftTeams integration in WorkspaceUtil to use MicrosoftTeamsUtil and include projectId in user name retrieval 2025-09-16 20:52:20 +01:00
Simon Larsen
877810219c feat: Enhance MicrosoftTeamsUtil with detailed debug logging for access token management and message sending 2025-09-16 20:48:56 +01:00
Simon Larsen
fed8c9a261 feat: Add detailed debug logging for message sending in MicrosoftTeamsUtil 2025-09-16 20:43:25 +01:00
Simon Larsen
896d36b16b feat: Add detailed logging for channel retrieval and message posting in MicrosoftTeamsUtil 2025-09-16 20:41:21 +01:00
Simon Larsen
a9c6d565f4 feat: Implement access token refresh mechanism in MicrosoftTeamsUtil for improved token management 2025-09-16 20:39:33 +01:00
Simon Larsen
ae58a33456 refactor: Improve debug logging format for channel search in SlackUtil 2025-09-16 18:17:34 +01:00
Simon Larsen
d689d7a12a feat: Enhance MicrosoftTeamsUtil with valid access token retrieval and update interfaces for additional data fields 2025-09-16 16:44:24 +01:00
Simon Larsen
f28ada8994 fix: Add undefined parameter to API calls in MicrosoftTeamsUtil for consistency 2025-09-16 16:33:34 +01:00
Nawaz Dhandala
4d9de1d326 refactor: Add debug logging for channel search in SlackUtil 2025-09-16 14:42:28 +01:00
Simon Larsen
405b28ee91 feat: Pass workspaceType to query in WorkspaceNotificationRuleTable component 2025-09-16 14:33:11 +01:00
Simon Larsen
245aeac4a9 refactor: Remove Coming Soon component and update Microsoft Teams connection messages for consistency 2025-09-16 14:16:56 +01:00
Nawaz Dhandala
6b8f8db991 feat: add archiver package and update Microsoft Teams integration components
- Added `archiver` package to `package.json` and `package-lock.json`.
- Refactored `MicrosoftTeamsIntegration.tsx` for improved readability and consistency.
- Updated error handling and state management in `MicrosoftTeamsIntegration`.
- Enhanced documentation component for Microsoft Teams integration.
- Cleaned up formatting and structure in `MicrosoftTeamsIntegrationDocumentation.tsx`.
- Adjusted settings page to utilize the updated Microsoft Teams integration component.
2025-09-16 14:08:40 +01:00
Simon Larsen
6c06c6682a refactor: Update team selection UI layout and button styles for improved alignment 2025-09-16 14:02:01 +01:00
Simon Larsen
f48da38ae1 feat: Replace button elements with reusable Button component for team selection UI 2025-09-16 13:55:47 +01:00
Simon Larsen
29de1dfbb2 refactor: Remove team search functionality and update team selection UI 2025-09-16 13:53:59 +01:00
Simon Larsen
9063bd145f feat: Replace team selection buttons with radio buttons for improved UX 2025-09-16 13:50:55 +01:00
Simon Larsen
436a393cb1 refactor: Enhance team selection UI with improved styling and search functionality 2025-09-16 13:46:53 +01:00
Simon Larsen
a7f6e264f5 feat: Implement team selection UI for Microsoft Teams integration with search functionality 2025-09-16 13:30:10 +01:00
Simon Larsen
777c2a36bf refactor: Remove optional serviceCatalogId from MicrosoftTeamsMiscData interface 2025-09-16 12:23:53 +01:00
Simon Larsen
c005448103 feat: Update Microsoft Teams API scopes to include User.Read permission 2025-09-16 12:05:03 +01:00
Simon Larsen
54f7be2c62 feat: Update Microsoft Teams OAuth flow to use static redirect URI with state parameter for projectId and userId 2025-09-16 11:53:26 +01:00
Simon Larsen
1f52b91bc5 refactor: Simplify Microsoft Teams integration page by removing unnecessary components and structure 2025-09-16 11:45:14 +01:00
Simon Larsen
d54bafd5d1 Merge branch 'master' of github.com:OneUptime/oneuptime into v4-ms-teams 2025-09-16 11:20:37 +01:00
Simon Larsen
7728e84f34 Merge pull request #2007 from OneUptime/span-fix
refactor: Enhance root span detection logic in TraceExplorer component
2025-09-16 11:15:37 +01:00
Simon Larsen
a5808eac5e refactor: Enhance root span detection logic in TraceExplorer component 2025-09-16 11:15:13 +01:00
Simon Larsen
5fdb54a8d9 feat: Implement Microsoft Teams API with endpoints for app manifest, OAuth, and webhooks 2025-09-16 10:47:09 +01:00
Simon Larsen
f02a4de88d feat: Refactor channel ID handling to include team ID and improve channel info retrieval from Microsoft Graph API 2025-09-16 10:38:18 +01:00
Simon Larsen
49b2999d4f feat: Enhance Microsoft Teams integration with new messaging and channel management features 2025-09-16 10:33:59 +01:00
Simon Larsen
60c1caa11f feat: Add Microsoft Teams integration component with authentication handling 2025-09-16 10:26:40 +01:00
Simon Larsen
3eb21895d8 feat: Implement Microsoft Teams authentication actions and request handling 2025-09-16 10:26:13 +01:00
Simon Larsen
2d9527e94d feat: Add Microsoft Teams integration documentation component 2025-09-16 10:10:55 +01:00
Simon Larsen
9f26e6f75d feat: Update Microsoft Teams incident action types to use specific incident action constants 2025-09-16 09:59:25 +01:00
Simon Larsen
7890698f7d feat: Refactor scheduled maintenance action handling to use constants for action types 2025-09-16 09:58:55 +01:00
Simon Larsen
e738db0b24 feat: Add Microsoft Teams action handlers for alerts, incidents, monitors, and on-call duties; update scheduled maintenance action types 2025-09-16 09:48:25 +01:00
Simon Larsen
67524ee869 feat: Implement Microsoft Teams scheduled maintenance actions and update alert action types 2025-09-16 09:34:49 +01:00
Simon Larsen
0e5c0d1509 feat: Add Microsoft Teams integration with configuration and action types 2025-09-16 09:32:43 +01:00
Simon Larsen
cdff9338d9 refactor: Simplify stats initialization and update logic in TraceExplorer component 2025-09-15 18:50:46 +01:00
Simon Larsen
074926ee41 Merge pull request #2005 from OneUptime/on-call-loop
On call loop
2025-09-15 18:35:29 +01:00
Nawaz Dhandala
b0ef6e23a3 refactor: Improve readability and structure in LayerUtil's getEvents method 2025-09-15 18:35:08 +01:00
Simon Larsen
1ab6bc5af9 test: Enhance overnight window test to validate distinct segments across midnight 2025-09-15 18:18:22 +01:00
Simon Larsen
04dba20871 feat: Implement weekly restrictions handling in LayerUtil and add corresponding tests 2025-09-15 18:15:13 +01:00
Simon Larsen
1cd5c927f6 feat: Add comprehensive tests for LayerUtil including daily restrictions and rotation handoff scenarios 2025-09-15 18:10:35 +01:00
Simon Larsen
6513222c3b feat: Add support for overnight event windows in getEventsByDailyRestriction method 2025-09-15 18:09:07 +01:00
Simon Larsen
498abd5251 fix: Update error handling to exclude status 400 for network-related errors 2025-09-15 17:21:48 +01:00
Nawaz Dhandala
fc46a81eb8 refactor: Improve code readability and consistency in various components 2025-09-15 12:41:12 +01:00
Simon Larsen
c3fb6e9f32 Merge branch 'master' of github.com:OneUptime/oneuptime 2025-09-15 12:35:58 +01:00
Simon Larsen
3aba42b965 feat: Enhance TraceExplorer UI with trace ID display and copy functionality 2025-09-15 12:22:51 +01:00
Simon Larsen
5651a43b22 feat: Add service filtering and statistics display in TraceExplorer 2025-09-15 12:19:29 +01:00
Simon Larsen
929792ef6a feat: Enhance TraceExplorer with error filtering and summary stats display 2025-09-15 11:54:20 +01:00
Simon Larsen
e13cdc4523 Merge pull request #2004 from OneUptime/span-improve
Span improve
2025-09-15 11:50:47 +01:00
Simon Larsen
8dc0535dac feat: Enhance JSONTable grouping logic to always override existing keys with grouped array representation 2025-09-15 11:50:21 +01:00
Simon Larsen
dc4541739a feat: Implement grouping for primitive arrays in JSONTable component 2025-09-15 11:41:07 +01:00
Simon Larsen
a4a3da7e2e feat: Add UI improvement task with Tailwind CSS guidelines for modern design 2025-09-15 11:30:54 +01:00
Simon Larsen
21432e1416 feat: Add default sorting to TelemetryServiceTable by name in ascending order 2025-09-15 11:23:57 +01:00
Simon Larsen
2f568f1319 feat: Add JSONTable component for improved display of JSON attributes in SpanViewer 2025-09-15 11:17:04 +01:00
Simon Larsen
50bdd592b4 refactor: Clean up telemetry span attributes by removing unnecessary properties 2025-09-15 10:50:02 +01:00
Simon Larsen
3938637b84 feat: Enhance MarkdownViewer styling and improve preformatted code handling 2025-09-10 20:08:30 +01:00
Simon Larsen
3ed9e21271 Merge branch 'master' of github.com:OneUptime/oneuptime 2025-09-10 19:09:57 +01:00
Simon Larsen
63e1266e2b feat: Improve MarkdownViewer styling with enhanced Tailwind CSS classes for better readability and aesthetics 2025-09-10 19:09:53 +01:00
Nawaz Dhandala
a552812711 feat: Add projectId support to SlackUtil message sending for incident and scheduled maintenance actions 2025-09-10 18:21:30 +01:00
Nawaz Dhandala
ad07ab75fe feat: Add elkjs dependency for enhanced functionality 2025-09-10 18:13:59 +01:00
Nawaz Dhandala
c8deffebb0 refactor: Improve code readability by standardizing formatting and spacing in SlackUtil methods 2025-09-10 17:12:51 +01:00
Simon Larsen
67a3ea5109 feat: Enhance SlackUtil with projectId support and caching for channel operations 2025-09-10 17:11:04 +01:00
Simon Larsen
6728cc0458 feat: Add projectId parameter to channel-related methods for improved context handling 2025-09-10 17:08:02 +01:00
Simon Larsen
f84ab2474f feat: Optimize channel existence checks by introducing getWorkspaceChannelByName method and streamline channel name normalization 2025-09-10 16:47:36 +01:00
Simon Larsen
5c8ce04eed feat: Enhance pagination handling by supporting 'skip' and 'limit' parameters from both query and body 2025-09-09 16:56:55 +01:00
Nawaz Dhandala
3064aa0364 feat: Improve code formatting and descriptions in announcement-related components and migrations for better readability 2025-09-09 14:36:32 +01:00
Simon Larsen
9625f1381c feat: Add migration for AnnouncementMonitor and AnnouncementTemplateMonitor tables with foreign key constraints 2025-09-09 14:12:33 +01:00
Simon Larsen
6ecd3ad166 Merge branch 'master' into announcement-monitor 2025-09-09 14:04:36 +01:00
Simon Larsen
8e54cac86e feat: Add createEditModalWidth prop with large size to multiple template views for consistent modal presentation 2025-09-09 14:01:46 +01:00
Nawaz Dhandala
cc52bb76d1 feat: Enhance incident state handling by adding type definitions, improving error handling, and updating default state display 2025-09-09 13:54:52 +01:00
Nawaz Dhandala
4c037f54f4 feat: Refactor incident state migration and update related components for improved clarity and functionality 2025-09-09 13:49:34 +01:00
Simon Larsen
b869628d4a feat: Implement fetch for initial incident state and update form values 2025-09-09 13:48:11 +01:00
Simon Larsen
0fbeb503ad feat: Update incident state field title and description for clarity 2025-09-09 13:06:29 +01:00
Simon Larsen
a302e4dc6c feat: Implement automatic selection of the first incident state and update related references 2025-09-09 12:58:07 +01:00
Nawaz Dhandala
00c8783137 feat: Add monitor selection to status page announcements and templates, enhancing resource notification capabilities 2025-09-09 12:56:42 +01:00
Simon Larsen
11211f4a62 feat: Update initial incident state description to reflect default behavior 2025-09-09 12:53:30 +01:00
Simon Larsen
d29750d66e feat: Add initialIncidentState field to IncidentTemplates for incident creation 2025-09-09 12:47:15 +01:00
Simon Larsen
7dc590dab4 feat: Add initialIncidentStateId migration and update index references 2025-09-09 12:37:43 +01:00
Simon Larsen
1d0ed64c1a feat: Rename currentIncidentState to initialIncidentState and update related references in IncidentTemplate, IncidentService, and IncidentTemplatesView 2025-09-09 12:26:48 +01:00
Simon Larsen
0cf3884be4 Merge branch 'master' into select-incident-state 2025-09-09 12:21:12 +01:00
Simon Larsen
165f5608e6 feat: Add step to free disk space in GitHub Actions runner for improved image build efficiency 2025-09-09 12:18:07 +01:00
Nawaz Dhandala
f2b8cfbffb Merge branch 'master' of https://github.com/OneUptime/oneuptime 2025-09-09 12:12:57 +01:00
Nawaz Dhandala
6084e15f20 refactor: Enhance type annotations in MarkdownEditor and tests for improved type safety 2025-09-09 12:12:55 +01:00
Simon Larsen
b1db4187de Learn more about Markdown syntax. 2025-09-09 12:12:09 +01:00
Nawaz Dhandala
20ce8a8c74 refactor: Clean up MarkdownEditor and FormField code for improved readability and consistency 2025-09-09 11:45:00 +01:00
Simon Larsen
39200249d1 feat: Update spell check handling in MarkdownEditor and tests for improved functionality 2025-09-09 11:38:58 +01:00
Simon Larsen
27533125e4 feat: Add createEditModalWidth prop to multiple components for consistent modal sizing 2025-09-09 11:23:20 +01:00
Simon Larsen
99dd421329 feat: Add createEditModalWidth prop to IncidentDelete for consistent modal sizing 2025-09-09 11:17:11 +01:00
Simon Larsen
4184894f27 feat: Refactor MarkdownEditor toolbar layout for improved organization and readability 2025-09-09 11:12:40 +01:00
Simon Larsen
a7a00dc0fa feat: Add dataTestId prop to MarkdownEditor and FormField for improved testing 2025-09-09 10:58:06 +01:00
Simon Larsen
9340f69789 feat: Add additional toolbar buttons and formatting options in MarkdownEditor 2025-09-09 10:35:10 +01:00
Simon Larsen
ba33bc0c23 feat: Enhance MarkdownEditor with improved heading handling and toolbar buttons 2025-09-09 10:27:41 +01:00
Simon Larsen
b8cac60c6e feat: Enhance Markdown preview with improved code block handling and styling 2025-09-09 09:59:32 +01:00
Simon Larsen
3a5d5253d0 feat: Enhance MarkdownEditor with toolbar buttons and preview functionality 2025-09-09 09:53:44 +01:00
Simon Larsen
64010b0348 feat: Add initial incident state selection to incident templates and creation forms 2025-09-09 08:35:46 +01:00
Simon Larsen
84ca2ff311 fix: Remove redundant APP_VERSION build argument in Docker image deployment steps 2025-09-08 22:08:00 +01:00
Simon Larsen
c0becebadc feat: Update date formatting to user-friendly display in getMonitorStatusTimelineForStatusPage method 2025-09-08 21:58:53 +01:00
Nawaz Dhandala
6ef99fd890 refactor: Specify types for format and testDate in OneUptimeDate class methods 2025-09-08 21:52:31 +01:00
Nawaz Dhandala
a55f2f7842 refactor: Improve code readability by formatting function arguments and return values in date handling methods 2025-09-08 21:51:53 +01:00
Simon Larsen
0aae7877c7 feat: Update date formatting to user-friendly display in various components 2025-09-08 21:49:19 +01:00
Simon Larsen
8d6cc37f7a feat: Update date formatting to user-friendly display across various components 2025-09-08 21:41:57 +01:00
Simon Larsen
1a0f7eb1e7 feat: Enhance date formatting to user-friendly display in Scheduled Maintenance components 2025-09-08 21:36:47 +01:00
Simon Larsen
6ed65ed3ef fix: Change tag type from semver to raw for Docker image deployments 2025-09-08 20:43:09 +01:00
Simon Larsen
2ac342e26a feat: Add billing_enabled variable to Nginx configuration 2025-09-08 20:29:28 +01:00
Simon Larsen
fe80d6b1ff fix: Remove unnecessary markdown syntax from upgrading guide 2025-09-08 18:44:43 +01:00
Simon Larsen
a68254be6d fix: Clarify reason for discontinuing Bitnami charts in upgrading guide 2025-09-08 18:43:14 +01:00
Simon Larsen
49a9e355fe feat: Add upgrading guide and navigation link to documentation 2025-09-08 18:41:40 +01:00
Simon Larsen
7091e35393 Update GitHub Actions workflow to read version prefix from VERSION_PREFIX file and adjust versioning scheme
- Added a new job 'read-version' to read the major and minor version from VERSION_PREFIX file.
- Updated dependent jobs to use the version read from 'read-version' instead of hardcoded version.
- Changed versioning format in multiple jobs to reflect the new versioning scheme based on the content of VERSION_PREFIX.
- Created VERSION_PREFIX file with initial version set to 8.0.
2025-09-07 15:17:47 +01:00
Simon Larsen
34cc8a43ab Merge pull request #1995 from OneUptime/bitnami-mgr-postgres
Bitnami mgr postgres
2025-09-07 13:21:41 +01:00
Simon Larsen
75333ef36c feat: Add pod security context configuration for ClickHouse and Redis StatefulSets 2025-09-07 13:03:09 +01:00
Simon Larsen
d4b3f1b60b feat: Add primary pod security context configuration for PostgreSQL 2025-09-07 12:59:27 +01:00
Simon Larsen
318d20a5a5 feat: Update PostgreSQL StatefulSet to use primary nodeSelector, affinity, tolerations, and resources 2025-09-07 12:56:29 +01:00
Simon Larsen
44b9c33e5c feat: Add primary ConfigMaps for PostgreSQL configuration and pg_hba settings 2025-09-07 12:45:32 +01:00
Simon Larsen
317a17cbab feat: Rename PostgreSQL ConfigMaps to include 'primary' in their names for clarity 2025-09-07 12:40:23 +01:00
Simon Larsen
6d2cb53760 feat: Update PostgreSQL configuration to use primary settings for ConfigMaps 2025-09-07 12:37:30 +01:00
Simon Larsen
7ddc4be319 feat: Add pg_hba.conf configuration and corresponding ConfigMap for PostgreSQL 2025-09-07 12:32:47 +01:00
Simon Larsen
604776551b feat: Add PostgreSQL configuration checksum and update container args 2025-09-07 12:25:41 +01:00
Simon Larsen
26b085030d refactor: Remove initContainers from PostgreSQL StatefulSet and enable default configuration settings 2025-09-07 12:17:13 +01:00
Simon Larsen
e1046d2424 Merge branch 'master' into bitnami-mgr-postgres 2025-09-07 11:08:46 +01:00
Simon Larsen
cf2a7b9dfa feat: Enhance diagnostics collection in KinD setup script 2025-09-07 11:05:34 +01:00
Simon Larsen
55f4c0b65d docs: Add SQL query to check used and free space in Postgres 2025-09-06 20:38:34 +01:00
Simon Larsen
5100fbda52 docs: Add SQL query to check used and free space in Clickhouse 2025-09-06 20:35:17 +01:00
Simon Larsen
9e36188975 fix: Update image registry and repository in ci-values.yaml 2025-09-06 17:47:47 +01:00
Simon Larsen
26c2d41dfa Merge branch 'master' into bitnami-mgr-postgres 2025-09-06 17:45:00 +01:00
Simon Larsen
a511a433b1 refactor: Remove security context and default profiles from ClickHouse configuration 2025-09-06 11:27:50 +01:00
Simon Larsen
cc581e91b5 Merge pull request #1994 from OneUptime/bitnami-mgr
Bitnami mgr
2025-09-06 11:09:13 +01:00
Simon Larsen
3fd95fe8aa Merge branch 'master' into bitnami-mgr 2025-09-06 11:09:01 +01:00
Simon Larsen
6f2455c265 fix: Set ClickHouse resourcesPreset to "none" to override default value 2025-09-05 21:26:00 +01:00
Simon Larsen
de5b32a609 refactor: Remove default resourcesPreset for ClickHouse configuration 2025-09-05 21:17:13 +01:00
Simon Larsen
155b0d90f1 refactor: Transition from MicroK8s to KinD for Kubernetes cluster setup in CI scripts 2025-09-05 21:15:58 +01:00
Simon Larsen
3da5e12a0d feat: Add auto-generated password option for ClickHouse configuration 2025-09-05 21:14:58 +01:00
Simon Larsen
8accdc6bd4 Merge branch 'master' of github.com:OneUptime/oneuptime 2025-09-05 20:56:35 +01:00
Simon Larsen
22a10702ac feat: Add comprehensive architecture diagram and explanation for self-hosted setup 2025-09-05 20:56:32 +01:00
Nawaz Dhandala
a013c86fae refactor: Standardize wait duration for pod readiness checks 2025-09-05 16:24:25 +01:00
Nawaz Dhandala
361626d21f refactor: Enhance pod readiness check with improved diagnostics and error handling 2025-09-05 16:23:01 +01:00
Simon Larsen
6615ac63d7 refactor: Simplify legal page structure and enhance navigation with Bootstrap styling 2025-09-05 14:50:22 +01:00
Simon Larsen
655611b28d refactor: Revamp support page layout and content for improved user experience 2025-09-05 14:45:55 +01:00
Simon Larsen
9bd45ecd14 Merge branch 'master' into bitnami-mgr-postgres 2025-09-05 14:01:46 +01:00
Simon Larsen
53c9babb83 refactor: Replace commented wait logic with active polling for pod readiness 2025-09-05 14:01:00 +01:00
Simon Larsen
ccc0b0142b feat: Remove default profiles configuration from ClickHouse settings in values.yaml 2025-09-05 13:51:07 +01:00
Simon Larsen
4a2f7f68cb feat: Update PostgreSQL password field to use 'postgresPassword' in values.yaml and secrets.yaml 2025-09-05 12:48:10 +01:00
Simon Larsen
de994e10de feat: Fix PostgreSQL username to be fixed as "postgres" in values.yaml 2025-09-05 12:21:43 +01:00
Simon Larsen
2ff22ca079 feat: Enhance security context handling for ClickHouse, PostgreSQL, and Redis StatefulSets 2025-09-05 12:06:28 +01:00
Simon Larsen
e6b8f60977 feat: Update security context for init and primary PostgreSQL containers 2025-09-05 11:59:37 +01:00
Simon Larsen
b823b5924a feat: Add init container for PostgreSQL configuration symlinks and update security context 2025-09-05 11:42:35 +01:00
Simon Larsen
a58ddd94d5 feat: Add POSTGRES_INITDB_ARGS environment variable for PostgreSQL initialization 2025-09-04 20:19:08 +01:00
Simon Larsen
275e15ce96 feat: Update PostgreSQL environment variables and liveness probe configuration 2025-09-04 20:12:20 +01:00
Simon Larsen
1e2a30823c feat: Remove PostgreSQL authentication environment variables from StatefulSet 2025-09-04 19:48:21 +01:00
Simon Larsen
a326e7084e feat: Remove PostgreSQL dependency and associated chart from Helm configuration 2025-09-04 17:55:18 +01:00
Simon Larsen
2c1d20f680 feat: Remove PostgreSQL dependency from Helm chart 2025-09-04 17:54:48 +01:00
Simon Larsen
95bd2db0dd feat: Add security context support for ClickHouse, PostgreSQL, and Redis StatefulSets 2025-09-04 17:44:59 +01:00
Simon Larsen
3ca875254c feat: Add PostgreSQL StatefulSet configuration to Helm chart 2025-09-04 17:35:39 +01:00
Simon Larsen
7f1f78dad6 feat: Add PostgreSQL Secret configuration to Helm chart 2025-09-04 17:27:49 +01:00
Simon Larsen
a0d6468aee feat: Add PostgreSQL ConfigMap and Service templates for Helm chart 2025-09-04 17:27:30 +01:00
Simon Larsen
914c9bc58e feat: Update PostgreSQL configuration in values.yaml and README for built-in support 2025-09-04 17:26:21 +01:00
Simon Larsen
b38031e9f7 feat: Add subPath for data and config mounts in ClickHouse StatefulSet 2025-09-04 17:03:57 +01:00
Simon Larsen
487ca71f84 fix: Rename volume and mount paths for Redis data in StatefulSet configuration 2025-09-04 15:30:27 +01:00
Simon Larsen
67cd8e7db6 refactor: Remove initContainers from ClickHouse StatefulSet configuration 2025-09-04 15:28:17 +01:00
Simon Larsen
f44017d710 feat: Add Helm annotations for release name and namespace in templates 2025-09-04 12:23:16 +01:00
Simon Larsen
78240b906b feat: Add ClickHouse StatefulSet configuration to Helm chart 2025-09-03 21:57:59 +01:00
Simon Larsen
2ef0b3be27 feat: Add ClickHouse ConfigMap template for configuration management 2025-09-03 21:43:20 +01:00
Simon Larsen
0792d8367a feat: Add ClickHouse service and secret configurations to Helm chart 2025-09-03 21:43:01 +01:00
Simon Larsen
920397cead fix: Remove ClickHouse chart package from the repository 2025-09-03 21:41:35 +01:00
Simon Larsen
42c18e94ab feat: Update ClickHouse configuration and service settings in values.yaml 2025-09-03 21:41:29 +01:00
Simon Larsen
533f7eb238 fix: Remove ClickHouse dependency from Chart.yaml and Chart.lock 2025-09-03 21:38:03 +01:00
Simon Larsen
e2f16e85f1 Merge pull request #1993 from OneUptime/bitnami-mgr
Bitnami mgr
2025-09-03 20:12:22 +01:00
Simon Larsen
c98e6b8471 feat: Add KEDA chart dependency to README 2025-09-03 20:12:09 +01:00
Simon Larsen
c16c13fd89 feat: Add built-in Redis configuration to README and update external Redis instructions 2025-09-03 20:10:43 +01:00
Simon Larsen
c8ce0e8819 fix: Remove Redis cluster configuration options from values.yaml 2025-09-03 20:00:14 +01:00
Simon Larsen
9e98f6acdb fix: Remove Redis replica persistence configuration from values.yaml 2025-09-03 19:50:07 +01:00
Simon Larsen
a7a5b15dde feat: Implement Redis StatefulSet configuration in Helm chart 2025-09-03 19:49:49 +01:00
Simon Larsen
3ebb5217a2 feat: Add Redis master and headless service definitions to Helm chart 2025-09-03 19:46:10 +01:00
Simon Larsen
f570ffe1e3 feat: Add Redis ConfigMap template to Helm chart for Redis configuration management 2025-09-03 19:43:57 +01:00
Simon Larsen
ae94bf6d7c fix: Simplify Redis password handling in Helm chart by removing unnecessary conditional checks 2025-09-03 19:42:28 +01:00
Simon Larsen
d9a6e465bb fix: Remove Redis authentication requirement in values.yaml 2025-09-03 19:42:11 +01:00
Simon Larsen
020b171b77 fix: Update Redis password handling in Helm chart to support optional authentication 2025-09-03 19:38:06 +01:00
Simon Larsen
afc4932c28 fix: Remove Redis dependency and related configurations from Helm chart 2025-09-03 19:37:30 +01:00
Nawaz Dhandala
324851c57e fix: Refactor service operations to execute sequentially with improved error handling in AlertService, IncidentService, MonitorService, and ScheduledMaintenanceService 2025-09-03 15:41:23 +01:00
Nawaz Dhandala
380ecfa096 Refactor code for consistency and readability
- Updated array and object property access from single quotes to double quotes in Pagination.ts and Permissions.ts for consistency.
- Added missing commas in function parameters and object literals across multiple files in AlertService.ts, IncidentService.ts, MonitorService.ts, ScheduledMaintenanceService.ts, and WorkspaceNotificationRuleService.ts.
- Improved error logging messages in various services for better clarity.
- Removed unnecessary line breaks in Slack.ts and Workspace.ts for cleaner code.
- Ensured consistent formatting in Routes.ts by adding missing commas and adjusting line breaks.
2025-09-03 15:37:39 +01:00
Simon Larsen
5f9f73ceaa fix: Refactor monitor creation operations to execute sequentially with improved error handling in MonitorService 2025-09-03 15:35:13 +01:00
Simon Larsen
038ca4a920 fix: Update imports and improve formatting in Routes.ts for consistency and readability 2025-09-03 15:34:29 +01:00
Simon Larsen
d15629da0f fix: Refactor scheduled maintenance operations to execute sequentially with improved error handling in ScheduledMaintenanceService 2025-09-03 15:21:03 +01:00
Simon Larsen
363bbf9dea fix: Refactor incident creation operations to execute sequentially with improved error handling in IncidentService 2025-09-03 15:17:16 +01:00
Simon Larsen
6f0a0c8e38 fix: Remove unnecessary line breaks in error messages and logging for improved readability in WorkspaceNotificationRuleService 2025-09-03 15:06:19 +01:00
Simon Larsen
a75a62c708 fix: Refactor promise chain to use async/await for better readability in AlertService; add debug logging in WorkspaceNotificationRuleService 2025-09-03 14:18:05 +01:00
Simon Larsen
db76d716b9 fix: Remove redundant logging of existing workspace channels for cleaner output 2025-09-03 14:02:12 +01:00
Simon Larsen
b0abbf64b4 fix: Improve logging format in postToWorkspaceChannels for better readability 2025-09-03 13:55:43 +01:00
Simon Larsen
3a432cf8e6 Response from Slack API for getting all channels: 2025-09-03 13:54:58 +01:00
Simon Larsen
5c7d18e3ed fix: Update createdByUser field to use _id for consistency in Alert, Incident, and Scheduled Maintenance services 2025-09-03 13:27:37 +01:00
Simon Larsen
2590850ffa fix: Correct projectId usage in alert feed info generation for accurate monitor links 2025-09-03 13:13:14 +01:00
Simon Larsen
0eeb80e16e fix: Add createdByUserId and createdByUser fields to alert, incident, and scheduled maintenance services for improved tracking 2025-09-03 13:09:38 +01:00
Simon Larsen
e1cfe24a24 fix: Update pageData property access to bracket notation for consistency 2025-09-02 22:59:23 +01:00
Simon Larsen
4e4f3a889d fix: Update type casting for statusReport and probeMonitorResponse to 'any' for improved flexibility 2025-09-02 22:51:41 +01:00
Nawaz Dhandala
ede7ae103d fix: Enhance MonitorTemplateUtil to support additional monitor types and improve type safety 2025-09-02 22:15:04 +01:00
Simon Larsen
075c0fb6bd fix: Enhance template variable support for additional monitor types in MonitorTemplateUtil and update documentation 2025-09-02 22:08:51 +01:00
Nawaz Dhandala
5ebdb1ef7d fix: Refactor code for improved readability and maintainability in various components 2025-09-02 21:57:15 +01:00
Simon Larsen
387dbf332e fix: Correct spelling in API endpoint routes for escalation rules 2025-09-02 21:48:54 +01:00
Simon Larsen
9681e1dc88 fix: Remove fallback syntax from incident alert templating examples for clarity 2025-09-02 21:47:05 +01:00
Simon Larsen
fb29014480 Merge branch 'dynamic-alert' 2025-09-02 21:45:15 +01:00
Simon Larsen
1a5c2efc59 fix: Add debug logging for storage map and template value replacement in MonitorTemplateUtil 2025-09-02 21:43:28 +01:00
Simon Larsen
3e31e44ed5 fix: Enhance value replacement logic to properly serialize objects in VMUtil class 2025-09-02 21:26:55 +01:00
Simon Larsen
9e69d69429 fix: Update titles and descriptions for Global Probes settings for clarity 2025-09-02 21:15:21 +01:00
Simon Larsen
a108deac0f fix: Improve documentation links in MonitorCriteriaAlertForm and MonitorCriteriaIncidentForm for clarity 2025-09-02 21:14:03 +01:00
Simon Larsen
c767f14bf1 fix: Correct syntax error in AlertService class 2025-09-02 21:11:28 +01:00
Simon Larsen
d69485c436 fix: Update Global Probes status messages for clarity in ProbePage component 2025-09-02 20:50:20 +01:00
Simon Larsen
67a5bdb7b8 feat: Update Global Probe settings card with improved descriptions and toggle functionality 2025-09-02 20:47:56 +01:00
Nawaz Dhandala
6504731025 refactor: Replace 'any' types with specific types for improved type safety across multiple files 2025-09-02 20:41:24 +01:00
Nawaz Dhandala
773692081c docs: Update guideline to specify stopping after fixing 25 files for review 2025-09-02 20:25:52 +01:00
Nawaz Dhandala
51c6234966 docs: Add guideline to replace "any" types with proper types 2025-09-02 20:23:53 +01:00
Nawaz Dhandala
fac6e9a1fe fix: Correct formatting issues in MonitorTemplateUtil and MonitorCriteriaAlertForm 2025-09-02 20:14:33 +01:00
Nawaz Dhandala
86e5d85d55 feat: Enhance dynamic template documentation links in MonitorCriteriaAlertForm and MonitorCriteriaIncidentForm 2025-09-02 20:10:14 +01:00
Simon Larsen
1c592435e9 Merge pull request #1990 from OneUptime/any-type
Any type
2025-09-02 20:02:38 +01:00
Nawaz Dhandala
02fed5bd6e refactor: Enhance type safety by explicitly defining ref types and simplifying conditional checks 2025-09-02 19:58:41 +01:00
Nawaz Dhandala
dd724fcc6e refactor: Improve type safety by updating formRef initialization and adding optional chaining for setFieldValue 2025-09-02 19:52:28 +01:00
Nawaz Dhandala
6ba26bcb82 refactor: Improve type safety by adding LayoutOptions type and removing 'any' casts in ServiceDependencyGraph 2025-09-02 19:51:45 +01:00
Nawaz Dhandala
799ab3220d refactor: Replace 'any' type with specific types for ref and input for improved type safety 2025-09-02 19:50:53 +01:00
Nawaz Dhandala
f73f2fb732 refactor: Change argValue type from any to unknown for better type safety 2025-09-02 19:50:34 +01:00
Simon Larsen
43d6ead92c fix: Correct formatting issues in MonitorAlert class logging and data processing 2025-09-02 19:22:31 +01:00
Simon Larsen
4c053b3f31 refactor: Improve formatting and readability in MonitorTemplateUtil methods 2025-09-02 19:02:29 +01:00
Simon Larsen
c026e411cf feat: Add dynamic template usage descriptions in MonitorCriteriaAlertForm and MonitorCriteriaIncidentForm 2025-09-02 19:01:22 +01:00
Simon Larsen
65a9e32db1 feat: Implement MonitorTemplateUtil for dynamic template processing in incidents and alerts 2025-09-02 18:58:15 +01:00
Simon Larsen
0b15e97e08 feat: Integrate MonitorTemplateUtil for dynamic alert and incident title/description processing 2025-09-02 18:56:19 +01:00
Simon Larsen
bd74b96596 feat: Add link to Incident & Alert Dynamic Templating documentation in navigation 2025-09-02 18:45:48 +01:00
Nawaz Dhandala
990d3ea750 feat: Add doNotAddGlobalProbesByDefaultOnNewMonitors column to Project table and update related files 2025-09-02 14:59:23 +01:00
Simon Larsen
30665a1907 feat: Add migration to introduce doNotAddGlobalProbesByDefaultOnNewMonitors column in Project table 2025-09-02 14:58:09 +01:00
Simon Larsen
04db4289fa feat: Add setting to control auto-adding of global probes to new monitors 2025-09-02 14:57:18 +01:00
Nawaz Dhandala
01f4c030a7 style: Format allowedDomains and homeUrl for improved readability 2025-09-02 14:29:17 +01:00
Simon Larsen
d060ed8b64 feat: Add dynamic robots.txt route to control indexing based on domain 2025-09-02 14:27:40 +01:00
Nawaz Dhandala
413240733e fix: Remove unnecessary blank lines in Markdown and Probe initialization files 2025-09-02 14:17:15 +01:00
Simon Larsen
b0799093dd feat: Enhance proxy agent support by specifying types for httpAgent and httpsAgent in WebsiteRequest and API classes 2025-09-02 14:10:42 +01:00
Simon Larsen
0ecdc775db feat: Refactor proxy configuration to use getRequestProxyAgents method across multiple modules 2025-09-02 14:02:40 +01:00
Simon Larsen
82065c20b1 feat: Add HTTP/HTTPS proxy support in FetchMonitorTest, FetchList, and Alive jobs 2025-09-02 13:58:15 +01:00
Simon Larsen
3dcd1ee604 feat: Add HTTP/HTTPS proxy support in probeMonitorTest and probeMonitor methods 2025-09-02 13:53:57 +01:00
Simon Larsen
f63c69e6a6 feat: Add per-request HTTP/HTTPS proxy agent support in API and WebsiteRequest classes 2025-09-02 13:52:25 +01:00
Simon Larsen
6ba793e871 refactor: Remove axios proxy configuration from ProxyConfig class 2025-09-02 13:49:58 +01:00
Simon Larsen
7afd243992 feat: Remove backticks from inline code rendering in Markdown 2025-09-01 21:19:39 +01:00
Simon Larsen
4f58155719 feat: Enhance inline code rendering by removing backticks from code content 2025-09-01 21:12:30 +01:00
Simon Larsen
553adc4aef feat: Add custom styling for inline code in Markdown renderer 2025-09-01 21:11:37 +01:00
Simon Larsen
f668a626d7 refactor: Simplify code block styling in Markdown renderer 2025-09-01 21:02:19 +01:00
Nawaz Dhandala
e3bd534295 refactor: Add missing commas in logging statements for SCIM and StatusPageSCIM 2025-09-01 20:50:53 +01:00
Simon Larsen
6aa5c3b314 feat: Enhance SCIM Users endpoint to support filtering by email and improve logging 2025-09-01 20:50:12 +01:00
Simon Larsen
3bc4f7267d refactor: Clean up logging statements and remove unnecessary commas in SCIM.ts 2025-09-01 20:48:54 +01:00
Simon Larsen
a7021cf045 refactor: Replace logSCIMOperation with logger.debug for consistent logging in SCIM and StatusPageSCIM 2025-09-01 20:40:27 +01:00
Simon Larsen
2709e1d976 feat: Update values.yaml to allow insecure images temporarily and specify legacy image repositories for PostgreSQL, ClickHouse, and Redis 2025-09-01 16:38:56 +01:00
Nawaz Dhandala
8ec9d2a930 feat: Add type annotations for proxy-related variables in ProxyConfig and monitors 2025-09-01 14:58:33 +01:00
Nawaz Dhandala
224c225789 Merge branch 'master' of https://github.com/OneUptime/oneuptime 2025-09-01 14:53:33 +01:00
Simon Larsen
85dae7a307 feat: Add proxy configuration options for probes in values.yaml and update README and probe.yaml 2025-09-01 14:53:24 +01:00
Nawaz Dhandala
332a479c22 feat: Improve proxy configuration handling and logging in monitors 2025-09-01 14:50:52 +01:00
Simon Larsen
d708fbbb52 feat: Add proxy configuration examples to Custom Probe documentation and component 2025-09-01 14:49:40 +01:00
Simon Larsen
03bceb959e feat: Enhance proxy support in SSL and Synthetic Monitors to prefer HTTPS, fallback to HTTP 2025-09-01 14:47:01 +01:00
Simon Larsen
efa411206e feat: Update SSL and Synthetic Monitors to use HTTPS proxy configuration 2025-09-01 14:41:57 +01:00
Simon Larsen
27fd99f2e8 feat: Update proxy configuration to support separate HTTP and HTTPS proxy URLs 2025-09-01 14:40:54 +01:00
Simon Larsen
07361bfeb7 feat: Enhance SyntheticMonitor with proxy support in browser launch options 2025-09-01 14:07:15 +01:00
Simon Larsen
bc8a5be0fa feat: Add proxy support for CustomCodeMonitor and SyntheticMonitor with logging 2025-09-01 14:00:55 +01:00
Simon Larsen
518768078a feat: Implement proxy configuration for HTTP requests and add ProxyConfig utility 2025-09-01 13:56:29 +01:00
Simon Larsen
86e95f99ff feat: Add PROXY_URL configuration option for probe and update example env file 2025-09-01 12:52:50 +01:00
Simon Larsen
ea48f56097 feat: Add custom styles for code blocks in blog posts 2025-09-01 12:43:03 +01:00
Nawaz Dhandala
b8b9dd859a Refactor migration files for consistency and readability; update BillingService and ProjectService for improved code clarity; enhance Countries interface formatting; standardize string quotes in various components; fix minor formatting issues in Settings and SendAnnouncementCreatedNotification. 2025-08-27 14:50:48 +01:00
Simon Larsen
d28c14ef24 feat: Update projectId reference in status page notification logic 2025-08-27 14:49:33 +01:00
Simon Larsen
670bec2a12 feat: Validate projectId and statusPageId in getStatusPageLinkInDashboard method 2025-08-27 14:48:40 +01:00
Simon Larsen
aff24845a8 feat: Add financeAccountingEmail handling in updateCustomerBusinessDetails method 2025-08-27 14:26:59 +01:00
Simon Larsen
f280e97c1b feat: Add migration for financeAccountingEmail field in Project model 2025-08-27 14:14:02 +01:00
Simon Larsen
62facf62dd feat: Add financeAccountingEmail field to Project model and update billing settings 2025-08-27 14:12:16 +01:00
Simon Larsen
db0387d81a feat: Update placeholder condition to include empty string check 2025-08-27 14:06:33 +01:00
Simon Larsen
5c4b19ab3d feat: Update nodemon configurations to improve performance and debugging options 2025-08-27 13:41:05 +01:00
Simon Larsen
463755fa4d This will be synced to Stripe and appear on future invoices. 2025-08-27 13:40:26 +01:00
Simon Larsen
85888572de feat: Update subscriber notification statuses to 'Success' for existing records in Incident and related tables 2025-08-27 13:20:07 +01:00
Simon Larsen
475bb25b2d feat: Add businessDetailsCountry field to Project migration and update index 2025-08-27 13:14:40 +01:00
Simon Larsen
badd200aed feat: Add country selection dropdown for billing details and implement country options 2025-08-27 13:14:01 +01:00
Simon Larsen
b40d87cbc9 feat: Add business details country field to Project model and update billing services to handle country code 2025-08-27 13:05:54 +01:00
Simon Larsen
36d0066b3a refactor: Simplify migration by removing unnecessary constraints and columns from Project and GlobalConfig tables 2025-08-27 13:03:01 +01:00
Simon Larsen
a49a0b2cba fix: Ensure blog post cards maintain full height for consistent layout 2025-08-27 12:48:41 +01:00
Simon Larsen
bada97d474 feat: Enhance customer address handling in Stripe by mapping business details to structured address fields 2025-08-27 12:42:17 +01:00
Simon Larsen
a1699f2d55 feat: Add business details field to Project model and update Stripe customer details 2025-08-27 12:37:09 +01:00
Simon Larsen
a11e054291 feat: Add custom link rendering with Tailwind styles and external link handling 2025-08-27 11:19:20 +01:00
Simon Larsen
47cf7ba763 fix: Increase timeout for SSL provisioning and delete old data jobs to accommodate longer processing times 2025-08-27 10:16:35 +01:00
Nawaz Dhandala
4e0dfb3664 fix: Simplify BadDataException handling for disabled and missing monitors in ingestion processes 2025-08-27 10:12:13 +01:00
Simon Larsen
250cb9e547 fix: Gracefully handle expected BadDataException cases for disabled and missing monitors in probe ingestion 2025-08-27 10:10:59 +01:00
Simon Larsen
541257e3c6 fix: Handle expected BadDataException cases for disabled and missing monitors in server monitor ingestion 2025-08-27 10:10:23 +01:00
Simon Larsen
ed43686736 fix: Centralize "Monitor disabled" message and improve error handling for disabled monitors 2025-08-27 10:09:26 +01:00
Simon Larsen
9ca45f23e3 fix: Replace hardcoded "Monitor not found" messages with centralized exception messages 2025-08-27 09:58:34 +01:00
Simon Larsen
e3573a9b77 fix: Refactor monitor not found error handling to use centralized exception messages 2025-08-27 09:55:19 +01:00
Simon Larsen
c9e78044e6 fix: Improve error handling in incoming request ingestion worker to handle disabled monitors gracefully 2025-08-27 09:48:54 +01:00
Nawaz Dhandala
813581dec5 fix: Add return type to logoutUser method and specify type for route in navigateToLoginPage method 2025-08-26 21:39:35 +01:00
Nawaz Dhandala
e528decf73 fix: Refactor QueueWorker options handling; improve logoutUser method formatting and navigation logic in StatusPageUtil 2025-08-26 21:36:56 +01:00
Simon Larsen
42ef41ede8 fix: Enhance QueueWorker options with lock duration and max stalled count; improve telemetry processing with yielding to avoid stall detection 2025-08-26 21:32:38 +01:00
Simon Larsen
af26472db4 fix: Simplify email validation logic and improve user lookup in SCIM user operations 2025-08-26 21:24:39 +01:00
Simon Larsen
44b5c8b668 fix: Enhance email validation and logging in SCIM user operations 2025-08-26 20:47:50 +01:00
Simon Larsen
d821b88ed7 fix: Update Docker image tags and labels for multiple services in release workflow 2025-08-26 18:52:15 +01:00
Simon Larsen
1df43e21ff fix: Refactor logoutUser method and enhance navigation logging in StatusPageUtil 2025-08-26 18:37:49 +01:00
Simon Larsen
76ca6ee7e1 fix: Add missing continuation for APP_VERSION build argument in multiple Docker image deploy jobs 2025-08-26 18:25:32 +01:00
Simon Larsen
dac731a57b refactor: Remove unused mock for ProjectUserService in TeamMemberService tests 2025-08-26 16:56:52 +01:00
Simon Larsen
0f4b248598 fix: Add missing context for Docker image build in nginx deployment 2025-08-26 16:53:25 +01:00
Simon Larsen
b2c14e0380 fix: Add missing build context for multiple Docker image deploy jobs 2025-08-26 16:51:51 +01:00
Simon Larsen
3ab9705bbe fix: Allow deletion of teams in Users component by setting isDeleteable to true 2025-08-26 16:34:19 +01:00
Nawaz Dhandala
40812c8749 refactor: Clean up whitespace in TeamMemberService and SCIM files; update description formatting in Users component 2025-08-26 15:59:22 +01:00
Simon Larsen
45ae1501f2 refactor: Replace ProjectUser with TeamMember in SCIM query for team members 2025-08-26 15:55:23 +01:00
Simon Larsen
13d9f19606 refactor: Remove ProjectUserService calls to streamline TeamMemberService operations 2025-08-26 15:52:28 +01:00
Simon Larsen
ad3221310a refactor: Remove ProjectUser model and associated service to streamline user management 2025-08-26 15:51:06 +01:00
Simon Larsen
659042fcfb fix: Update isCreateable property to false for Teams component 2025-08-26 14:43:54 +01:00
Simon Larsen
d65b9c7b29 feat: Refactor Teams component to use TeamMember model and update filtering logic 2025-08-26 14:42:14 +01:00
Simon Larsen
dc77206e6f feat: Add debug logging for SCIM team operations to track user additions 2025-08-26 14:25:31 +01:00
Nawaz Dhandala
9c1910d3f1 refactor: Remove unnecessary context argument from Docker build commands in workflows 2025-08-26 14:09:31 +01:00
Nawaz Dhandala
afe8f8e6f4 refactor: Implement retry mechanism for account and isolated VM compilation steps in workflows 2025-08-26 13:15:54 +01:00
Nawaz Dhandala
015bd0f870 refactor: Implement retry mechanism for Docker image builds in multiple workflows 2025-08-26 13:12:18 +01:00
Nawaz Dhandala
383c145186 refactor: Integrate retry mechanism for Docker image builds in workflows 2025-08-26 13:06:55 +01:00
Simon Larsen
f155795e6b fix: Remove single quotes from changelog delimiter for correct output formatting 2025-08-26 11:56:22 +01:00
Nawaz Dhandala
757f5b5721 refactor: Improve type annotations for better clarity in error handling 2025-08-26 11:42:15 +01:00
Nawaz Dhandala
694215df06 refactor: Improve code formatting for better readability in multiple files 2025-08-26 11:40:01 +01:00
Nawaz Dhandala
0eb6022f1d Merge branch 'master' of https://github.com/OneUptime/oneuptime 2025-08-26 11:38:10 +01:00
Nawaz Dhandala
3109006828 refactor: Replace regex literals with RegExp constructor for improved clarity in MarkdownViewer and StringUtils 2025-08-26 11:38:08 +01:00
Simon Larsen
272695bd11 feat: Enhance error handling and logging in processMetricsAsync and processTracesAsync methods for improved robustness 2025-08-26 11:36:47 +01:00
Simon Larsen
330e3bc106 fix: Correct syntax error in JavaScript expression example for incoming request monitors 2025-08-26 11:26:01 +01:00
Nawaz Dhandala
c7876bf3a3 refactor: Improve regex pattern for fact extraction and enhance code formatting in OtelIngestService 2025-08-26 11:22:36 +01:00
Simon Larsen
345ada5404 feat: Enhance error handling and logging in processLogsAsync method for improved telemetry data ingestion 2025-08-26 11:16:32 +01:00
Simon Larsen
4f97b1b460 feat: Enhance image loading and layout stability across blog views 2025-08-25 10:46:19 +01:00
Nawaz Dhandala
e35ef1809f refactor: Improve code formatting and consistency across Microsoft Teams integration files 2025-08-21 14:45:34 +01:00
Simon Larsen
c2926f3542 feat: Implement structured MessageCard creation from markdown for Microsoft Teams
- Added a method to build a structured MessageCard from markdown input, enhancing message formatting for Teams.
- Extracted title, facts, and actions from markdown to improve rendering in Teams notifications.
2025-08-21 14:43:13 +01:00
Simon Larsen
9495b4bd47 feat: Add Microsoft Teams incoming webhook option to subscriber settings
- Introduced a new property for Microsoft Teams incoming webhook URL in the StatusPageSubscriberService.
- Enhanced subscriber configuration to support Microsoft Teams notifications.
2025-08-21 14:25:49 +01:00
Simon Larsen
ad3f36fdf5 refactor: Simplify Slack and Microsoft Teams notification handling in StatusPageSubscriberService
- Removed try-catch blocks for sending notifications and replaced them with promise chaining for better readability.
- Added logging for successful notification sends and error handling directly in the promise catch.
2025-08-21 14:11:06 +01:00
Simon Larsen
f2221b0a40 feat: Implement Microsoft Teams webhook validation and notification in StatusPage subscriber service
- Added validation for Microsoft Teams incoming webhook URL during subscriber setup.
- Implemented notification sending to Microsoft Teams channel upon successful subscription.
- Updated SideMenu components to reflect the new naming convention for Microsoft Teams subscribers.
2025-08-21 14:04:20 +01:00
Simon Larsen
62fbc1f4be feat: Enhance Microsoft Teams subscriber validation and handling in StatusPage API
- Added validation to ensure Microsoft Teams subscribers are only processed if enabled.
- Updated error messages to include Microsoft Teams workspace name requirements.
- Implemented handling for Microsoft Teams incoming webhook URL and workspace name in subscriber setup.
2025-08-21 13:52:35 +01:00
Simon Larsen
054a2bc8f5 feat: Enable Microsoft Teams subscribers in StatusPage API 2025-08-21 13:49:28 +01:00
Simon Larsen
896787109c feat: Add Microsoft Teams subscriber option to Email, Slack, and SMS subscription pages 2025-08-21 13:47:13 +01:00
Simon Larsen
3a55fcc872 feat: Update microsoftTeamsIncomingWebhookUrl column type to text and add migration 2025-08-21 13:27:44 +01:00
Simon Larsen
2945a48d05 feat: Update microsoftTeamsWorkspaceName column type to VeryLongText and add migration 2025-08-21 13:15:44 +01:00
Simon Larsen
da3a7ddb2e feat: Add migration for Microsoft Teams subscriber functionality in StatusPage 2025-08-21 12:57:16 +01:00
Simon Larsen
04a0bfedaa fix: Make Microsoft Teams subscriber prop required in SideMenu component 2025-08-21 12:17:00 +01:00
Simon Larsen
fa5c7b1e73 feat: Add Microsoft Teams subscriber functionality
- Implemented Microsoft Teams subscribers in the dashboard side menu.
- Created Microsoft Teams subscriber settings page with toggle options.
- Added routes for Microsoft Teams subscribers in the routing configuration.
- Updated page map and route map to include Microsoft Teams subscriber paths.
- Enhanced the app state management to handle Microsoft Teams subscription settings.
- Integrated Microsoft Teams notification sending in the announcement and incident jobs.
- Developed Microsoft Teams subscription management and creation forms.
- Added UI components for managing Microsoft Teams subscribers in the status page.
2025-08-21 12:09:53 +01:00
Simon Larsen
a1c2918cd7 feat: Update homepage heading for improved clarity and emphasis on monitoring capabilities 2025-08-21 10:24:49 +01:00
Simon Larsen
91b11b12c1 fix: Update canonical links for blog posts and remove redundant canonical tags in head partials 2025-08-21 10:15:52 +01:00
Simon Larsen
778a34d631 feat: Implement fallback to commit messages in changelog if empty 2025-08-20 09:17:53 +01:00
Simon Larsen
6dbd838ca4 refactor: Remove mobile redirect script from homepage for cleaner code 2025-08-19 22:23:20 +01:00
Simon Larsen
e09634dc6f feat: Enhance blog listing with featured post display and improved layout 2025-08-19 22:21:12 +01:00
Simon Larsen
af60715de2 Merge branch 'master' of github.com:OneUptime/oneuptime 2025-08-19 22:11:14 +01:00
Simon Larsen
3b4c54876e feat: Update default page size for blog pagination from 50 to 24 for improved performance 2025-08-19 22:11:12 +01:00
Nawaz Dhandala
e357100e46 refactor: Remove redundant dynamic section checks from sitemap tests 2025-08-19 20:43:08 +01:00
Simon Larsen
7f3a50076d feat: Add reference section check to sitemap tests for improved coverage 2025-08-19 14:25:37 +01:00
Simon Larsen
9d182b6d55 feat: Add additional static paths to sitemap generation for improved coverage 2025-08-19 14:25:16 +01:00
Simon Larsen
33fce0b53c feat: Implement caching for home URL retrieval to improve performance 2025-08-19 14:15:47 +01:00
Nawaz Dhandala
3db8419349 refactor: Simplify error handling in BlogPostUtil by removing unused error variables 2025-08-19 13:50:35 +01:00
Nawaz Dhandala
dfc324b099 refactor: Improve code formatting and readability in Markdown and BlogPost utilities 2025-08-19 12:58:17 +01:00
Simon Larsen
36521ef37c feat: Update blog post listing to improve layout and add Open Source Commitment section 2025-08-19 12:55:09 +01:00
Simon Larsen
a6f336340e feat: Implement pagination and tag filtering for blog posts 2025-08-19 12:40:51 +01:00
Simon Larsen
c36f782192 feat: Refactor blog post listing and tags display for improved layout and user experience 2025-08-19 12:34:23 +01:00
Simon Larsen
5219f1cfc0 feat: Remove unused imports and the getNameOfGitHubUser function from BlogPostUtil class 2025-08-19 12:29:27 +01:00
Simon Larsen
7f84d50baa feat: Add optional bio field for authors and update blog post template to display it 2025-08-19 12:28:12 +01:00
Simon Larsen
cd2ce3f1a8 feat: Add a newline for improved readability in BlogPostUtil class 2025-08-19 12:15:23 +01:00
Simon Larsen
01b0e01ca8 feat: Implement caching for blog metadata and improve author resolution without GitHub API calls 2025-08-19 12:07:33 +01:00
Simon Larsen
73dc6bb5db feat: Remove social media image display from blog post template 2025-08-19 11:55:38 +01:00
Simon Larsen
ab7fc1c244 feat: Enhance Markdown renderer with improved code block styling and add support for horizontal rules, emphasis, and strikethrough 2025-08-19 11:50:48 +01:00
Simon Larsen
3927bea29c feat: Enhance Markdown rendering with improved list, table, and inline code support; update tag display styling for better visual consistency 2025-08-19 11:47:21 +01:00
Simon Larsen
6060d66c2b feat: Revamp blog post layout with enhanced styling, author details, and reading time estimation 2025-08-19 11:37:10 +01:00
Nawaz Dhandala
9f4869b05f test: Enhance sitemap tests to validate presence of dynamic sections 2025-08-19 10:07:11 +01:00
Nawaz Dhandala
17bdfee012 refactor: Simplify regex usage in sitemap tests and improve middleware formatting 2025-08-19 10:05:59 +01:00
Simon Larsen
4988b9fc7a feat: Refactor mobile menu implementation for improved accessibility and usability 2025-08-19 10:04:20 +01:00
Simon Larsen
9edc6b9f18 feat: Enhance mobile navigation and improve header styling for better accessibility 2025-08-19 09:58:03 +01:00
Simon Larsen
525e19faa6 feat: Improve tab accessibility and keyboard navigation with semantic identifiers 2025-08-19 09:49:23 +01:00
Simon Larsen
588e8976d2 feat: Add middleware to inject home URL for canonical links and update canonical tag in head-basic.ejs 2025-08-19 09:47:59 +01:00
Simon Larsen
d5e28e98fb Merge branch 'master' of github.com:OneUptime/oneuptime 2025-08-19 09:39:45 +01:00
Simon Larsen
0e84bc9c40 feat: Enhance accessibility and keyboard navigation for product tabs 2025-08-19 09:39:42 +01:00
Nawaz Dhandala
39e8b1da6b refactor: Enhance type annotations and improve code readability in sitemap tests 2025-08-18 13:47:35 +01:00
Nawaz Dhandala
66c4badd94 refactor: Improve formatting of product pages check in sitemap tests 2025-08-18 13:41:01 +01:00
Simon Larsen
a245fabc34 feat: Add end-to-end tests for sitemap loading and validation 2025-08-18 13:39:19 +01:00
Nawaz Dhandala
fa9fce2774 refactor: Improve type annotations and error handling in various modules 2025-08-18 12:59:17 +01:00
Nawaz Dhandala
a256f4be54 Refactor logging statements for improved readability and consistency across services
- Updated logging statements in Sitemap.ts to enhance code clarity.
- Reformatted logger.info calls in IncomingRequestIngest, OpenTelemetryIngest, Probe, ProbeIngest, and ServerMonitorIngest to use multi-line formatting for better readability.
- Adjusted import statements in Probe to follow a consistent multi-line format.
2025-08-18 12:48:23 +01:00
Simon Larsen
4869172648 feat: Ensure home URL is prioritized in sitemap XML generation 2025-08-18 12:46:45 +01:00
Simon Larsen
1abd323b00 feat: Refactor sitemap generation and clean up unused imports 2025-08-18 12:45:06 +01:00
Simon Larsen
fa196a55cd feat: Update XML builder import and fix attribute assignment in URL set creation 2025-08-18 12:32:57 +01:00
Simon Larsen
29b4417aca feat: Update robots.txt to remove disallowed paths and simplify access 2025-08-18 12:31:57 +01:00
Simon Larsen
29c0d7e7e9 Merge branch 'master' of github.com:OneUptime/oneuptime 2025-08-16 15:04:23 +01:00
Simon Larsen
bfbe2437c0 feat: Add concurrency logging for various ingest services 2025-08-16 15:04:10 +01:00
Nawaz Dhandala
ca0e082daf refactor: Simplify mock setup for ProjectUserService in TeamMemberService tests 2025-08-15 19:06:41 +01:00
Nawaz Dhandala
1987b1c42e refactor: Add type annotations for domain and verificationText variables in DNS tests 2025-08-15 16:31:23 +01:00
Nawaz Dhandala
38be6edec3 feat: Improve DNS error handling with user-friendly messages and enhance tests for TXT and CNAME record verification 2025-08-15 16:29:41 +01:00
Simon Larsen
b80dda6d1a feat: Enhance DNS error handling with user-friendly messages and add tests for TXT and CNAME record verification 2025-08-15 16:23:56 +01:00
Simon Larsen
f65b2711ca feat: Change Repeat Policy Times column type from Boolean to Number 2025-08-15 11:49:57 +01:00
Simon Larsen
151c3d8c52 feat: Update logging scripts in package.json to include follow-logs command and adjust logs command 2025-08-15 11:48:16 +01:00
Simon Larsen
f17ae36dce docs: Update TerraformBuild.md to clarify restrictions on editing API key and oneuptime_url variables 2025-08-15 11:45:31 +01:00
Simon Larsen
777c6948ad feat: Add Terraform provider generation and installation instructions 2025-08-15 11:42:03 +01:00
Simon Larsen
1c6f9adbcb Merge branch 'release' of github.com:OneUptime/oneuptime into release 2025-08-14 13:52:14 +01:00
Simon Larsen
6d1b3f8568 Merge branch 'master' into release 2025-08-14 13:52:00 +01:00
Simon Larsen
11526816d1 feat: Add onCallScheduleId to UserNotificationRuleService and UserOnCallLogService for enhanced tracking 2025-08-14 13:44:45 +01:00
Nawaz Dhandala
bf190b6a32 refactor: Clean up code formatting and improve readability across multiple services and components 2025-08-14 13:10:43 +01:00
Simon Larsen
4736ea8227 feat: Refine extractSayMessagesFromCallRequest to focus on main messages for call summary 2025-08-14 12:59:38 +01:00
Simon Larsen
2cb4281fc3 feat: Update viewPageRoute in ExecutionLogsTable to use RouteUtil for dynamic route population 2025-08-14 12:47:16 +01:00
Simon Larsen
505ed980c3 feat: Improve sayMessage extraction from onInputCallRequest in CallService 2025-08-14 12:44:47 +01:00
Simon Larsen
a0b77c94b0 feat: Enhance message extraction in CallService to include onInputCallRequest messages 2025-08-14 12:37:46 +01:00
Simon Larsen
bb996ddaa2 feat: Add OnCall-related fields to notification services for enhanced tracking 2025-08-14 12:26:51 +01:00
Simon Larsen
a2f8e49bc1 feat: Enhance channel retrieval in Slack and MicrosoftTeams with error handling and fallback mechanism 2025-08-14 11:38:37 +01:00
Simon Larsen
4d6086b7fd feat: Enhance modal display in PushLogsTable with Markdown support for better formatting 2025-08-14 11:26:20 +01:00
Simon Larsen
f84a1db36a refactor: Improve modal text handling in CallLogsTable and remove unused Device Type column in PushLogsTable 2025-08-14 11:21:20 +01:00
Simon Larsen
3aa4214e54 fix: Update condition for rendering placeholder in Detail component for better null handling 2025-08-14 11:12:53 +01:00
Simon Larsen
2fb8341f9c feat: Add default value for actionType in WorkspaceNotificationLog for improved tracking 2025-08-13 21:10:09 +01:00
Simon Larsen
888d18d0a3 Merge branch 'master' of github.com:OneUptime/oneuptime 2025-08-13 21:01:53 +01:00
Simon Larsen
7f8270fac1 refactor: Add actionType to log in send message functionality for better tracking 2025-08-13 21:01:46 +01:00
Simon Larsen
318c1516cd Merge pull request #1981 from OneUptime/master
Release
2025-08-13 19:50:22 +01:00
Nawaz Dhandala
0343dcca93 refactor: Improve mail sending logic in UserService for better readability and consistency 2025-08-13 19:50:05 +01:00
Simon Larsen
7728e38029 refactor: Rename messageSummary to message in WorkspaceNotificationLog and related services for consistency 2025-08-13 19:49:36 +01:00
Simon Larsen
83a0efcbb9 refactor: Remove length restriction on joined strings in WorkspaceNotificationRuleService for improved readability 2025-08-13 19:42:17 +01:00
Simon Larsen
577ba9436e refactor: Simplify return logic in Service class and remove unnecessary TypeScript ignore comment in StatusPagesRoutes 2025-08-13 19:41:37 +01:00
Simon Larsen
a521f42417 Merge branch 'master' of github.com:OneUptime/oneuptime 2025-08-13 19:38:06 +01:00
Simon Larsen
1002616ab3 refactor: Remove redundant userId parameter from service calls for cleaner code 2025-08-13 19:38:02 +01:00
Nawaz Dhandala
13adbd2443 refactor: Update MigrationName1755109893911 for consistent formatting and include in index 2025-08-13 19:36:34 +01:00
Simon Larsen
12b82252f1 refactor: Remove unnecessary await from CallService.makeCall in UserNotificationRuleService and UserNotificationSettingService for improved performance 2025-08-13 19:35:43 +01:00
Simon Larsen
bdb9cb9b9a Merge branch 'master' of github.com:OneUptime/oneuptime 2025-08-13 19:32:27 +01:00
Simon Larsen
c5e19db669 refactor: Add deviceName column to PushNotificationLog and update default actionType in WorkspaceNotificationLog for improved tracking 2025-08-13 19:32:23 +01:00
Nawaz Dhandala
345bdf80e1 refactor: Update device handling in PushNotification to support new devices format and improve error handling 2025-08-13 19:29:55 +01:00
Nawaz Dhandala
c4bf5e5001 refactor: Enhance device handling in PushNotification and improve type safety in PushNotificationService and PushLogsTable 2025-08-13 19:25:27 +01:00
Nawaz Dhandala
a3685b3698 refactor: Update push notification handling to use devices array with optional device names for improved clarity 2025-08-13 19:20:27 +01:00
Nawaz Dhandala
388f6e3530 refactor: Add deviceName field to PushNotificationLog and update PushLogsTable for improved device tracking 2025-08-13 19:09:32 +01:00
Nawaz Dhandala
0493dabb93 refactor: Remove unnecessary whitespace in makeCall method for improved code consistency 2025-08-13 18:57:10 +01:00
Simon Larsen
2383b2d352 refactor: Replace userBelongsToTeamId with teamId in CallService, UserNotificationRuleService, and UserNotificationSettingService for consistency 2025-08-13 18:56:21 +01:00
Simon Larsen
c526c0e320 refactor: Replace userBelongsToTeamId and overridedByUserId with teamId in notification services for consistency 2025-08-13 18:45:42 +01:00
Nawaz Dhandala
7d607608b3 refactor: Enhance user display handling in notification log tables for improved clarity 2025-08-13 18:18:27 +01:00
Nawaz Dhandala
abece559ea refactor: Simplify onClick handlers in CallLogsTable and EmailLogsTable for improved readability 2025-08-13 18:09:46 +01:00
Simon Larsen
f9b0c499ed refactor: Simplify props in CallLogsTable, EmailLogsTable, PushLogsTable, SmsLogsTable, and WorkspaceLogsTable for improved readability and maintainability 2025-08-13 18:07:26 +01:00
Nawaz Dhandala
38594f5198 refactor: Align query prop indentation in PushLogsTable, SmsLogsTable, and WorkspaceLogsTable for consistency 2025-08-13 18:00:41 +01:00
Simon Larsen
8f08ec42c7 refactor: Update query prop type to use Query<BaseModel> for improved type safety in log tables 2025-08-13 17:59:59 +01:00
Simon Larsen
8c697149e3 refactor: Remove unused log components (CallLog, EmailLog, PushLog, SmsLog) 2025-08-13 17:57:56 +01:00
Simon Larsen
2ff3dee440 refactor: Remove CallLog, EmailLog, and SmsLog components to streamline dashboard settings 2025-08-13 17:52:07 +01:00
Nawaz Dhandala
c0e8193614 refactor: Update NotificationLogsTabs to use typed Query object for improved type safety 2025-08-13 17:42:14 +01:00
Nawaz Dhandala
c1d06fdae5 refactor: Simplify component formatting in NotificationLogsTabs and related pages 2025-08-13 17:40:33 +01:00
Simon Larsen
00ed20ea68 fix: Update import path for NotificationLogs component in ScheduledMaintenanceEventsRoutes 2025-08-13 17:40:02 +01:00
Simon Larsen
db93cc8841 Merge branch 'master' of github.com:OneUptime/oneuptime 2025-08-13 17:37:41 +01:00
Simon Larsen
374e8ecb4c refactor: Update NotificationLogsTabs to use query object for improved flexibility 2025-08-13 17:37:14 +01:00
Nawaz Dhandala
9ac17850a6 refactor: Simplify MailService sendMail calls and improve formatting in Notification components 2025-08-13 17:34:58 +01:00
Simon Larsen
a38d75b8f0 Refactor Notification Logs: Consolidate Tabs into NotificationLogsTabs Component
- Removed individual log components for Email, SMS, Call, Push, and Workspace from various pages.
- Replaced existing tab implementations with a unified NotificationLogsTabs component.
- Updated pages for Incidents, OnCallDuty, ScheduledMaintenanceEvents, StatusPages, and Settings to use the new NotificationLogsTabs.
- The NotificationLogsTabs component dynamically generates tabs based on the provided singular name and query key.
2025-08-13 17:32:47 +01:00
Simon Larsen
1bfdf90bca fix: Remove unnecessary userId parameter from signup request handling 2025-08-13 17:30:33 +01:00
Simon Larsen
49e3db9442 fix: Update icon in SideMenu components from List to Bell for improved clarity 2025-08-13 17:19:18 +01:00
Simon Larsen
83ab7030a4 fix: Update icon for SideMenuItem to improve visual consistency 2025-08-13 17:18:18 +01:00
Simon Larsen
76abf62917 feat: Add on-call policy related fields to notification services and API endpoints 2025-08-13 17:13:04 +01:00
Simon Larsen
a5c2f19846 fix: Remove unnecessary userId parameter from signup response handling 2025-08-13 15:59:01 +01:00
Simon Larsen
d37e783aeb fix: Remove unnecessary userId parameter from signup request handling 2025-08-13 15:58:17 +01:00
Simon Larsen
657ea0ec09 refactor: Update default messages in notification log tables to specify project context 2025-08-13 15:56:27 +01:00
Simon Larsen
e59f7a0a7f refactor: Update action type values in WorkspaceNotificationLog and related services for consistency 2025-08-13 15:55:02 +01:00
Nawaz Dhandala
35fc80de1e refactor: Rename "Initiated by" to "User" in notification log tables for consistency 2025-08-13 15:21:49 +01:00
Nawaz Dhandala
5e2dea40a3 refactor: Adjust formatting in Tabs component for improved readability 2025-08-13 15:18:30 +01:00
Nawaz Dhandala
a66f92e0b9 refactor: Simplify TabElement class assignment and clean up Tabs component structure 2025-08-13 15:16:58 +01:00
Nawaz Dhandala
a2ce7c9433 refactor: Remove unnecessary blank line before SettingsWorkspaceLog component 2025-08-13 15:02:49 +01:00
Nawaz Dhandala
eab1dc4b1b refactor: Replace SettingsWorkspaceLog component with direct import of WorkspaceLogsTable and remove unused WorkspaceLog file 2025-08-13 15:01:23 +01:00
Nawaz Dhandala
6939c70ed8 feat: Add actionType to WorkspaceNotificationLog and update OnCallDutyPolicyScheduleLayer defaults in migration 2025-08-13 14:58:11 +01:00
Simon Larsen
cce0dc8a45 feat: Add migration to include actionType in WorkspaceNotificationLog and update OnCallDutyPolicyScheduleLayer defaults 2025-08-13 14:53:05 +01:00
Simon Larsen
ec51d7b574 Merge branch 'master' of github.com:OneUptime/oneuptime 2025-08-13 14:38:16 +01:00
Simon Larsen
d4129cfa8e fix: Correct permission assignment for creating incident state timelines 2025-08-13 14:38:12 +01:00
Nawaz Dhandala
1ee7c092d3 Refactor Workspace Notification Log Service and related components
- Added missing commas in migration index for consistency.
- Cleaned up whitespace in WorkspaceNotificationLogService for better readability.
- Updated logChannelCreated and logUserInvited methods to improve code clarity.
- Enhanced button press logging in Slack actions for better error handling.
- Standardized formatting in ScheduledMaintenance and Incident Slack actions.
- Improved code structure in UptimeUtil for better readability.
- Refactored Notification components in Dashboard for consistent formatting.
- Updated WorkspaceLog component to include color typing for action types.
2025-08-13 14:30:51 +01:00
Simon Larsen
daf3b13e00 refactor: Remove unused resource association logic from user invitation process 2025-08-13 14:27:09 +01:00
Simon Larsen
5035319cc6 feat: Add action type column to WorkspaceLogsTable for improved visibility 2025-08-13 14:23:17 +01:00
Simon Larsen
3d53889d1e feat: Refactor notification log components to simplify structure and improve readability 2025-08-13 14:22:19 +01:00
Simon Larsen
65b610ebbb feat: Enhance WorkspaceLog with action type display and filtering options 2025-08-13 14:09:49 +01:00
Simon Larsen
30d1b43178 feat: Add WorkspaceNotificationActionType enum for notification actions 2025-08-13 14:08:35 +01:00
Simon Larsen
d0645f5dc2 feat: Add on-call duty policy fields and constraints to log tables 2025-08-13 13:42:31 +01:00
Simon Larsen
2274c14098 feat: Add On-Call Duty Policy and Schedule Notification Logs
- Enhanced PushNotificationLog, SmsLog, and WorkspaceNotificationLog models to include relationships with OnCallDutyPolicy, OnCallDutyPolicyEscalationRule, OnCallDutyPolicySchedule, and Team.
- Introduced new NotificationLogs components for OnCallDutyPolicy and OnCallDutySchedule, allowing users to view logs for Email, SMS, Call, Push, and Workspace notifications.
- Updated SideMenu components to include links to the new Notification Logs pages for both OnCallDutyPolicy and OnCallDutySchedule.
- Added routes and page mappings for the new Notification Logs views in OnCallDutyRoutes and PageMap.
2025-08-13 13:40:32 +01:00
Simon Larsen
d1b4d3867a feat: Enhance event overlap checks for today's events in DayUptimeGraph and UptimeUtil 2025-08-13 13:18:08 +01:00
Simon Larsen
04d4712c81 feat: Add Notification Logs section and related components for scheduled maintenance events 2025-08-13 12:58:42 +01:00
Nawaz Dhandala
9c2d2b658b refactor: Simplify event overlap check for today's events in DayUptimeGraph 2025-08-13 12:37:00 +01:00
Nawaz Dhandala
fdb1444dc8 fix: Specify type for loop index in push log creation for clarity 2025-08-13 12:24:55 +01:00
Nawaz Dhandala
39f724b77f fix: Make 'name' property public in MigrationName class for consistency 2025-08-13 12:24:08 +01:00
Nawaz Dhandala
dbfa153209 feat: Add userId tracking to email notifications and enhance logging across services 2025-08-13 12:23:45 +01:00
Simon Larsen
1c8739237f feat: Add userId tracking to email notifications across various services 2025-08-13 12:13:19 +01:00
Simon Larsen
26cdaacf6b feat: Add AlertDescription lazy import and route for alert view description 2025-08-13 11:55:28 +01:00
Simon Larsen
bbc8f0c680 feat: Add userId field to various services for enhanced tracking and logging 2025-08-12 21:54:51 +01:00
Simon Larsen
61dd8e9202 feat: Add userId field to notification logs and services for tracking user actions 2025-08-12 21:33:29 +01:00
Simon Larsen
6e17832239 feat: Enhance PushNotificationService with detailed logging and error handling 2025-08-12 21:00:30 +01:00
Simon Larsen
6460827c4c refactor: Remove Thread ID column from WorkspaceLogsTable 2025-08-12 20:40:22 +01:00
Simon Larsen
7bfd810b73 feat: Integrate MarkdownViewer for modal description in WorkspaceLogsTable 2025-08-12 19:46:18 +01:00
Simon Larsen
8b17217778 refactor: Remove redundant error logging for missing threads in workspace notification service 2025-08-12 19:44:24 +01:00
Simon Larsen
19a86e9683 fix: Change type of elk variable to any in ServiceDependencyGraph component 2025-08-12 19:23:10 +01:00
Simon Larsen
d2fd46db50 Merge branch 'master' of github.com:OneUptime/oneuptime 2025-08-12 17:39:52 +01:00
Simon Larsen
26cfbd07cb feat: Add server names hash tuning options to nginx configuration 2025-08-12 17:39:47 +01:00
Nawaz Dhandala
33992984e2 fix: Correct indentation for metrics endpoint exposure in WorkersFeatureSet 2025-08-12 17:38:38 +01:00
Nawaz Dhandala
e295c19b19 feat: Implement KEDA autoscaling for worker service with metrics endpoint 2025-08-12 17:37:13 +01:00
Nawaz Dhandala
51d42c8436 feat: Update no items message in Notification Logs tables to include pluralization 2025-08-12 14:57:25 +01:00
Nawaz Dhandala
50f16d0fdc Refactor Notification Logs Tables and Related Components
- Updated EmailLogsTable, PushLogsTable, and SmsLogsTable components for improved readability and consistency in code formatting.
- Enhanced the structure of props in various components to maintain uniformity.
- Refactored the return statements in several components to use consistent formatting.
- Cleaned up unnecessary whitespace and improved the organization of imports across multiple files.
- Adjusted the breadcrumb and route mapping for better clarity and maintainability.
2025-08-11 16:48:29 +01:00
Nawaz Dhandala
726ab4d7c0 Merge branch 'master' of https://github.com/OneUptime/oneuptime 2025-08-11 16:47:55 +01:00
Nawaz Dhandala
1461dd0164 feat: Add source and target positions to nodes in ServiceDependencyGraph 2025-08-11 16:47:54 +01:00
Simon Larsen
5ce1a782b3 refactor: Remove 'Subject' column from EmailLogsTable component 2025-08-11 14:41:59 +01:00
Simon Larsen
741eaec1d3 feat: Add modal functionality to Notification Logs tables for viewing details 2025-08-11 14:38:35 +01:00
Simon Larsen
2d8c931641 refactor: Standardize formatting in EmailLogsTable component 2025-08-11 14:26:05 +01:00
Simon Larsen
4db479958b refactor: Remove unused imports and simplify ServiceCatalogPage component 2025-08-11 14:26:00 +01:00
Simon Larsen
8c825f1498 refactor: Update imports in ServiceDependencyGraph and clean up AnnouncementView component 2025-08-11 14:10:48 +01:00
Simon Larsen
25426992be feat: Add route for deleting announcement view in Status Pages 2025-08-11 14:01:25 +01:00
Simon Larsen
861a72d194 feat: Enhance Notification Logs tables with singular and plural naming support 2025-08-11 13:54:54 +01:00
Simon Larsen
0857cebcfc Refactor Notification Logs: Replace ModelTable with dedicated components for Call, Email, Push, SMS, and Workspace logs
- Introduced new components: CallLogsTable, EmailLogsTable, PushLogsTable, SmsLogsTable, and WorkspaceLogsTable.
- Updated NotificationLogsSms, NotificationLogsEmail, NotificationLogsCall, NotificationLogsPush, and NotificationLogsWorkspace to utilize the new components.
- Removed redundant column and filter definitions from the individual log pages, centralizing them in the new table components.
- Enhanced code readability and maintainability by separating concerns and reducing duplication.
2025-08-11 13:28:50 +01:00
Nawaz Dhandala
975af2c22a Merge branch 'master' of https://github.com/OneUptime/oneuptime 2025-08-11 13:16:02 +01:00
Nawaz Dhandala
98d15f91b0 feat: Integrate ELK for layout management in ServiceDependencyGraph and add typings for elkjs 2025-08-11 13:15:59 +01:00
Simon Larsen
6d55b59a21 Merge branch 'master' of github.com:OneUptime/oneuptime 2025-08-11 12:47:35 +01:00
Simon Larsen
8a27651d84 feat: Add Notification Logs section and related components for Status Pages 2025-08-11 12:45:05 +01:00
Nawaz Dhandala
6a35dffcb5 feat: Update ServiceDependencyGraph to enhance edge styling and add read-only view styles 2025-08-11 12:34:49 +01:00
Nawaz Dhandala
1e4c46bb3f feat: Enhance ServiceDependencyGraph with luminance-based text color and update SideMenu icon 2025-08-11 12:33:11 +01:00
Simon Larsen
fe44c0fde4 feat: Add migration for WorkspaceNotificationLog table and related constraints 2025-08-11 12:09:54 +01:00
Nawaz Dhandala
089f612ec4 refactor: Improve code formatting and consistency across various components and routes 2025-08-11 12:01:59 +01:00
Simon Larsen
8dbd9e7430 Merge pull request #1982 from OneUptime/service-catalog-dependency
feat: Implement Service Catalog Dependency Graph and associated layou…
2025-08-11 11:58:53 +01:00
Simon Larsen
66eb9eede0 feat: Update notification log routes and components for consistency across Alerts, Incidents, Settings, and Status Pages 2025-08-11 11:57:02 +01:00
Nawaz Dhandala
e8db6fcb7f feat: Implement Service Catalog Dependency Graph and associated layout components 2025-08-11 11:47:08 +01:00
Simon Larsen
d9e7f44590 feat: Consolidate notification logs into unified components across Alerts, Incidents, Settings, and Status Pages 2025-08-10 15:20:14 +01:00
Simon Larsen
b7df0a7d05 Merge branch 'master' of github.com:OneUptime/oneuptime 2025-08-10 14:51:25 +01:00
Nawaz Dhandala
4101954862 refactor: Enhance type definitions and improve message summary functions in WorkspaceNotificationRuleService 2025-08-10 14:51:09 +01:00
Nawaz Dhandala
d68e4737e7 refactor: Improve code formatting and readability across multiple components 2025-08-10 14:46:44 +01:00
Nawaz Dhandala
bfb80388a0 Merge branch 'master' of https://github.com/OneUptime/oneuptime 2025-08-10 14:45:52 +01:00
Nawaz Dhandala
1beb96345b feat: Add Workspace Logs functionality across various pages
- Introduced Workspace Logs link in the SideMenu for Alerts, Incidents, Settings, and Status Pages.
- Created new routes for viewing Workspace Logs in Alerts, Incidents, Settings, and Status Pages.
- Added WorkspaceNotificationLog model to handle logs related to messages sent to Slack and Microsoft Teams.
- Implemented WorkspaceNotificationLogService for managing log entries.
- Developed UI components for displaying Workspace Logs in Alerts, Incidents, Settings, and Status Pages.
- Added filtering options for Workspace Logs based on status and workspace type.
2025-08-10 14:45:50 +01:00
Simon Larsen
1eb2af737d feat: Add routes for viewing alert, incident, and announcement logs 2025-08-10 14:28:00 +01:00
Simon Larsen
18f756a29b Merge branch 'master' of github.com:OneUptime/oneuptime 2025-08-10 14:21:36 +01:00
Simon Larsen
002f98720c feat: Add route for Push Logs settings page 2025-08-10 14:21:32 +01:00
Nawaz Dhandala
fdf5aacc2b Refactor notification log components for improved readability and consistency
- Updated AlertPushLogs, AlertSmsLogs, IncidentCallLogs, IncidentSmsLogs, IncidentEmailLogs, and related components to enhance code formatting and structure.
- Improved the layout of column definitions and filter options for better readability.
- Ensured consistent use of object destructuring and formatting across components.
- Added missing line breaks and indentation for clarity.
- Updated query and cardProps formatting for consistency across components.
2025-08-10 13:28:44 +01:00
Simon Larsen
574cad6806 feat: Add migration for PushNotificationLog table and related constraints 2025-08-10 13:27:28 +01:00
Simon Larsen
34c4ae947b feat: Add Push Notification Logs functionality
- Introduced PushNotificationLog model to track push notifications sent to users.
- Added permissions for reading push logs in the Permission enum.
- Updated various side menus to include links to Push Logs in Alerts, Incidents, and Settings.
- Created routes for viewing Push Logs in Alerts, Incidents, and Status Pages.
- Implemented UI components for displaying Push Logs in respective pages.
- Added filtering and column configuration for Push Logs tables.
- Integrated PushStatus enum to manage the status of push notifications.
- Implemented PushNotificationLogService for database interactions related to push logs.
2025-08-10 13:25:53 +01:00
Simon Larsen
8d9fc46506 feat: Add migration for new fields and constraints in CallLog, EmailLog, and SmsLog 2025-08-09 22:50:27 +01:00
Simon Larsen
c41fbefdcb feat: Add status page announcement logs to Call, Email, and SMS logs
- Enhanced CallLog, EmailLog, and SmsLog models to include relationships with StatusPageAnnouncement.
- Updated CallService, MailService, and SmsService to handle statusPageAnnouncementId.
- Introduced AnnouncementViewLayout and AnnouncementSideMenu for better navigation.
- Created dedicated components for viewing Email, SMS, and Call logs related to announcements.
- Added routes for viewing logs and deleting announcements.
- Implemented filtering and display of logs in the UI.
2025-08-09 22:47:24 +01:00
Simon Larsen
3e47051233 Add notification logs for alerts and incidents
- Implemented Email, SMS, and Call logs for alerts in the dashboard.
- Created corresponding components for viewing logs: AlertEmailLogs, AlertSmsLogs, AlertCallLogs.
- Added routes for accessing alert notification logs.
- Enhanced the SideMenu to include links to notification logs for alerts.
- Implemented Email, SMS, and Call logs for incidents in the dashboard.
- Created corresponding components for viewing logs: IncidentEmailLogs, IncidentSmsLogs, IncidentCallLogs.
- Added routes for accessing incident notification logs.
- Enhanced the SideMenu to include links to notification logs for incidents.
- Updated various services to include statusPageId and incidentId where necessary for better tracking.
2025-08-09 21:09:57 +01:00
Simon Larsen
8219f44708 fix: Adjust job removal counts in Queue class to manage Redis bloat more effectively 2025-08-08 22:05:54 +01:00
Simon Larsen
59e6505aa3 Merge branch 'master' of github.com:OneUptime/oneuptime 2025-08-08 22:03:46 +01:00
Simon Larsen
1f971b932a feat: Implement initial cleanup for legacy jobs in Queue class to manage memory and prevent Redis bloat 2025-08-08 22:03:43 +01:00
Nawaz Dhandala
d0e12ae86f fix: Adjust indentation for improved readability in Config and Routes files 2025-08-08 21:56:35 +01:00
Simon Larsen
cd11a450cd feat: Introduce configurable concurrency settings for ingest workers in environment variables 2025-08-08 21:52:49 +01:00
Simon Larsen
c0259fc041 feat: Add concurrency setting for OpenTelemetry Ingest worker and update related configurations 2025-08-08 21:21:53 +01:00
Simon Larsen
db1f5a29bb fix: Update notification handling to mark announcements and public notes as Skipped when no related entities are found 2025-08-08 19:17:55 +01:00
Simon Larsen
a9ecaf2dc8 fix: Update incident handling to mark subscriber notifications as Skipped when no monitors are attached 2025-08-08 19:14:52 +01:00
Nawaz Dhandala
138aad596a Refactor logging statements for improved readability and consistency across worker jobs; ensure all debug messages are formatted uniformly. Update migration index to include trailing comma for consistency. Simplify route initialization in Workers feature set. 2025-08-08 18:53:40 +01:00
Simon Larsen
2577b339aa fix: Reorder route initialization to ensure worker routes are registered before default catch-alls 2025-08-08 18:44:09 +01:00
Simon Larsen
80e7731cca fix: Remove ClusterKeyAuthorization middleware from inspector route 2025-08-08 18:13:03 +01:00
Simon Larsen
9da7b258f9 feat: Add migration to rename subscriber notification fields and update database schema 2025-08-08 17:48:07 +01:00
Simon Larsen
0ec3b1aa39 fix: Remove obsolete migration files and update index to reflect changes 2025-08-08 17:42:27 +01:00
Simon Larsen
7a9bb22813 fix: Add UpdateSubscriberNotificationStatusToEnum migration for subscriber notification status updates 2025-08-08 17:34:12 +01:00
Simon Larsen
92550ac7d6 fix: Remove unnecessary createdAt condition in subscriber notification jobs and add debug logging for better traceability 2025-08-08 16:56:47 +01:00
Nawaz Dhandala
101df5b9b7 fix: Refactor error message in StatusPageDelete for better readability and clarity 2025-08-08 15:17:54 +01:00
Simon Larsen
c3d7672935 fix: Remove unnecessary NOT NULL constraint on subscriberNotificationStatusOnIncidentCreated in Incident table migration 2025-08-08 15:08:03 +01:00
Simon Larsen
859c6378af fix: Update Dockerfile to use apt-get for installing bash and curl 2025-08-08 14:05:51 +01:00
Simon Larsen
620979eab2 fix: Correct typo in notification status message 2025-08-08 13:37:52 +01:00
Simon Larsen
0aa1c51efa fix: Update error message in StatusPageDelete to clarify environment variable setup for Docker and Helm 2025-08-08 13:37:13 +01:00
Simon Larsen
1985e9fc25 fix: Update error message in StatusPageDelete to include environment variable requirement for custom domains 2025-08-08 13:35:27 +01:00
Simon Larsen
ba4093838b fix: Add type assertion for categoryColors in ChartLegend to ensure correct type usage 2025-08-07 22:31:54 +01:00
Nawaz Dhandala
4bd7902afe fix: Refactor ResourceGenerator to improve type annotations and code clarity 2025-08-07 22:05:47 +01:00
Simon Larsen
5c300ed513 feat: Enhance update method to conditionally include fields based on change detection 2025-08-07 22:03:21 +01:00
Simon Larsen
c4a50e853c feat: Add logging for unauthorized update attempts in ColumnPermissions 2025-08-07 21:29:13 +01:00
Simon Larsen
20c1f13876 fix: Exclude computed fields from default empty list assignment in ResourceGenerator 2025-08-07 21:22:48 +01:00
Simon Larsen
09426ed6be Merge branch 'master' of github.com:OneUptime/oneuptime 2025-08-07 21:11:42 +01:00
Nawaz Dhandala
675a031ee6 fix: Correct return type of getYAxisDomain function to match expected output 2025-08-07 21:02:59 +01:00
Simon Larsen
92986ac1f8 feat: Add isDefaultValueColumn flag to Downtime Monitor Statuses field in StatusPage model 2025-08-07 17:38:07 +01:00
Simon Larsen
2b95d608dc fix: Remove redundant build command for darwin arm architecture 2025-08-07 16:55:43 +01:00
Nawaz Dhandala
2696071933 fix: Correct formatting in ProjectSSO and UserNotificationSetting models; update conditional logic in DatabaseService 2025-08-07 16:46:14 +01:00
Nawaz Dhandala
684a61b599 feat: Add default values for boolean fields in various database models 2025-08-07 16:45:33 +01:00
Nawaz Dhandala
633a89161e Merge branch 'master' of https://github.com/OneUptime/oneuptime 2025-08-07 15:03:02 +01:00
Nawaz Dhandala
fd24781783 refactor: Optimize onBarClick and shape rendering logic in BarChart component 2025-08-07 15:03:00 +01:00
Simon Larsen
903b13d515 feat: Handle null values for required fields in data processing 2025-08-07 14:48:01 +01:00
Simon Larsen
58a128a05e Merge branch 'master' of github.com:OneUptime/oneuptime 2025-08-07 14:45:51 +01:00
Simon Larsen
ec1d567813 feat: Set default values for status page configuration options 2025-08-07 14:45:48 +01:00
Nawaz Dhandala
0a53161eac refactor: Improve type annotations and code consistency across various components 2025-08-07 14:31:13 +01:00
Nawaz Dhandala
83b91af708 Refactor code for consistency and readability across various components
- Updated import statements for better formatting in multiple files.
- Added semicolons for consistency in BarChart and SparkChart components.
- Improved code readability by using braces for single-line if statements in various functions.
- Enhanced the structure of return statements for clarity in SubscriberNotificationStatus and other components.
- Refactored map functions to use explicit return statements for better readability.
- Cleaned up whitespace and formatting in multiple files for a more uniform code style.
2025-08-07 13:55:08 +01:00
Simon Larsen
3fefee8725 feat: Enhance notification status handling and improve UI components 2025-08-07 13:49:53 +01:00
Simon Larsen
9b2cc7d377 feat: Update create permissions for Incident and Scheduled Maintenance models 2025-08-07 12:52:00 +01:00
Simon Larsen
1fb71ed2e3 feat: Implement subscriber notification status handling across multiple services 2025-08-07 12:46:40 +01:00
Simon Larsen
94a5abdb31 feat: Add SparkChart component with Area, Line, and Bar chart implementations 2025-08-07 11:52:44 +01:00
Simon Larsen
73f4559943 feat: Refactor BarChart component for improved type safety and event handling 2025-08-07 11:47:59 +01:00
Simon Larsen
9c3c6ee4e9 feat: Add BarChart component for enhanced data visualization 2025-08-07 11:29:19 +01:00
Simon Larsen
56743214a0 feat: Update button style in SubscriberNotificationStatus for improved UI consistency 2025-08-07 11:29:14 +01:00
Simon Larsen
9136c6d40e feat: Update color imports in SubscriberNotificationStatus for consistent branding 2025-08-07 11:23:46 +01:00
Simon Larsen
920a9baee9 feat: Update ConfirmModal behavior in SubscriberNotificationStatus for improved user interaction 2025-08-07 11:20:23 +01:00
Simon Larsen
1c4aad2d81 feat: Add SubscriberNotificationStatus component to IncidentViewStateTimeline and ScheduledMaintenanceDelete for enhanced notification display 2025-08-07 11:17:34 +01:00
Simon Larsen
8a4644922a feat: Add textColor prop to IconText component and update SubscriberNotificationStatus to utilize it 2025-08-07 11:04:20 +01:00
Simon Larsen
c3f4b7d3d4 feat: Enhance SubscriberNotificationStatus component with IconText and ConfirmModal for improved status display and user interaction 2025-08-07 10:54:10 +01:00
Simon Larsen
6f7c0814ee feat: Implement IconText component and refactor CheckboxViewer to use it 2025-08-07 10:49:21 +01:00
Nawaz Dhandala
77cd3fc4c0 refactor: Enhance type definitions for handleResendNotification and getNotificationStatusInfo across components 2025-08-06 16:02:57 +01:00
Nawaz Dhandala
e7cbc3d739 Refactor notification status assignments and improve code readability in incident and scheduled maintenance notification jobs
- Updated subscriber notification status assignments for better readability by breaking long lines.
- Added handling for non-visible scheduled maintenance events to set status to Skipped.
- Improved error handling and logging for scheduled maintenance public notes and state timelines.
- Ensured consistent formatting and structure across notification jobs for clarity and maintainability.
2025-08-06 15:41:28 +01:00
Simon Larsen
6d14ea19b9 feat: Add subscriberNotificationStatusMessage to selectMoreFields in multiple components 2025-08-06 14:47:13 +01:00
Simon Larsen
1290d3b946 fix: Remove unnecessary className from subscriberNotificationStatusMessage in PublicNote component 2025-08-06 14:29:51 +01:00
Simon Larsen
c0c58546d0 feat: Integrate Tooltip for subscriberNotificationStatusMessage in SubscriberNotificationStatus component 2025-08-06 14:17:37 +01:00
Simon Larsen
6c5ef10606 feat: Update migration files to rename subscriberNotificationFailedReason to subscriberNotificationStatusMessage and adjust imports 2025-08-06 14:05:26 +01:00
Simon Larsen
ec4c6ff7c5 feat: Rename subscriberNotificationFailedReason to subscriberNotificationStatusMessage across models and update related components 2025-08-06 14:04:06 +01:00
Simon Larsen
616e6e43ab feat: Update access control permissions for Incident, Scheduled Maintenance, and related models 2025-08-06 13:03:55 +01:00
Simon Larsen
aa08cd904b feat: Update SCIMPage to use Route for documentation link instead of URL 2025-08-06 12:34:21 +01:00
Simon Larsen
fef1c1055c feat: Integrate SubscriberNotificationStatus component in Incident and Scheduled Maintenance views, replacing checkbox logic 2025-08-06 12:31:14 +01:00
Simon Larsen
22e33809f9 feat: Remove style prop from SubscriberNotificationStatus component usage across various views 2025-08-06 11:39:59 +01:00
Simon Larsen
eb8324a3c2 feat: Replace NotificationStatusPill with SubscriberNotificationStatus component across various views 2025-08-06 11:25:35 +01:00
Simon Larsen
fa6dedc9a1 feat: Add resend notification functionality to NotificationStatusPill and related components 2025-08-06 11:24:20 +01:00
Simon Larsen
099cd807bf feat: Remove unused notification status fields and components from Announcements, Incidents, and Scheduled Maintenance tables 2025-08-06 10:58:15 +01:00
Simon Larsen
5d0b010fc4 feat: Remove unused StatusPageSCIM from AllModelTypes 2025-08-05 21:56:55 +01:00
Simon Larsen
1fc421f92a feat: Refactor notification status handling with NotificationStatusPill component 2025-08-05 21:31:08 +01:00
Simon Larsen
14a14e2341 feat: Add .claude/settings.local.json to .gitignore 2025-08-05 21:09:10 +01:00
Simon Larsen
ab23cca264 feat: Remove unused ActionButtonSchema import from multiple components 2025-08-05 20:42:06 +01:00
Simon Larsen
678a961fb9 feat: Add migration for updating subscriber notification status to the migration index 2025-08-05 18:35:49 +01:00
Simon Larsen
c2e458f035 feat: Rename notification failure reason columns for consistency and update types to text 2025-08-05 18:35:14 +01:00
Simon Larsen
4daf17dc8c feat: Add SCIM documentation for automated user provisioning and deprovisioning 2025-08-05 18:04:59 +01:00
Simon Larsen
842aa4b88d feat: Rename notification failure reason fields to subscriberNotificationFailedReason for consistency across incident and scheduled maintenance jobs 2025-08-05 18:03:33 +01:00
Simon Larsen
fd51142693 feat: Update notification failure reason fields to subscriberNotificationFailedReason and change type to VeryLongText 2025-08-05 17:57:17 +01:00
Simon Larsen
e0ddf80aa6 feat: Add migration for updating subscriber notification status and handling failure reasons in Incident and ScheduledMaintenance tables 2025-08-05 17:53:06 +01:00
Simon Larsen
e8d55164c6 feat: Update notification handling for subscribers across various jobs
- Introduced a new enum `StatusPageSubscriberNotificationStatus` to manage notification statuses (Skipped, Pending, InProgress, Success, Failed).
- Updated `SendNotificationToSubscribers` jobs for Announcements, Incidents, Scheduled Maintenance, and Public Notes to utilize the new notification status system.
- Added logic to mark notifications as Skipped if they should not be sent, and to update the status to InProgress when notifications are being processed.
- Implemented success and failure handling for notifications, updating the respective status and logging errors as needed.
- Modified database schema to replace old boolean notification flags with the new enum-based status fields, ensuring backward compatibility with existing records.
- Added migration script to handle the transition of existing records to the new notification status system.
2025-08-05 17:49:28 +01:00
Nawaz Dhandala
5cd8795e7a refactor: Clean up code formatting and improve readability in SCIM and StatusPageSCIM files 2025-08-05 12:49:04 +01:00
Simon Larsen
aebf7a4f2e feat: Remove Groups Endpoint display and enhance user identifier information in SCIM settings 2025-08-05 12:48:10 +01:00
Simon Larsen
3ef093eee1 feat: Add password generation for private users and update SideMenu icon 2025-08-05 12:46:09 +01:00
Simon Larsen
b4c530a6a5 feat: Update SCIM links and enhance HiddenText component for copy functionality 2025-08-05 12:37:34 +01:00
Simon Larsen
166228cad5 feat: Enhance SCIM configuration handling and update UI elements 2025-08-05 12:28:48 +01:00
Simon Larsen
1eb95c71fe feat: Add StatusPageSCIM API integration to BaseAPIFeatureSet 2025-08-05 12:16:24 +01:00
Simon Larsen
56f33f256b feat: Increase concurrency limit for ingest job processing to improve throughput 2025-08-05 11:13:49 +01:00
Simon Larsen
42afd164b7 feat: Increase concurrency limit for telemetry job processing to improve performance 2025-08-05 11:11:12 +01:00
Nawaz Dhandala
0796166a55 fix: Correct syntax errors in navigation group array in Nav.ts 2025-08-05 11:07:45 +01:00
Nawaz Dhandala
170bfa8515 refactor: Improve code formatting and consistency across SCIM-related files 2025-08-05 11:03:00 +01:00
Simon Larsen
2f517d8dcc feat: Update SCIM documentation URLs to use environment configuration 2025-08-05 11:00:18 +01:00
Simon Larsen
cb5c4dce45 feat: Add SCIM API documentation for user provisioning and deprovisioning 2025-08-05 10:52:57 +01:00
Simon Larsen
d9abeda60d feat: Refactor SCIM utility functions for improved modularity and logging 2025-08-05 10:33:38 +01:00
Simon Larsen
15c4c89310 feat: Add StatusPageSCIM model and related database migration
- Implemented StatusPageSCIM model with necessary fields and access controls.
- Created migration script to set up StatusPageSCIM table in the database.
- Developed StatusPageSCIMService for handling SCIM configurations, including bearer token generation.
- Added SCIM management page in the dashboard with functionalities for creating, editing, and resetting bearer tokens.
2025-08-05 10:07:24 +01:00
Simon Larsen
8c1d5652f4 feat: Change button style type for resetting bearer token to outline 2025-08-04 22:14:31 +01:00
Nawaz Dhandala
fbf87cf8d4 refactor: Add type annotations to formatUserForSCIM and resetBearerToken functions for improved type safety 2025-08-04 22:10:06 +01:00
Nawaz Dhandala
1c12ad94dd fix: Add type annotations for improved type safety in SCIM and Metrics modules 2025-08-04 22:07:10 +01:00
Nawaz Dhandala
aa09bab7c9 Refactor SCIM migrations and models; update formatting and improve readability
- Added missing comma in AllModelTypes array in Index.ts.
- Refactored MigrationName1754304193228 to improve query formatting and readability.
- Refactored MigrationName1754315774827 for consistency in formatting.
- Updated migration index file to include new migration.
- Standardized string quotes in Queue.ts for consistency.
- Cleaned up SCIMAuthorization.ts by removing unnecessary whitespace and improving log formatting.
- Refactored StartServer.ts to standardize content-type header handling.
- Improved formatting in SCIM.tsx for better readability and consistency.
- Refactored Metrics.ts to standardize queueSize extraction and type checking.
- Enhanced Probe.ts logging for clarity and consistency.
2025-08-04 21:36:11 +01:00
Simon Larsen
f7d1975ab0 feat: Add debug logging for parsing names from SCIM users 2025-08-04 21:35:30 +01:00
Simon Larsen
99c9a591cb feat: Refactor SCIM user handling to improve name parsing and team operations 2025-08-04 21:29:16 +01:00
Simon Larsen
c956d01789 feat: Enhance user name handling in SCIM responses by parsing full names into given and family names 2025-08-04 21:22:01 +01:00
Simon Larsen
17c829869b feat: Implement user activation handling by adding users to configured teams 2025-08-04 21:18:03 +01:00
Simon Larsen
d65e91a912 feat: Enhance SCIM user update logging and handle user deactivation by removing from teams 2025-08-04 21:17:28 +01:00
Simon Larsen
39710ba9b0 feat: Enhance SCIM user update and delete logging, and improve team removal logic 2025-08-04 21:12:49 +01:00
Simon Larsen
8c70a4dfae feat: Update SCIM user handling to improve pagination and remove duplicates 2025-08-04 18:00:20 +01:00
Simon Larsen
ff99055594 feat: Refactor SCIM endpoints to enhance logging and improve user query handling 2025-08-04 17:44:23 +01:00
Simon Larsen
f01cc2fd71 feat: Enhance logging for SCIM requests and responses across various endpoints 2025-08-04 17:34:28 +01:00
Simon Larsen
49b43593b1 feat: Add middleware to handle SCIM content type before JSON parsing 2025-08-04 17:25:01 +01:00
Simon Larsen
e293ffd0eb feat: Remove isEnabled column from ProjectSCIM and update related services and migrations 2025-08-04 14:58:25 +01:00
Simon Larsen
b62a5e7722 feat: Add functionality to reset Bearer Token with confirmation modals 2025-08-04 14:47:45 +01:00
Simon Larsen
8f8ba0abb8 feat: Enhance SCIM middleware logging and update SCIM page state management 2025-08-04 13:01:09 +01:00
Simon Larsen
5525556b54 feat: Rename ProjectScima to ProjectSCIM and update imports 2025-08-04 12:28:09 +01:00
Simon Larsen
669066b70a feat: Implement ProjectSCIM model and SCIM page functionality 2025-08-04 12:27:48 +01:00
Simon Larsen
76d2abed08 fix: Update SCIM endpoint URLs to include versioning 2025-08-04 12:01:50 +01:00
Simon Larsen
a6c18b3f21 fix: Remove HTTP_PROTOCOL from SCIM endpoint URLs in SCIMPage component 2025-08-04 12:01:24 +01:00
Simon Larsen
955ea7bc31 feat: Restore ProjectSCIM service with bearer token generation logic 2025-08-04 11:58:18 +01:00
Simon Larsen
45719d4656 feat: Reintroduce ProjectSCIM service with bearer token generation logic 2025-08-04 11:58:07 +01:00
Simon Larsen
796c94a261 fix: Correct import casing for ProjectSCIM across multiple files 2025-08-04 11:46:56 +01:00
Simon Larsen
d2fe822cb7 feat: Integrate ProjectSCIM model and service into the Base API feature set 2025-08-04 11:44:02 +01:00
Simon Larsen
289a369eab feat: Add migration for ProjectSCIM and ProjectScimTeam tables with foreign key constraints 2025-08-04 11:43:44 +01:00
Simon Larsen
6f07e3e119 feat: Update SCIM API endpoints to include versioning in the URL 2025-08-04 11:19:17 +01:00
Simon Larsen
8cdc1e9faf feat: Add SCIM API endpoints and middleware for user management and configuration 2025-08-04 11:09:52 +01:00
Simon Larsen
d4609a84ef feat: Implement Project SCIM service with bearer token generation 2025-08-04 10:15:34 +01:00
Simon Larsen
eb4a91a598 feat: Add SCIM settings page and routing to the dashboard 2025-08-04 10:14:47 +01:00
Simon Larsen
5bea404d6c feat: Add SCIM API integration to Identity feature set 2025-08-04 10:13:44 +01:00
Simon Larsen
df3f8b6a74 feat: Add optional stackTrace field to job data structures for enhanced error tracking 2025-08-03 12:59:51 +01:00
Simon Larsen
0c9d2c821a feat: Add advanced horizontal pod autoscaler configuration for improved scaling behavior 2025-08-02 13:05:05 +01:00
Simon Larsen
ba49aaf0c3 fix: Skip probe offline email notifications when billing is enabled 2025-08-02 12:36:50 +01:00
Simon Larsen
6ea5ad7fe8 fix: Update nextPingAt calculation to use a 2-minute offset for improved timing accuracy 2025-08-02 11:42:01 +01:00
Simon Larsen
962866d109 fix: Improve queue size extraction and handling in metrics endpoint 2025-08-01 20:58:58 +01:00
Simon Larsen
115216561c feat: Add ports configuration for OneUptime probe service 2025-08-01 20:36:30 +01:00
Simon Larsen
f709c90cc4 fix: Update probe port handling in KEDA ScaledObjects for improved configuration 2025-08-01 20:21:41 +01:00
Simon Larsen
d7f01b0189 fix: Update default port value in probe template for better configuration handling 2025-08-01 20:19:31 +01:00
Simon Larsen
c3eaa8995c fix ports 2025-08-01 20:19:10 +01:00
Simon Larsen
53b482b9f3 refactor: Update Helm templates to use new port structure in values.yaml 2025-08-01 20:13:30 +01:00
Simon Larsen
d52670f39c refactor: Update Helm templates to use new port structure in values.yaml 2025-08-01 18:22:05 +01:00
Simon Larsen
fdc1332b9e Merge branch 'master' of github.com:OneUptime/oneuptime 2025-08-01 16:17:09 +01:00
Simon Larsen
a937416663 fix: Update autoscaler condition to prevent conflicts with KEDA configuration 2025-08-01 16:17:05 +01:00
Nawaz Dhandala
546d41da81 fix: Clean up formatting and ensure consistent return structure in metrics endpoints 2025-08-01 16:13:05 +01:00
Simon Larsen
c4c6793b29 feat: Implement KEDA autoscaling configuration for probes and add metrics endpoints 2025-08-01 15:38:04 +01:00
Simon Larsen
c894b112e6 fix: Await monitorResource call to ensure proper error handling in incoming request processing 2025-08-01 14:34:17 +01:00
Simon Larsen
304baf1bb4 fix: Await monitorResource call to ensure proper error handling in probe response processing 2025-08-01 14:33:17 +01:00
Simon Larsen
9adea6b1ba feat: Remove Helm annotations for post-install and post-upgrade hooks from templates 2025-08-01 14:01:04 +01:00
Simon Larsen
5498521e02 feat: Add Helm annotations for post-install and post-upgrade hooks 2025-08-01 13:47:52 +01:00
Simon Larsen
9e97c6ddbc feat: Update autoscaler conditions for fluent-ingest, incoming-request-ingest, probe-ingest, and server-monitor-ingest templates 2025-08-01 13:23:39 +01:00
Nawaz Dhandala
63272e09f8 refactor: Simplify function parameter formatting and improve readability in various files 2025-08-01 10:45:55 +01:00
842 changed files with 63119 additions and 11573 deletions

17
.github/instructions/instructions.md vendored Normal file
View File

@@ -0,0 +1,17 @@
---
applyTo: '**'
---
# Building and Compiling
If you would like to compile or build any project. Please cd into the directory and run the following command:
```
npm run compile
```
Ths will make sure there are no type / syntax errors.
# Typescript Types.
Please do not use "any" types. Please create proper types where required.

View File

@@ -19,11 +19,19 @@ jobs:
uses: actions/checkout@v4
- name: Preinstall
run: npm run prerun
uses: nick-fields/retry@v3
with:
timeout_minutes: 10
max_attempts: 3
command: npm run prerun
# build image for accounts service
- name: build docker image
run: sudo docker build -f ./Accounts/Dockerfile .
uses: nick-fields/retry@v3
with:
timeout_minutes: 45
max_attempts: 3
command: sudo docker build -f ./Accounts/Dockerfile .
docker-build-isolated-vm:
runs-on: ubuntu-latest
@@ -34,11 +42,19 @@ jobs:
uses: actions/checkout@v4
- name: Preinstall
run: npm run prerun
uses: nick-fields/retry@v3
with:
timeout_minutes: 10
max_attempts: 3
command: npm run prerun
# build image for accounts service
- name: build docker image
run: sudo docker build -f ./IsolatedVM/Dockerfile .
uses: nick-fields/retry@v3
with:
timeout_minutes: 45
max_attempts: 3
command: sudo docker build -f ./IsolatedVM/Dockerfile .
docker-build-home:
runs-on: ubuntu-latest
@@ -49,11 +65,19 @@ jobs:
uses: actions/checkout@v4
- name: Preinstall
run: npm run prerun
uses: nick-fields/retry@v3
with:
timeout_minutes: 10
max_attempts: 3
command: npm run prerun
# build image for accounts service
- name: build docker image
run: sudo docker build -f ./Home/Dockerfile .
uses: nick-fields/retry@v3
with:
timeout_minutes: 45
max_attempts: 3
command: sudo docker build -f ./Home/Dockerfile .
docker-build-worker:
runs-on: ubuntu-latest
@@ -64,11 +88,19 @@ jobs:
uses: actions/checkout@v4
- name: Preinstall
run: npm run prerun
uses: nick-fields/retry@v3
with:
timeout_minutes: 10
max_attempts: 3
command: npm run prerun
# build image for accounts service
- name: build docker image
run: sudo docker build -f ./Worker/Dockerfile .
uses: nick-fields/retry@v3
with:
timeout_minutes: 45
max_attempts: 3
command: sudo docker build -f ./Worker/Dockerfile .
docker-build-workflow:
runs-on: ubuntu-latest
@@ -79,11 +111,19 @@ jobs:
uses: actions/checkout@v4
- name: Preinstall
run: npm run prerun
uses: nick-fields/retry@v3
with:
timeout_minutes: 10
max_attempts: 3
command: npm run prerun
# build image for accounts service
- name: build docker image
run: sudo docker build -f ./Workflow/Dockerfile .
uses: nick-fields/retry@v3
with:
timeout_minutes: 45
max_attempts: 3
command: sudo docker build -f ./Workflow/Dockerfile .
docker-build-api-reference:
runs-on: ubuntu-latest
@@ -94,11 +134,19 @@ jobs:
uses: actions/checkout@v4
- name: Preinstall
run: npm run prerun
uses: nick-fields/retry@v3
with:
timeout_minutes: 10
max_attempts: 3
command: npm run prerun
# build image for accounts service
- name: build docker image
run: sudo docker build -f ./APIReference/Dockerfile .
uses: nick-fields/retry@v3
with:
timeout_minutes: 45
max_attempts: 3
command: sudo docker build -f ./APIReference/Dockerfile .
docker-build-docs:
runs-on: ubuntu-latest
@@ -109,11 +157,19 @@ jobs:
uses: actions/checkout@v4
- name: Preinstall
run: npm run prerun
uses: nick-fields/retry@v3
with:
timeout_minutes: 10
max_attempts: 3
command: npm run prerun
# build image for accounts service
- name: build docker image
run: sudo docker build -f ./Docs/Dockerfile .
uses: nick-fields/retry@v3
with:
timeout_minutes: 45
max_attempts: 3
command: sudo docker build -f ./Docs/Dockerfile .
docker-build-otel-collector:
@@ -125,11 +181,19 @@ jobs:
uses: actions/checkout@v4
- name: Preinstall
run: npm run prerun
uses: nick-fields/retry@v3
with:
timeout_minutes: 10
max_attempts: 3
command: npm run prerun
# build image for accounts service
- name: build docker image
run: sudo docker build -f ./OTelCollector/Dockerfile .
uses: nick-fields/retry@v3
with:
timeout_minutes: 45
max_attempts: 3
command: sudo docker build -f ./OTelCollector/Dockerfile .
docker-build-app:
runs-on: ubuntu-latest
@@ -140,12 +204,20 @@ jobs:
uses: actions/checkout@v4
- name: Preinstall
run: npm run prerun
uses: nick-fields/retry@v3
with:
timeout_minutes: 10
max_attempts: 3
command: npm run prerun
# build image for accounts service
- name: build docker image
run: sudo docker build -f ./App/Dockerfile .
uses: nick-fields/retry@v3
with:
timeout_minutes: 45
max_attempts: 3
command: sudo docker build -f ./App/Dockerfile .
docker-build-copilot:
@@ -157,11 +229,19 @@ jobs:
uses: actions/checkout@v4
- name: Preinstall
run: npm run prerun
uses: nick-fields/retry@v3
with:
timeout_minutes: 10
max_attempts: 3
command: npm run prerun
# build image for accounts service
- name: build docker image
run: sudo docker build -f ./Copilot/Dockerfile .
uses: nick-fields/retry@v3
with:
timeout_minutes: 45
max_attempts: 3
command: sudo docker build -f ./Copilot/Dockerfile .
docker-build-e2e:
runs-on: ubuntu-latest
@@ -172,12 +252,20 @@ jobs:
uses: actions/checkout@v4
- name: Preinstall
run: npm run prerun
uses: nick-fields/retry@v3
with:
timeout_minutes: 10
max_attempts: 3
command: npm run prerun
# build image for accounts service
- name: build docker image
run: sudo docker build -f ./E2E/Dockerfile .
uses: nick-fields/retry@v3
with:
timeout_minutes: 45
max_attempts: 3
command: sudo docker build -f ./E2E/Dockerfile .
docker-build-admin-dashboard:
runs-on: ubuntu-latest
@@ -188,11 +276,19 @@ jobs:
uses: actions/checkout@v4
- name: Preinstall
run: npm run prerun
uses: nick-fields/retry@v3
with:
timeout_minutes: 10
max_attempts: 3
command: npm run prerun
# build image for home
- name: build docker image
run: sudo docker build -f ./AdminDashboard/Dockerfile .
uses: nick-fields/retry@v3
with:
timeout_minutes: 45
max_attempts: 3
command: sudo docker build -f ./AdminDashboard/Dockerfile .
docker-build-dashboard:
runs-on: ubuntu-latest
@@ -203,11 +299,19 @@ jobs:
uses: actions/checkout@v4
- name: Preinstall
run: npm run prerun
uses: nick-fields/retry@v3
with:
timeout_minutes: 10
max_attempts: 3
command: npm run prerun
# build image for home
- name: build docker image
run: sudo docker build -f ./Dashboard/Dockerfile .
uses: nick-fields/retry@v3
with:
timeout_minutes: 45
max_attempts: 3
command: sudo docker build -f ./Dashboard/Dockerfile .
docker-build-probe:
runs-on: ubuntu-latest
@@ -218,11 +322,19 @@ jobs:
uses: actions/checkout@v4
- name: Preinstall
run: npm run prerun
uses: nick-fields/retry@v3
with:
timeout_minutes: 10
max_attempts: 3
command: npm run prerun
# build image probe api
- name: build docker image
run: sudo docker build -f ./Probe/Dockerfile .
uses: nick-fields/retry@v3
with:
timeout_minutes: 45
max_attempts: 3
command: sudo docker build -f ./Probe/Dockerfile .
docker-build-probe-ingest:
runs-on: ubuntu-latest
@@ -233,11 +345,19 @@ jobs:
uses: actions/checkout@v4
- name: Preinstall
run: npm run prerun
uses: nick-fields/retry@v3
with:
timeout_minutes: 10
max_attempts: 3
command: npm run prerun
# build image probe api
- name: build docker image
run: sudo docker build -f ./ProbeIngest/Dockerfile .
uses: nick-fields/retry@v3
with:
timeout_minutes: 45
max_attempts: 3
command: sudo docker build -f ./ProbeIngest/Dockerfile .
docker-build-server-monitor-ingest:
runs-on: ubuntu-latest
@@ -248,11 +368,19 @@ jobs:
uses: actions/checkout@v4
- name: Preinstall
run: npm run prerun
uses: nick-fields/retry@v3
with:
timeout_minutes: 10
max_attempts: 3
command: npm run prerun
# build image probe api
- name: build docker image
run: sudo docker build -f ./ServerMonitorIngest/Dockerfile .
uses: nick-fields/retry@v3
with:
timeout_minutes: 45
max_attempts: 3
command: sudo docker build -f ./ServerMonitorIngest/Dockerfile .
docker-build-open-telemetry-ingest:
runs-on: ubuntu-latest
@@ -263,11 +391,19 @@ jobs:
uses: actions/checkout@v4
- name: Preinstall
run: npm run prerun
uses: nick-fields/retry@v3
with:
timeout_minutes: 10
max_attempts: 3
command: npm run prerun
# build image probe api
- name: build docker image
run: sudo docker build -f ./OpenTelemetryIngest/Dockerfile .
uses: nick-fields/retry@v3
with:
timeout_minutes: 45
max_attempts: 3
command: sudo docker build -f ./OpenTelemetryIngest/Dockerfile .
docker-build-incoming-request-ingest:
runs-on: ubuntu-latest
@@ -278,11 +414,19 @@ jobs:
uses: actions/checkout@v4
- name: Preinstall
run: npm run prerun
uses: nick-fields/retry@v3
with:
timeout_minutes: 10
max_attempts: 3
command: npm run prerun
# build image probe api
- name: build docker image
run: sudo docker build -f ./IncomingRequestIngest/Dockerfile .
uses: nick-fields/retry@v3
with:
timeout_minutes: 45
max_attempts: 3
command: sudo docker build -f ./IncomingRequestIngest/Dockerfile .
docker-build-fluent-ingest:
runs-on: ubuntu-latest
@@ -293,11 +437,19 @@ jobs:
uses: actions/checkout@v4
- name: Preinstall
run: npm run prerun
uses: nick-fields/retry@v3
with:
timeout_minutes: 10
max_attempts: 3
command: npm run prerun
# build image probe api
- name: build docker image
run: sudo docker build -f ./FluentIngest/Dockerfile .
uses: nick-fields/retry@v3
with:
timeout_minutes: 45
max_attempts: 3
command: sudo docker build -f ./FluentIngest/Dockerfile .
docker-build-status-page:
runs-on: ubuntu-latest
@@ -308,11 +460,19 @@ jobs:
uses: actions/checkout@v4
- name: Preinstall
run: npm run prerun
uses: nick-fields/retry@v3
with:
timeout_minutes: 10
max_attempts: 3
command: npm run prerun
# build image for home
- name: build docker image
run: sudo docker build -f ./StatusPage/Dockerfile .
uses: nick-fields/retry@v3
with:
timeout_minutes: 45
max_attempts: 3
command: sudo docker build -f ./StatusPage/Dockerfile .
docker-build-test-server:
runs-on: ubuntu-latest
@@ -323,8 +483,16 @@ jobs:
uses: actions/checkout@v4
- name: Preinstall
run: npm run prerun
uses: nick-fields/retry@v3
with:
timeout_minutes: 10
max_attempts: 3
command: npm run prerun
# build image for mail service
- name: build docker image
run: sudo docker build -f ./TestServer/Dockerfile .
uses: nick-fields/retry@v3
with:
timeout_minutes: 45
max_attempts: 3
command: sudo docker build -f ./TestServer/Dockerfile .

View File

@@ -20,7 +20,12 @@ jobs:
with:
node-version: latest
- run: cd Common && npm install
- run: cd Accounts && npm install && npm run compile && npm run dep-check
- name: Compile Accounts
uses: nick-fields/retry@v3
with:
timeout_minutes: 30
max_attempts: 3
command: cd Accounts && npm install && npm run compile && npm run dep-check
compile-isolated-vm:
runs-on: ubuntu-latest
@@ -32,7 +37,12 @@ jobs:
with:
node-version: latest
- run: cd Common && npm install
- run: cd IsolatedVM && npm install && npm run compile && npm run dep-check
- name: Compile IsolatedVM
uses: nick-fields/retry@v3
with:
timeout_minutes: 30
max_attempts: 3
command: cd IsolatedVM && npm install && npm run compile && npm run dep-check
compile-common:
runs-on: ubuntu-latest
@@ -43,7 +53,12 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version: latest
- run: cd Common && npm install && npm run compile && npm run dep-check
- name: Compile Common
uses: nick-fields/retry@v3
with:
timeout_minutes: 30
max_attempts: 3
command: cd Common && npm install && npm run compile && npm run dep-check
compile-app:
runs-on: ubuntu-latest
@@ -55,7 +70,12 @@ jobs:
with:
node-version: latest
- run: cd Common && npm install
- run: cd App && npm install && npm run compile && npm run dep-check
- name: Compile App
uses: nick-fields/retry@v3
with:
timeout_minutes: 30
max_attempts: 3
command: cd App && npm install && npm run compile && npm run dep-check
compile-home:
runs-on: ubuntu-latest
@@ -67,7 +87,12 @@ jobs:
with:
node-version: latest
- run: cd Common && npm install
- run: cd Home && npm install && npm run compile && npm run dep-check
- name: Compile Home
uses: nick-fields/retry@v3
with:
timeout_minutes: 30
max_attempts: 3
command: cd Home && npm install && npm run compile && npm run dep-check
compile-worker:
runs-on: ubuntu-latest
@@ -79,7 +104,12 @@ jobs:
with:
node-version: latest
- run: cd Common && npm install
- run: cd Worker && npm install && npm run compile && npm run dep-check
- name: Compile Worker
uses: nick-fields/retry@v3
with:
timeout_minutes: 30
max_attempts: 3
command: cd Worker && npm install && npm run compile && npm run dep-check
compile-workflow:
runs-on: ubuntu-latest
@@ -91,7 +121,12 @@ jobs:
with:
node-version: latest
- run: cd Common && npm install
- run: cd Workflow && npm install && npm run compile && npm run dep-check
- name: Compile Workflow
uses: nick-fields/retry@v3
with:
timeout_minutes: 30
max_attempts: 3
command: cd Workflow && npm install && npm run compile && npm run dep-check
compile-api-reference:
runs-on: ubuntu-latest
@@ -103,7 +138,12 @@ jobs:
with:
node-version: latest
- run: cd Common && npm install
- run: cd APIReference && npm install && npm run compile && npm run dep-check
- name: Compile API Reference
uses: nick-fields/retry@v3
with:
timeout_minutes: 30
max_attempts: 3
command: cd APIReference && npm install && npm run compile && npm run dep-check
compile-docs-reference:
runs-on: ubuntu-latest
@@ -115,7 +155,12 @@ jobs:
with:
node-version: latest
- run: cd Common && npm install
- run: cd Docs && npm install && npm run compile && npm run dep-check
- name: Compile Docs Reference
uses: nick-fields/retry@v3
with:
timeout_minutes: 30
max_attempts: 3
command: cd Docs && npm install && npm run compile && npm run dep-check
compile-copilot:
runs-on: ubuntu-latest
@@ -127,7 +172,12 @@ jobs:
with:
node-version: latest
- run: cd Common && npm install
- run: cd Copilot && npm install && npm run compile && npm run dep-check
- name: Compile Copilot
uses: nick-fields/retry@v3
with:
timeout_minutes: 30
max_attempts: 3
command: cd Copilot && npm install && npm run compile && npm run dep-check
compile-nginx:
runs-on: ubuntu-latest
@@ -140,7 +190,12 @@ jobs:
node-version: latest
- run: cd Common && npm install
- run: cd Nginx && npm install && npm run compile && npm run dep-check
- name: Compile Nginx
uses: nick-fields/retry@v3
with:
timeout_minutes: 30
max_attempts: 3
command: cd Nginx && npm install && npm run compile && npm run dep-check
compile-infrastructure-agent:
runs-on: ubuntu-latest
@@ -150,7 +205,12 @@ jobs:
- uses: actions/checkout@v4
# Setup Go
- uses: actions/setup-go@v5
- run: cd InfrastructureAgent && go build .
- name: Compile Infrastructure Agent
uses: nick-fields/retry@v3
with:
timeout_minutes: 30
max_attempts: 3
command: cd InfrastructureAgent && go build .
compile-admin-dashboard:
@@ -164,7 +224,12 @@ jobs:
node-version: latest
- run: cd Common && npm install
- run: cd AdminDashboard && npm install && npm run compile && npm run dep-check
- name: Compile Admin Dashboard
uses: nick-fields/retry@v3
with:
timeout_minutes: 30
max_attempts: 3
command: cd AdminDashboard && npm install && npm run compile && npm run dep-check
compile-dashboard:
runs-on: ubuntu-latest
@@ -177,7 +242,12 @@ jobs:
node-version: latest
- run: cd Common && npm install
- run: cd Dashboard && npm install && npm run compile && npm run dep-check
- name: Compile Dashboard
uses: nick-fields/retry@v3
with:
timeout_minutes: 30
max_attempts: 3
command: cd Dashboard && npm install && npm run compile && npm run dep-check
compile-e2e:
@@ -191,7 +261,12 @@ jobs:
node-version: latest
- run: sudo apt-get update
- run: cd Common && npm install
- run: cd E2E && npm install && npm run compile && npm run dep-check
- name: Compile E2E
uses: nick-fields/retry@v3
with:
timeout_minutes: 30
max_attempts: 3
command: cd E2E && npm install && npm run compile && npm run dep-check
compile-probe:
runs-on: ubuntu-latest
@@ -203,7 +278,12 @@ jobs:
with:
node-version: latest
- run: cd Common && npm install
- run: cd Probe && npm install && npm run compile && npm run dep-check
- name: Compile Probe
uses: nick-fields/retry@v3
with:
timeout_minutes: 30
max_attempts: 3
command: cd Probe && npm install && npm run compile && npm run dep-check
compile-probe-ingest:
runs-on: ubuntu-latest
@@ -215,7 +295,12 @@ jobs:
with:
node-version: latest
- run: cd Common && npm install
- run: cd ProbeIngest && npm install && npm run compile && npm run dep-check
- name: Compile Probe Ingest
uses: nick-fields/retry@v3
with:
timeout_minutes: 30
max_attempts: 3
command: cd ProbeIngest && npm install && npm run compile && npm run dep-check
compile-server-monitor-ingest:
runs-on: ubuntu-latest
@@ -227,7 +312,12 @@ jobs:
with:
node-version: latest
- run: cd Common && npm install
- run: cd ServerMonitorIngest && npm install && npm run compile && npm run dep-check
- name: Compile Server Monitor Ingest
uses: nick-fields/retry@v3
with:
timeout_minutes: 30
max_attempts: 3
command: cd ServerMonitorIngest && npm install && npm run compile && npm run dep-check
compile-open-telemetry-ingest:
runs-on: ubuntu-latest
@@ -239,7 +329,12 @@ jobs:
with:
node-version: latest
- run: cd Common && npm install
- run: cd OpenTelemetryIngest && npm install && npm run compile && npm run dep-check
- name: Compile Open Telemetry Ingest
uses: nick-fields/retry@v3
with:
timeout_minutes: 30
max_attempts: 3
command: cd OpenTelemetryIngest && npm install && npm run compile && npm run dep-check
compile-incoming-request-ingest:
@@ -252,7 +347,12 @@ jobs:
with:
node-version: latest
- run: cd Common && npm install
- run: cd IncomingRequestIngest && npm install && npm run compile && npm run dep-check
- name: Compile Incoming Request Ingest
uses: nick-fields/retry@v3
with:
timeout_minutes: 30
max_attempts: 3
command: cd IncomingRequestIngest && npm install && npm run compile && npm run dep-check
compile-fluent-ingest:
runs-on: ubuntu-latest
@@ -264,7 +364,12 @@ jobs:
with:
node-version: latest
- run: cd Common && npm install
- run: cd FluentIngest && npm install && npm run compile && npm run dep-check
- name: Compile Fluent Ingest
uses: nick-fields/retry@v3
with:
timeout_minutes: 30
max_attempts: 3
command: cd FluentIngest && npm install && npm run compile && npm run dep-check
compile-status-page:
@@ -278,7 +383,12 @@ jobs:
node-version: latest
- run: cd Common && npm install
- run: cd StatusPage && npm install && npm run compile && npm run dep-check
- name: Compile Status Page
uses: nick-fields/retry@v3
with:
timeout_minutes: 30
max_attempts: 3
command: cd StatusPage && npm install && npm run compile && npm run dep-check
compile-test-server:
runs-on: ubuntu-latest
@@ -290,7 +400,12 @@ jobs:
with:
node-version: latest
- run: cd Common && npm install
- run: cd TestServer && npm install && npm run compile && npm run dep-check
- name: Compile Test Server
uses: nick-fields/retry@v3
with:
timeout_minutes: 30
max_attempts: 3
command: cd TestServer && npm install && npm run compile && npm run dep-check
compile-mcp:
runs-on: ubuntu-latest
@@ -302,4 +417,9 @@ jobs:
with:
node-version: latest
- run: cd Common && npm install
- run: cd MCP && npm install && npm run compile && npm run dep-check
- name: Compile MCP
uses: nick-fields/retry@v3
with:
timeout_minutes: 30
max_attempts: 3
command: cd MCP && npm update @oneuptime/common && npm install && npm run compile && npm run dep-check

File diff suppressed because it is too large Load Diff

View File

@@ -77,17 +77,21 @@ jobs:
ls -la "$PROVIDER_DIR" || true
- name: Test Go build
run: |
PROVIDER_DIR="./Terraform"
if [ -d "$PROVIDER_DIR" ] && [ -f "$PROVIDER_DIR/go.mod" ]; then
cd "$PROVIDER_DIR"
echo "🔨 Testing Go build..."
go mod tidy
go build -v ./...
echo "✅ Go build successful"
else
echo "⚠️ Cannot test build - missing go.mod or provider directory"
fi
uses: nick-fields/retry@v3
with:
timeout_minutes: 30
max_attempts: 3
command: |
PROVIDER_DIR="./Terraform"
if [ -d "$PROVIDER_DIR" ] && [ -f "$PROVIDER_DIR/go.mod" ]; then
cd "$PROVIDER_DIR"
echo "🔨 Testing Go build..."
go mod tidy
go build -v ./...
echo "✅ Go build successful"
else
echo "⚠️ Cannot test build - missing go.mod or provider directory"
fi
- name: Upload Terraform provider as artifact
uses: actions/upload-artifact@v4

File diff suppressed because it is too large Load Diff

1
.gitignore vendored
View File

@@ -127,3 +127,4 @@ MCP/build/
MCP/.env
MCP/node_modules
Dashboard/public/sw.js
.claude/settings.local.json

View File

@@ -3,7 +3,7 @@
#
# Pull base image nodejs image.
FROM public.ecr.aws/docker/library/node:23.8-alpine3.21
FROM public.ecr.aws/docker/library/node:24.9-alpine3.21
RUN mkdir /tmp/npm && chmod 2777 /tmp/npm && chown 1000:1000 /tmp/npm && npm config set cache /tmp/npm --global
RUN npm config set fetch-retries 5
@@ -17,6 +17,7 @@ ARG APP_VERSION
ENV GIT_SHA=${GIT_SHA}
ENV APP_VERSION=${APP_VERSION}
ENV PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1
# IF APP_VERSION is not set, set it to 1.0.0

View File

@@ -2,6 +2,7 @@ import { IsBillingEnabled } from "Common/Server/EnvironmentConfig";
import { ViewsPath } from "../Utils/Config";
import ResourceUtil, { ModelDocumentation } from "../Utils/Resources";
import { ExpressRequest, ExpressResponse } from "Common/Server/Utils/Express";
import Dictionary from "Common/Types/Dictionary";
// Retrieve resources documentation
const Resources: Array<ModelDocumentation> = ResourceUtil.getResources();
@@ -16,7 +17,7 @@ export default class ServiceHandler {
// Extract page parameter from request
const page: string | undefined = req.params["page"];
const pageData: any = {};
const pageData: Dictionary<unknown> = {};
// Set default page title and description for the authentication page
pageTitle = "Authentication";

View File

@@ -4,6 +4,7 @@ import ResourceUtil, { ModelDocumentation } from "../Utils/Resources";
import LocalCache from "Common/Server/Infrastructure/LocalCache";
import { ExpressRequest, ExpressResponse } from "Common/Server/Utils/Express";
import LocalFile from "Common/Server/Utils/LocalFile";
import Dictionary from "Common/Types/Dictionary";
const Resources: Array<ModelDocumentation> = ResourceUtil.getResources();
@@ -12,9 +13,9 @@ export default class ServiceHandler {
_req: ExpressRequest,
res: ExpressResponse,
): Promise<void> {
const pageData: any = {};
const pageData: Dictionary<unknown> = {};
pageData.selectCode = await LocalCache.getOrSetString(
pageData["selectCode"] = await LocalCache.getOrSetString(
"data-type",
"select",
async () => {
@@ -22,7 +23,7 @@ export default class ServiceHandler {
},
);
pageData.sortCode = await LocalCache.getOrSetString(
pageData["sortCode"] = await LocalCache.getOrSetString(
"data-type",
"sort",
async () => {
@@ -30,7 +31,7 @@ export default class ServiceHandler {
},
);
pageData.equalToCode = await LocalCache.getOrSetString(
pageData["equalToCode"] = await LocalCache.getOrSetString(
"data-type",
"equal-to",
async () => {
@@ -38,7 +39,7 @@ export default class ServiceHandler {
},
);
pageData.equalToOrNullCode = await LocalCache.getOrSetString(
pageData["equalToOrNullCode"] = await LocalCache.getOrSetString(
"data-type",
"equal-to-or-null",
async () => {
@@ -48,7 +49,7 @@ export default class ServiceHandler {
},
);
pageData.greaterThanCode = await LocalCache.getOrSetString(
pageData["greaterThanCode"] = await LocalCache.getOrSetString(
"data-type",
"greater-than",
async () => {
@@ -58,7 +59,7 @@ export default class ServiceHandler {
},
);
pageData.greaterThanOrEqualCode = await LocalCache.getOrSetString(
pageData["greaterThanOrEqualCode"] = await LocalCache.getOrSetString(
"data-type",
"greater-than-or-equal",
async () => {
@@ -68,7 +69,7 @@ export default class ServiceHandler {
},
);
pageData.lessThanCode = await LocalCache.getOrSetString(
pageData["lessThanCode"] = await LocalCache.getOrSetString(
"data-type",
"less-than",
async () => {
@@ -78,7 +79,7 @@ export default class ServiceHandler {
},
);
pageData.lessThanOrEqualCode = await LocalCache.getOrSetString(
pageData["lessThanOrEqualCode"] = await LocalCache.getOrSetString(
"data-type",
"less-than-or-equal",
async () => {
@@ -88,7 +89,7 @@ export default class ServiceHandler {
},
);
pageData.includesCode = await LocalCache.getOrSetString(
pageData["includesCode"] = await LocalCache.getOrSetString(
"data-type",
"includes",
async () => {
@@ -98,7 +99,7 @@ export default class ServiceHandler {
},
);
pageData.lessThanOrNullCode = await LocalCache.getOrSetString(
pageData["lessThanOrNullCode"] = await LocalCache.getOrSetString(
"data-type",
"less-than-or-equal",
async () => {
@@ -108,7 +109,7 @@ export default class ServiceHandler {
},
);
pageData.greaterThanOrNullCode = await LocalCache.getOrSetString(
pageData["greaterThanOrNullCode"] = await LocalCache.getOrSetString(
"data-type",
"less-than-or-equal",
async () => {
@@ -118,7 +119,7 @@ export default class ServiceHandler {
},
);
pageData.isNullCode = await LocalCache.getOrSetString(
pageData["isNullCode"] = await LocalCache.getOrSetString(
"data-type",
"is-null",
async () => {
@@ -126,7 +127,7 @@ export default class ServiceHandler {
},
);
pageData.notNullCode = await LocalCache.getOrSetString(
pageData["notNullCode"] = await LocalCache.getOrSetString(
"data-type",
"not-null",
async () => {
@@ -134,7 +135,7 @@ export default class ServiceHandler {
},
);
pageData.notEqualToCode = await LocalCache.getOrSetString(
pageData["notEqualToCode"] = await LocalCache.getOrSetString(
"data-type",
"not-equals",
async () => {

View File

@@ -2,6 +2,7 @@ import { IsBillingEnabled } from "Common/Server/EnvironmentConfig";
import { ViewsPath } from "../Utils/Config";
import ResourceUtil, { ModelDocumentation } from "../Utils/Resources";
import { ExpressRequest, ExpressResponse } from "Common/Server/Utils/Express";
import Dictionary from "Common/Types/Dictionary";
// Fetch a list of resources used in the application
const Resources: Array<ModelDocumentation> = ResourceUtil.getResources();
@@ -17,7 +18,7 @@ export default class ServiceHandler {
// Get the 'page' parameter from the request
const page: string | undefined = req.params["page"];
const pageData: any = {};
const pageData: Dictionary<unknown> = {};
// Set the default page title and description
pageTitle = "Errors";

View File

@@ -2,6 +2,7 @@ import { IsBillingEnabled } from "Common/Server/EnvironmentConfig";
import { ViewsPath } from "../Utils/Config";
import ResourceUtil, { ModelDocumentation } from "../Utils/Resources";
import { ExpressRequest, ExpressResponse } from "Common/Server/Utils/Express";
import Dictionary from "Common/Types/Dictionary";
// Get all resources and featured resources from ResourceUtil
const Resources: Array<ModelDocumentation> = ResourceUtil.getResources();
@@ -20,10 +21,10 @@ export default class ServiceHandler {
// Get the requested page from the URL parameters
const page: string | undefined = req.params["page"];
const pageData: any = {};
const pageData: Dictionary<unknown> = {};
// Set featured resources for the page
pageData.featuredResources = FeaturedResources;
pageData["featuredResources"] = FeaturedResources;
// Set page title and description
pageTitle = "Introduction";

View File

@@ -3,7 +3,10 @@ import ResourceUtil, { ModelDocumentation } from "../Utils/Resources";
import PageNotFoundServiceHandler from "./PageNotFound";
import { AppApiRoute } from "Common/ServiceRoute";
import { ColumnAccessControl } from "Common/Types/BaseDatabase/AccessControl";
import { getTableColumns } from "Common/Types/Database/TableColumn";
import {
getTableColumns,
TableColumnMetadata,
} from "Common/Types/Database/TableColumn";
import Dictionary from "Common/Types/Dictionary";
import ObjectID from "Common/Types/ObjectID";
import Permission, {
@@ -33,7 +36,7 @@ export default class ServiceHandler {
let pageTitle: string = "";
let pageDescription: string = "";
let page: string | undefined = req.params["page"];
const pageData: any = {};
const pageData: Dictionary<unknown> = {};
// Check if page is provided
if (!page) {
@@ -56,7 +59,9 @@ export default class ServiceHandler {
page = "model";
// Get table columns for current resource
const tableColumns: any = getTableColumns(currentResource.model);
const tableColumns: Dictionary<TableColumnMetadata> = getTableColumns(
currentResource.model,
);
// Filter out columns with no access
for (const key in tableColumns) {
@@ -77,12 +82,14 @@ export default class ServiceHandler {
continue;
}
if (tableColumns[key].hideColumnInDocumentation) {
if (tableColumns[key] && tableColumns[key]!.hideColumnInDocumentation) {
delete tableColumns[key];
continue;
}
tableColumns[key].permissions = accessControl;
if (tableColumns[key]) {
(tableColumns[key] as any).permissions = accessControl;
}
}
// Remove unnecessary columns
@@ -92,11 +99,11 @@ export default class ServiceHandler {
delete tableColumns["version"];
// Set page data
pageData.title = currentResource.model.singularName;
pageData.description = currentResource.model.tableDescription;
pageData.columns = tableColumns;
pageData["title"] = currentResource.model.singularName;
pageData["description"] = currentResource.model.tableDescription;
pageData["columns"] = tableColumns;
pageData.tablePermissions = {
pageData["tablePermissions"] = {
read: currentResource.model.readRecordPermissions.map(
(permission: Permission) => {
return PermissionDictionary[permission];
@@ -120,7 +127,7 @@ export default class ServiceHandler {
};
// Cache the list request data
pageData.listRequest = await LocalCache.getOrSetString(
pageData["listRequest"] = await LocalCache.getOrSetString(
"model",
"list-request",
async () => {
@@ -130,7 +137,7 @@ export default class ServiceHandler {
);
// Cache the item request data
pageData.itemRequest = await LocalCache.getOrSetString(
pageData["itemRequest"] = await LocalCache.getOrSetString(
"model",
"item-request",
async () => {
@@ -140,7 +147,7 @@ export default class ServiceHandler {
);
// Cache the item response data
pageData.itemResponse = await LocalCache.getOrSetString(
pageData["itemResponse"] = await LocalCache.getOrSetString(
"model",
"item-response",
async () => {
@@ -152,7 +159,7 @@ export default class ServiceHandler {
);
// Cache the count request data
pageData.countRequest = await LocalCache.getOrSetString(
pageData["countRequest"] = await LocalCache.getOrSetString(
"model",
"count-request",
async () => {
@@ -164,7 +171,7 @@ export default class ServiceHandler {
);
// Cache the count response data
pageData.countResponse = await LocalCache.getOrSetString(
pageData["countResponse"] = await LocalCache.getOrSetString(
"model",
"count-response",
async () => {
@@ -175,7 +182,7 @@ export default class ServiceHandler {
},
);
pageData.updateRequest = await LocalCache.getOrSetString(
pageData["updateRequest"] = await LocalCache.getOrSetString(
"model",
"update-request",
async () => {
@@ -186,7 +193,7 @@ export default class ServiceHandler {
},
);
pageData.updateResponse = await LocalCache.getOrSetString(
pageData["updateResponse"] = await LocalCache.getOrSetString(
"model",
"update-response",
async () => {
@@ -197,7 +204,7 @@ export default class ServiceHandler {
},
);
pageData.createRequest = await LocalCache.getOrSetString(
pageData["createRequest"] = await LocalCache.getOrSetString(
"model",
"create-request",
async () => {
@@ -208,7 +215,7 @@ export default class ServiceHandler {
},
);
pageData.createResponse = await LocalCache.getOrSetString(
pageData["createResponse"] = await LocalCache.getOrSetString(
"model",
"create-response",
async () => {
@@ -219,7 +226,7 @@ export default class ServiceHandler {
},
);
pageData.deleteRequest = await LocalCache.getOrSetString(
pageData["deleteRequest"] = await LocalCache.getOrSetString(
"model",
"delete-request",
async () => {
@@ -230,7 +237,7 @@ export default class ServiceHandler {
},
);
pageData.deleteResponse = await LocalCache.getOrSetString(
pageData["deleteResponse"] = await LocalCache.getOrSetString(
"model",
"delete-response",
async () => {
@@ -242,7 +249,7 @@ export default class ServiceHandler {
);
// Get list response from cache or set it if it's not available
pageData.listResponse = await LocalCache.getOrSetString(
pageData["listResponse"] = await LocalCache.getOrSetString(
"model",
"list-response",
async () => {
@@ -254,14 +261,15 @@ export default class ServiceHandler {
);
// Generate a unique ID for the example object
pageData.exampleObjectID = ObjectID.generate();
pageData["exampleObjectID"] = ObjectID.generate();
// Construct the API path for the current resource
pageData.apiPath =
pageData["apiPath"] =
AppApiRoute.toString() + currentResource.model.crudApiPath?.toString();
// Check if the current resource is a master admin API
pageData.isMasterAdminApiDocs = currentResource.model.isMasterAdminApiDocs;
pageData["isMasterAdminApiDocs"] =
currentResource.model.isMasterAdminApiDocs;
// Render the index page with the required data
return res.render(`${ViewsPath}/pages/index`, {

View File

@@ -7,6 +7,7 @@ import { ViewsPath } from "../Utils/Config";
import ResourceUtil, { ModelDocumentation } from "../Utils/Resources";
import { ExpressRequest, ExpressResponse } from "Common/Server/Utils/Express";
import URL from "Common/Types/API/URL";
import Dictionary from "Common/Types/Dictionary";
// Fetch a list of resources used in the application
const Resources: Array<ModelDocumentation> = ResourceUtil.getResources();
@@ -22,7 +23,7 @@ export default class ServiceHandler {
// Get the 'page' parameter from the request
const page: string | undefined = req.params["page"];
const pageData: any = {
const pageData: Dictionary<unknown> = {
hostUrl: new URL(HttpProtocol, Host).toString(),
};

View File

@@ -4,6 +4,7 @@ import ResourceUtil, { ModelDocumentation } from "../Utils/Resources";
import LocalCache from "Common/Server/Infrastructure/LocalCache";
import { ExpressRequest, ExpressResponse } from "Common/Server/Utils/Express";
import LocalFile from "Common/Server/Utils/LocalFile";
import Dictionary from "Common/Types/Dictionary";
const Resources: Array<ModelDocumentation> = ResourceUtil.getResources(); // Get all resources from ResourceUtil
@@ -15,14 +16,14 @@ export default class ServiceHandler {
let pageTitle: string = ""; // Initialize page title
let pageDescription: string = ""; // Initialize page description
const page: string | undefined = req.params["page"]; // Get the page parameter from the request
const pageData: any = {}; // Initialize page data object
const pageData: Dictionary<unknown> = {}; // Initialize page data object
// Set page title and description
pageTitle = "Pagination";
pageDescription = "Learn how to paginate requests with OneUptime API";
// Get response and request code from LocalCache or LocalFile
pageData.responseCode = await LocalCache.getOrSetString(
pageData["responseCode"] = await LocalCache.getOrSetString(
"pagination",
"response",
async () => {
@@ -33,7 +34,7 @@ export default class ServiceHandler {
},
);
pageData.requestCode = await LocalCache.getOrSetString(
pageData["requestCode"] = await LocalCache.getOrSetString(
"pagination",
"request",
async () => {

View File

@@ -3,6 +3,7 @@ import ResourceUtil, { ModelDocumentation } from "../Utils/Resources";
import { PermissionHelper, PermissionProps } from "Common/Types/Permission";
import { ExpressRequest, ExpressResponse } from "Common/Server/Utils/Express";
import { IsBillingEnabled } from "Common/Server/EnvironmentConfig";
import Dictionary from "Common/Types/Dictionary";
const Resources: Array<ModelDocumentation> = ResourceUtil.getResources();
@@ -17,14 +18,14 @@ export default class ServiceHandler {
// Get the requested page
const page: string | undefined = req.params["page"];
const pageData: any = {};
const pageData: Dictionary<unknown> = {};
// Set page title and description
pageTitle = "Permissions";
pageDescription = "Learn how permissions work with OneUptime";
// Filter permissions to only include those assignable to tenants
pageData.permissions = PermissionHelper.getAllPermissionProps().filter(
pageData["permissions"] = PermissionHelper.getAllPermissionProps().filter(
(i: PermissionProps) => {
return i.isAssignableToTenant;
},

View File

@@ -1,8 +1,14 @@
{
"watch": ["./","../Common/Server", "../Common/Types", "../Common/Utils", "../Common/Models"],
"ext": "ts,json,tsx,env,js,jsx,hbs",
"ext": "ts,tsx",
"ignore": [
"./node_modules/**",
"./public/**",
"./bin/**",
"./build/**",
"greenlock.d/*"
],
"exec": "node --inspect=0.0.0.0:9229 --require ts-node/register Index.ts"
"watchOptions": {"useFsEvents": false, "interval": 500},
"env": {"TS_NODE_TRANSPILE_ONLY": "1", "TS_NODE_FILES": "false"},
"exec": "node -r ts-node/register/transpile-only Index.ts"
}

View File

@@ -66,6 +66,7 @@
"crypto-js": "^4.2.0",
"dotenv": "^16.4.4",
"ejs": "^3.1.10",
"elkjs": "^0.10.0",
"esbuild": "^0.25.5",
"express": "^4.21.1",
"formik": "^2.4.6",

View File

@@ -135,7 +135,6 @@
<link rel="apple-touch-icon-precomposed" href="/img/ou-wb.svg">
<link rel="icon" href="/img/ou-wb.svg">
<link rel="image_src" type="image/png" href="/img/hou-wb.svg">
<link rel="canonical" href="/">
<link rel="manifest" href="/manifest.json">
<meta property="og:title" content="OneUptime - One Complete Observability platform.">
<meta property="og:url" content="https://oneuptime.com">

View File

@@ -3,7 +3,7 @@
#
# Pull base image nodejs image.
FROM public.ecr.aws/docker/library/node:23.8-alpine3.21
FROM public.ecr.aws/docker/library/node:24.9-alpine3.21
RUN mkdir /tmp/npm && chmod 2777 /tmp/npm && chown 1000:1000 /tmp/npm && npm config set cache /tmp/npm --global
RUN npm config set fetch-retries 5
@@ -17,6 +17,7 @@ ARG APP_VERSION
ENV GIT_SHA=${GIT_SHA}
ENV APP_VERSION=${APP_VERSION}
ENV PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1
# IF APP_VERSION is not set, set it to 1.0.0

View File

@@ -1,7 +1,10 @@
{
"watch": ["./","../Common/UI", "../Common/Types", "../Common/Utils", "../Common/Models"],
"ext": "ts,json,tsx,env,js,jsx,hbs",
"ext": "ts,tsx",
"ignore": [
"./node_modules/**",
"./public/**",
"./bin/**",
"./public/**",
"./public/dist/**",
"./build/*",

View File

@@ -70,6 +70,7 @@
"crypto-js": "^4.2.0",
"dotenv": "^16.4.4",
"ejs": "^3.1.10",
"elkjs": "^0.10.0",
"esbuild": "^0.25.5",
"express": "^4.21.1",
"formik": "^2.4.6",

View File

@@ -1,6 +1,8 @@
import {
LOGIN_API_URL,
VERIFY_TWO_FACTOR_AUTH_API_URL,
VERIFY_TOTP_AUTH_API_URL,
GENERATE_WEBAUTHN_AUTH_OPTIONS_API_URL,
VERIFY_WEBAUTHN_AUTH_API_URL,
} from "../Utils/ApiPaths";
import Route from "Common/Types/API/Route";
import URL from "Common/Types/API/URL";
@@ -12,17 +14,20 @@ import { DASHBOARD_URL } from "Common/UI/Config";
import OneUptimeLogo from "Common/UI/Images/logos/OneUptimeSVG/3-transparent.svg";
import UiAnalytics from "Common/UI/Utils/Analytics";
import LoginUtil from "Common/UI/Utils/Login";
import UserTwoFactorAuth from "Common/Models/DatabaseModels/UserTwoFactorAuth";
import UserTotpAuth from "Common/Models/DatabaseModels/UserTotpAuth";
import UserWebAuthn from "Common/Models/DatabaseModels/UserWebAuthn";
import Navigation from "Common/UI/Utils/Navigation";
import UserUtil from "Common/UI/Utils/User";
import User from "Common/Models/DatabaseModels/User";
import React from "react";
import useAsyncEffect from "use-async-effect";
import StaticModelList from "Common/UI/Components/ModelList/StaticModelList";
import BasicForm from "Common/UI/Components/Forms/BasicForm";
import API from "Common/UI/Utils/API/API";
import HTTPErrorResponse from "Common/Types/API/HTTPErrorResponse";
import HTTPResponse from "Common/Types/API/HTTPResponse";
import Base64 from "Common/Utils/Base64";
import ErrorMessage from "Common/UI/Components/ErrorMessage/ErrorMessage";
import ComponentLoader from "Common/UI/Components/ComponentLoader/ComponentLoader";
const LoginPage: () => JSX.Element = () => {
const apiUrl: URL = LOGIN_API_URL;
@@ -36,14 +41,32 @@ const LoginPage: () => JSX.Element = () => {
const [showTwoFactorAuth, setShowTwoFactorAuth] =
React.useState<boolean>(false);
const [twoFactorAuthList, setTwoFactorAuthList] = React.useState<
UserTwoFactorAuth[]
>([]);
const [totpAuthList, setTotpAuthList] = React.useState<UserTotpAuth[]>([]);
const [selectedTwoFactorAuth, setSelectedTwoFactorAuth] = React.useState<
UserTwoFactorAuth | undefined
const [webAuthnList, setWebAuthnList] = React.useState<UserWebAuthn[]>([]);
const [selectedTotpAuth, setSelectedTotpAuth] = React.useState<
UserTotpAuth | undefined
>(undefined);
const [selectedWebAuthn, setSelectedWebAuthn] = React.useState<
UserWebAuthn | undefined
>(undefined);
type TwoFactorMethod = {
type: "totp" | "webauthn";
item: UserTotpAuth | UserWebAuthn;
};
const twoFactorMethods: TwoFactorMethod[] = [
...totpAuthList.map((item: UserTotpAuth) => {
return { type: "totp" as const, item };
}),
...webAuthnList.map((item: UserWebAuthn) => {
return { type: "webauthn" as const, item };
}),
];
const [isTwoFactorAuthLoading, setIsTwoFactorAuthLoading] =
React.useState<boolean>(false);
const [twofactorAuthError, setTwoFactorAuthError] =
@@ -57,6 +80,96 @@ const LoginPage: () => JSX.Element = () => {
}
}, []);
useAsyncEffect(async () => {
if (selectedWebAuthn) {
setIsTwoFactorAuthLoading(true);
try {
const result: HTTPResponse<JSONObject> = await API.post({
url: GENERATE_WEBAUTHN_AUTH_OPTIONS_API_URL,
data: {
data: {
email: initialValues["email"],
},
},
});
if (result instanceof HTTPErrorResponse) {
throw result;
}
const data: any = result.data as any;
// Convert base64url strings back to Uint8Array
data.options.challenge = Base64.base64UrlToUint8Array(
data.options.challenge,
);
if (data.options.allowCredentials) {
data.options.allowCredentials.forEach((cred: any) => {
cred.id = Base64.base64UrlToUint8Array(cred.id);
});
}
// Use WebAuthn API
const credential: PublicKeyCredential =
(await navigator.credentials.get({
publicKey: data.options,
})) as PublicKeyCredential;
const assertionResponse: AuthenticatorAssertionResponse =
credential.response as AuthenticatorAssertionResponse;
// Verify
const verifyResult: HTTPResponse<JSONObject> = await API.post({
url: VERIFY_WEBAUTHN_AUTH_API_URL,
data: {
data: {
...initialValues,
challenge: data.challenge,
credential: {
id: credential.id,
rawId: Base64.uint8ArrayToBase64Url(
new Uint8Array(credential.rawId),
),
response: {
authenticatorData: Base64.uint8ArrayToBase64Url(
new Uint8Array(assertionResponse.authenticatorData),
),
clientDataJSON: Base64.uint8ArrayToBase64Url(
new Uint8Array(assertionResponse.clientDataJSON),
),
signature: Base64.uint8ArrayToBase64Url(
new Uint8Array(assertionResponse.signature),
),
userHandle: assertionResponse.userHandle
? Base64.uint8ArrayToBase64Url(
new Uint8Array(assertionResponse.userHandle),
)
: null,
},
type: credential.type,
},
},
},
});
if (verifyResult instanceof HTTPErrorResponse) {
throw verifyResult;
}
const user: User = User.fromJSON(
verifyResult.data as JSONObject,
User,
) as User;
const miscData: JSONObject = {};
login(user as User, miscData);
} catch (error) {
setTwoFactorAuthError(API.getFriendlyErrorMessage(error as Error));
}
setIsTwoFactorAuthLoading(false);
}
}, [selectedWebAuthn]);
type LoginFunction = (user: User, miscData: JSONObject) => void;
const login: LoginFunction = (user: User, miscData: JSONObject): void => {
@@ -156,16 +269,23 @@ const LoginPage: () => JSX.Element = () => {
) => {
if (
miscData &&
(miscData as JSONObject)["twoFactorAuth"] === true
((((miscData as JSONObject)["totpAuthList"] as JSONArray)
?.length || 0) > 0 ||
(((miscData as JSONObject)["webAuthnList"] as JSONArray)
?.length || 0) > 0)
) {
const twoFactorAuthList: Array<UserTwoFactorAuth> =
UserTwoFactorAuth.fromJSONArray(
(miscData as JSONObject)[
"twoFactorAuthList"
] as JSONArray,
UserTwoFactorAuth,
const totpAuthList: Array<UserTotpAuth> =
UserTotpAuth.fromJSONArray(
(miscData as JSONObject)["totpAuthList"] as JSONArray,
UserTotpAuth,
);
setTwoFactorAuthList(twoFactorAuthList);
const webAuthnList: Array<UserWebAuthn> =
UserWebAuthn.fromJSONArray(
(miscData as JSONObject)["webAuthnList"] as JSONArray,
UserWebAuthn,
);
setTotpAuthList(totpAuthList);
setWebAuthnList(webAuthnList);
setShowTwoFactorAuth(true);
return;
}
@@ -187,19 +307,53 @@ const LoginPage: () => JSX.Element = () => {
/>
)}
{showTwoFactorAuth && !selectedTwoFactorAuth && (
<StaticModelList<UserTwoFactorAuth>
titleField="name"
descriptionField=""
selectedItems={[]}
list={twoFactorAuthList}
onClick={(item: UserTwoFactorAuth) => {
setSelectedTwoFactorAuth(item);
}}
/>
{showTwoFactorAuth && !selectedTotpAuth && !selectedWebAuthn && (
<div className="space-y-4">
{twoFactorMethods.map(
(method: TwoFactorMethod, index: number) => {
return (
<div
key={index}
className="cursor-pointer p-4 border border-gray-300 rounded-lg hover:bg-gray-50"
onClick={() => {
if (method.type === "totp") {
setSelectedTotpAuth(method.item as UserTotpAuth);
} else {
setSelectedWebAuthn(method.item as UserWebAuthn);
}
}}
>
<div className="font-medium">
{(method.item as any).name}
</div>
<div className="text-sm text-gray-500">
{method.type === "totp"
? "Authenticator App"
: "Security Key"}
</div>
</div>
);
},
)}
</div>
)}
{showTwoFactorAuth && selectedTwoFactorAuth && (
{showTwoFactorAuth && selectedWebAuthn && (
<div className="text-center">
<div className="text-lg font-medium mb-4">
Authenticating with Security Key
</div>
<div className="text-sm text-gray-500 mb-4">
Please follow the instructions on your security key device.
</div>
{isTwoFactorAuthLoading && <ComponentLoader />}
{twofactorAuthError && (
<ErrorMessage message={twofactorAuthError} />
)}
</div>
)}
{showTwoFactorAuth && selectedTotpAuth && (
<BasicForm
id="two-factor-auth-form"
name="Two Factor Auth"
@@ -225,14 +379,17 @@ const LoginPage: () => JSX.Element = () => {
try {
const code: string = data["code"] as string;
const twoFactorAuthId: string =
selectedTwoFactorAuth.id?.toString() as string;
selectedTotpAuth!.id?.toString() as string;
const result: HTTPErrorResponse | HTTPResponse<JSONObject> =
await API.post(VERIFY_TWO_FACTOR_AUTH_API_URL, {
await API.post({
url: VERIFY_TOTP_AUTH_API_URL,
data: {
...initialValues,
code: code,
twoFactorAuthId: twoFactorAuthId,
data: {
...initialValues,
code: code,
twoFactorAuthId: twoFactorAuthId,
},
},
});
@@ -261,7 +418,7 @@ const LoginPage: () => JSX.Element = () => {
)}
</div>
<div className="mt-10 text-center">
{!selectedTwoFactorAuth && (
{!selectedTotpAuth && !selectedWebAuthn && (
<div className="text-muted mb-0 text-gray-500">
Don&apos;t have an account?{" "}
<Link
@@ -272,11 +429,12 @@ const LoginPage: () => JSX.Element = () => {
</Link>
</div>
)}
{selectedTwoFactorAuth ? (
{selectedTotpAuth || selectedWebAuthn ? (
<div className="text-muted mb-0 text-gray-500">
<Link
onClick={() => {
setSelectedTwoFactorAuth(undefined);
setSelectedTotpAuth(undefined);
setSelectedWebAuthn(undefined);
}}
className="text-indigo-500 hover:text-indigo-900 cursor-pointer"
>

View File

@@ -42,12 +42,12 @@ const LoginPage: () => JSX.Element = () => {
try {
// get sso config by email.
const listResult: HTTPErrorResponse | HTTPResponse<JSONArray> =
await API.get(
URL.fromString(apiUrl.toString()).addQueryParam(
await API.get({
url: URL.fromString(apiUrl.toString()).addQueryParam(
"email",
email.toString(),
),
);
});
if (listResult instanceof HTTPErrorResponse) {
throw listResult;

View File

@@ -1,6 +1,6 @@
import Route from "Common/Types/API/Route";
import URL from "Common/Types/API/URL";
import { IDENTITY_URL } from "Common/UI/Config";
import { IDENTITY_URL, APP_API_URL } from "Common/UI/Config";
export const SIGNUP_API_URL: URL = URL.fromURL(IDENTITY_URL).addRoute(
new Route("/signup"),
@@ -9,9 +9,17 @@ export const LOGIN_API_URL: URL = URL.fromURL(IDENTITY_URL).addRoute(
new Route("/login"),
);
export const VERIFY_TWO_FACTOR_AUTH_API_URL: URL = URL.fromURL(
export const VERIFY_TOTP_AUTH_API_URL: URL = URL.fromURL(IDENTITY_URL).addRoute(
new Route("/verify-totp-auth"),
);
export const GENERATE_WEBAUTHN_AUTH_OPTIONS_API_URL: URL = URL.fromURL(
APP_API_URL,
).addRoute(new Route("/user-webauthn/generate-authentication-options"));
export const VERIFY_WEBAUTHN_AUTH_API_URL: URL = URL.fromURL(
IDENTITY_URL,
).addRoute(new Route("/verify-two-factor-auth"));
).addRoute(new Route("/verify-webauthn-auth"));
export const SERVICE_PROVIDER_LOGIN_URL: URL = URL.fromURL(
IDENTITY_URL,

View File

@@ -3,7 +3,7 @@
#
# Pull base image nodejs image.
FROM public.ecr.aws/docker/library/node:23.8-alpine3.21
FROM public.ecr.aws/docker/library/node:24.9-alpine3.21
RUN mkdir /tmp/npm && chmod 2777 /tmp/npm && chown 1000:1000 /tmp/npm && npm config set cache /tmp/npm --global
RUN npm config set fetch-retries 5
@@ -17,6 +17,7 @@ ARG APP_VERSION
ENV GIT_SHA=${GIT_SHA}
ENV APP_VERSION=${APP_VERSION}
ENV PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1
# IF APP_VERSION is not set, set it to 1.0.0

View File

@@ -1,7 +1,10 @@
{
"watch": ["./","../Common/UI", "../Common/Types", "../Common/Utils", "../Common/Models"],
"ext": "ts,json,tsx,env,js,jsx,hbs",
"ext": "ts,tsx",
"ignore": [
"./node_modules/**",
"./public/**",
"./bin/**",
"./public/**",
"./public/dist/**",
"./build/*",

View File

@@ -69,6 +69,7 @@
"crypto-js": "^4.2.0",
"dotenv": "^16.4.4",
"ejs": "^3.1.10",
"elkjs": "^0.10.0",
"esbuild": "^0.25.5",
"express": "^4.21.1",
"formik": "^2.4.6",

View File

@@ -5,6 +5,7 @@ import Projects from "./Pages/Projects/Index";
import SettingsAPIKey from "./Pages/Settings/APIKey/Index";
import SettingsAuthentication from "./Pages/Settings/Authentication/Index";
import SettingsCallSMS from "./Pages/Settings/CallSMS/Index";
import SettingsWhatsApp from "./Pages/Settings/WhatsApp/Index";
// Settings Pages.
import SettingsEmail from "./Pages/Settings/Email/Index";
import SettingsProbes from "./Pages/Settings/Probes/Index";
@@ -105,6 +106,11 @@ const App: () => JSX.Element = () => {
element={<SettingsCallSMS />}
/>
<PageRoute
path={RouteMap[PageMap.SETTINGS_WHATSAPP]?.toString() || ""}
element={<SettingsWhatsApp />}
/>
<PageRoute
path={RouteMap[PageMap.SETTINGS_PROBES]?.toString() || ""}
element={<SettingsProbes />}

View File

@@ -54,9 +54,9 @@ const DashboardFooter: () => JSX.Element = () => {
const fetchAppVersion: (appName: string) => Promise<JSONObject> = async (
appName: string,
): Promise<JSONObject> => {
const response: HTTPResponse<JSONObject> = await API.get<JSONObject>(
URL.fromString(`${HTTP_PROTOCOL}/${HOST}${appName}/version`),
);
const response: HTTPResponse<JSONObject> = await API.get<JSONObject>({
url: URL.fromString(`${HTTP_PROTOCOL}/${HOST}${appName}/version`),
});
if (response.data) {
return response.data as JSONObject;

View File

@@ -50,6 +50,15 @@ const DashboardSideMenu: () => JSX.Element = (): ReactElement => {
}}
icon={IconProp.Call}
/>
<SideMenuItem
link={{
title: "WhatsApp",
to: RouteUtil.populateRouteParams(
RouteMap[PageMap.SETTINGS_WHATSAPP] as Route,
),
}}
icon={IconProp.WhatsApp}
/>
</SideMenuSection>
<SideMenuSection title="Monitoring">

View File

@@ -0,0 +1,454 @@
import PageMap from "../../../Utils/PageMap";
import RouteMap, { RouteUtil } from "../../../Utils/RouteMap";
import DashboardSideMenu from "../SideMenu";
import Route from "Common/Types/API/Route";
import ObjectID from "Common/Types/ObjectID";
import FormFieldSchemaType from "Common/UI/Components/Forms/Types/FormFieldSchemaType";
import CardModelDetail from "Common/UI/Components/ModelDetail/CardModelDetail";
import Page from "Common/UI/Components/Page/Page";
import FieldType from "Common/UI/Components/Types/FieldType";
import GlobalConfig from "Common/Models/DatabaseModels/GlobalConfig";
import React, { FunctionComponent, ReactElement, useState } from "react";
import Card from "Common/UI/Components/Card/Card";
import MarkdownViewer from "Common/UI/Components/Markdown.tsx/MarkdownViewer";
import BasicForm from "Common/UI/Components/Forms/BasicForm";
import Alert, { AlertType } from "Common/UI/Components/Alerts/Alert";
import { JSONObject } from "Common/Types/JSON";
import HTTPErrorResponse from "Common/Types/API/HTTPErrorResponse";
import HTTPResponse from "Common/Types/API/HTTPResponse";
import URL from "Common/Types/API/URL";
import API from "Common/UI/Utils/API/API";
import { APP_API_URL } from "Common/UI/Config";
import WhatsAppTemplateMessages, {
WhatsAppTemplateId,
WhatsAppTemplateIds,
WhatsAppTemplateLanguage,
} from "Common/Types/WhatsApp/WhatsAppTemplates";
type ToFriendlyName = (value: string) => string;
const toFriendlyName: ToFriendlyName = (value: string): string => {
return value
.replace(/([a-z0-9])([A-Z])/g, "$1 $2")
.replace(/_/g, " ")
.replace(/\s+/g, " ")
.trim();
};
type ExtractTemplateVariables = (template: string) => Array<string>;
const extractTemplateVariables: ExtractTemplateVariables = (
template: string,
): Array<string> => {
const matches: RegExpMatchArray | null = template.match(/\{\{(.*?)\}\}/g);
if (!matches) {
return [];
}
const uniqueVariables: Set<string> = new Set<string>();
for (const match of matches) {
const variable: string = match.replace("{{", "").replace("}}", "").trim();
if (variable) {
uniqueVariables.add(variable);
}
}
return Array.from(uniqueVariables).sort((a: string, b: string) => {
return a.localeCompare(b);
});
};
type BuildWhatsAppSetupMarkdown = () => string;
const buildWhatsAppSetupMarkdown: BuildWhatsAppSetupMarkdown = (): string => {
const templateKeys: Array<keyof typeof WhatsAppTemplateIds> = Object.keys(
WhatsAppTemplateIds,
) as Array<keyof typeof WhatsAppTemplateIds>;
const description: string =
"Follow these steps to connect Meta WhatsApp with OneUptime so notifications can be delivered via WhatsApp.";
const prerequisitesList: Array<string> = [
"Meta Business Manager admin access for the WhatsApp Business Account.",
"A WhatsApp Business phone number approved for API messaging.",
"Admin access to OneUptime with permission to edit global notification settings.",
];
const setupStepsList: Array<string> = [
"Sign in to the [Meta Business Manager](https://business.facebook.com/) with admin access to your WhatsApp Business Account.",
"From **Business Settings → Accounts → WhatsApp Accounts**, create or select the account that owns your sender phone number.",
"In Buisness Portfolio, create a system user and assign it to the WhatsApp Business Account with the role of **Admin**.",
"Under **WhatsApp Manager → API Setup**, generate a long-lived access token and copy the phone number ID.",
"Paste the access token and phone number ID into the **Meta WhatsApp Settings** card above, then save.",
"For the **Business Account ID**, go to **Business Settings → Business Info** (or **Business Settings → WhatsApp Accounts → Settings**) and copy the **WhatsApp Business Account ID** value.",
"To locate the **App ID** and **App Secret**, open [Meta for Developers](https://developers.facebook.com/apps/), select your WhatsApp app, then navigate to **Settings → Basic**. The App ID is shown at the top; click **Show** next to **App Secret** to reveal and copy it.",
"Create each template listed below in the Meta WhatsApp Manager. Make sure the template name, language, and variables match exactly. Please make sure it's approved by Meta.",
"Send a test notification from OneUptime to confirm that WhatsApp delivery succeeds.",
];
const prerequisitesMarkdown: string = prerequisitesList
.map((item: string) => {
return `- ${item}`;
})
.join("\n");
const setupStepsMarkdown: string = setupStepsList
.map((item: string, index: number) => {
return `${index + 1}. ${item}`;
})
.join("\n");
const tableRows: string = templateKeys
.map((enumKey: keyof typeof WhatsAppTemplateIds) => {
const templateId: WhatsAppTemplateId = WhatsAppTemplateIds[enumKey];
const friendlyName: string = toFriendlyName(enumKey.toString());
const templateMessage: string = WhatsAppTemplateMessages[templateId];
const language: string = WhatsAppTemplateLanguage[templateId] || "en";
const variables: Array<string> =
extractTemplateVariables(templateMessage);
const variableList: string =
variables.length > 0
? variables
.map((variable: string) => {
return `\`${variable}\``;
})
.join(", ")
: "_None_";
return `| ${friendlyName} | \`${templateId}\` | ${language} | ${variableList} |`;
})
.join("\n");
const templateBodies: string = templateKeys
.map((enumKey: keyof typeof WhatsAppTemplateIds) => {
const templateId: WhatsAppTemplateId = WhatsAppTemplateIds[enumKey];
const friendlyName: string = toFriendlyName(enumKey.toString());
const templateMessage: string = WhatsAppTemplateMessages[templateId];
const language: string = WhatsAppTemplateLanguage[templateId] || "en";
const variables: Array<string> =
extractTemplateVariables(templateMessage);
const variableMarkdown: string =
variables.length > 0
? variables
.map((variable: string) => {
return `- \`${variable}\``;
})
.join("\n")
: "_None_";
const variablesHeading: string = variables.length
? `**Variables (${variables.length})**`
: "**Variables**";
return [
`#### ${friendlyName}`,
"",
`**Template Name:** \`${templateId}\``,
`**Language:** ${language}`,
"",
variablesHeading,
variableMarkdown,
"",
"**Body**",
"```plaintext",
templateMessage,
"```",
"",
"---",
].join("\n");
})
.join("\n\n");
const templateSummaryTable: string = [
"| Friendly Name | Template Name | Language | Variables |",
"| --- | --- | --- | --- |",
tableRows,
]
.filter(Boolean)
.join("\n");
return [
description,
"### Prerequisites",
prerequisitesMarkdown,
"### Setup Steps",
setupStepsMarkdown,
"### Required WhatsApp Templates",
templateSummaryTable,
"### Template Bodies",
"> Copy the exact template body below—including punctuation and spacing—when creating each template inside Meta. The variables list shows every placeholder that must be configured in WhatsApp Manager.",
templateBodies,
]
.filter(Boolean)
.join("\n\n");
};
const whatsappSetupMarkdown: string = buildWhatsAppSetupMarkdown();
const SettingsWhatsApp: FunctionComponent = (): ReactElement => {
const [isSendingTest, setIsSendingTest] = useState<boolean>(false);
const [testError, setTestError] = useState<string>("");
const [testSuccess, setTestSuccess] = useState<string>("");
return (
<Page
title={"Admin Settings"}
breadcrumbLinks={[
{
title: "Admin Dashboard",
to: RouteUtil.populateRouteParams(RouteMap[PageMap.HOME] as Route),
},
{
title: "Settings",
to: RouteUtil.populateRouteParams(
RouteMap[PageMap.SETTINGS] as Route,
),
},
{
title: "WhatsApp",
to: RouteUtil.populateRouteParams(
RouteMap[PageMap.SETTINGS_WHATSAPP] as Route,
),
},
]}
sideMenu={<DashboardSideMenu />}
>
<CardModelDetail
name="Meta WhatsApp Settings"
cardProps={{
title: "Meta WhatsApp Settings",
description:
"Configure Meta WhatsApp credentials. These values are used to send WhatsApp notifications from OneUptime.",
}}
isEditable={true}
editButtonText="Edit Meta WhatsApp Config"
formSteps={[
{
title: "Credentials",
id: "meta-credentials",
},
{
title: "Meta App",
id: "meta-app",
},
]}
formFields={[
{
field: {
metaWhatsAppAccessToken: true,
},
title: "Access Token",
stepId: "meta-credentials",
fieldType: FormFieldSchemaType.EncryptedText,
required: true,
description:
"Long-lived access token generated in the Meta WhatsApp Business Platform.",
placeholder: "EAAG...",
},
{
field: {
metaWhatsAppPhoneNumberId: true,
},
title: "Phone Number ID",
stepId: "meta-credentials",
fieldType: FormFieldSchemaType.Text,
required: true,
description:
"The WhatsApp Business phone number ID associated with your sending number.",
placeholder: "123456789012345",
},
{
field: {
metaWhatsAppBusinessAccountId: true,
},
title: "Business Account ID",
stepId: "meta-credentials",
fieldType: FormFieldSchemaType.Text,
required: false,
description:
"Optional Business Account ID that owns the WhatsApp templates.",
placeholder: "123456789012345",
},
{
field: {
metaWhatsAppAppId: true,
},
title: "App ID",
stepId: "meta-app",
fieldType: FormFieldSchemaType.Text,
required: false,
description:
"Optional Facebook App ID tied to your WhatsApp integration.",
placeholder: "987654321098765",
},
{
field: {
metaWhatsAppAppSecret: true,
},
title: "App Secret",
stepId: "meta-app",
fieldType: FormFieldSchemaType.EncryptedText,
required: false,
description:
"Optional Facebook App Secret used for webhook signature verification.",
placeholder: "Facebook App Secret",
},
]}
modelDetailProps={{
modelType: GlobalConfig,
id: "model-detail-global-config-meta-whatsapp",
fields: [
{
field: {
metaWhatsAppAccessToken: true,
},
title: "Access Token",
fieldType: FieldType.HiddenText,
placeholder: "Not Configured",
},
{
field: {
metaWhatsAppPhoneNumberId: true,
},
title: "Phone Number ID",
fieldType: FieldType.Text,
placeholder: "Not Configured",
},
{
field: {
metaWhatsAppBusinessAccountId: true,
},
title: "Business Account ID",
fieldType: FieldType.Text,
placeholder: "Not Configured",
},
{
field: {
metaWhatsAppAppId: true,
},
title: "App ID",
fieldType: FieldType.Text,
placeholder: "Not Configured",
},
{
field: {
metaWhatsAppAppSecret: true,
},
title: "App Secret",
fieldType: FieldType.HiddenText,
placeholder: "Not Configured",
},
],
modelId: ObjectID.getZeroObjectID(),
}}
/>
<Card
title="Send Test WhatsApp Message"
description="Send a test WhatsApp template message to confirm your Meta configuration."
>
{testSuccess ? (
<Alert
type={AlertType.SUCCESS}
title={testSuccess}
className="mb-4"
/>
) : (
<></>
)}
<BasicForm
id="send-test-whatsapp-form"
name="Send Test WhatsApp Message"
isLoading={isSendingTest}
error={testError || ""}
submitButtonText="Send Test Message"
maxPrimaryButtonWidth={true}
initialValues={{
phoneNumber: "",
}}
fields={[
{
field: {
phoneNumber: true,
},
title: "Recipient WhatsApp Number",
description:
"Enter the full international phone number (including country code) that should receive the test message.",
placeholder: "+11234567890",
required: true,
fieldType: FormFieldSchemaType.Phone,
disableSpellCheck: true,
},
]}
onSubmit={async (
values: JSONObject,
onSubmitSuccessful?: () => void,
) => {
const toPhone: string = String(values["phoneNumber"] || "").trim();
if (!toPhone) {
setTestSuccess("");
setTestError(
"Please enter a WhatsApp number before sending a test message.",
);
return;
}
setIsSendingTest(true);
setTestError("");
setTestSuccess("");
try {
const response: HTTPResponse<JSONObject> | HTTPErrorResponse =
await API.post({
url: URL.fromString(APP_API_URL.toString()).addRoute(
"/notification/whatsapp/test",
),
data: {
toPhone,
templateKey: WhatsAppTemplateIds.TestNotification,
templateLanguageCode:
WhatsAppTemplateLanguage[
WhatsAppTemplateIds.TestNotification
],
},
});
if (response instanceof HTTPErrorResponse) {
throw response;
}
if (response.isFailure()) {
throw new Error("Failed to send test WhatsApp message.");
}
setTestSuccess(
"Test WhatsApp message sent successfully. Check the recipient device to confirm delivery.",
);
if (onSubmitSuccessful) {
onSubmitSuccessful();
}
} catch (err) {
setTestError(API.getFriendlyMessage(err));
} finally {
setIsSendingTest(false);
}
}}
/>
</Card>
<Card
title="Meta WhatsApp Setup Guide"
description="Steps to connect Meta WhatsApp and the templates you must provision."
>
<MarkdownViewer text={whatsappSetupMarkdown} />
</Card>
</Page>
);
};
export default SettingsWhatsApp;

View File

@@ -15,6 +15,7 @@ enum PageMap {
SETTINGS_HOST = "SETTINGS_HOST",
SETTINGS_SMTP = "SETTINGS_SMTP",
SETTINGS_CALL_AND_SMS = "SETTINGS_CALL_AND_SMS",
SETTINGS_WHATSAPP = "SETTINGS_WHATSAPP",
SETTINGS_PROBES = "SETTINGS_PROBES",
SETTINGS_AUTHENTICATION = "SETTINGS_AUTHENTICATION",
SETTINGS_API_KEY = "SETTINGS_API_KEY",

View File

@@ -25,6 +25,7 @@ const RouteMap: Dictionary<Route> = {
[PageMap.SETTINGS_HOST]: new Route(`/admin/settings/host`),
[PageMap.SETTINGS_SMTP]: new Route(`/admin/settings/smtp`),
[PageMap.SETTINGS_CALL_AND_SMS]: new Route(`/admin/settings/call-and-sms`),
[PageMap.SETTINGS_WHATSAPP]: new Route(`/admin/settings/whatsapp`),
[PageMap.SETTINGS_PROBES]: new Route(`/admin/settings/probes`),
[PageMap.SETTINGS_AUTHENTICATION]: new Route(
`/admin/settings/authentication`,

View File

@@ -3,7 +3,7 @@
#
# Pull base image nodejs image.
FROM public.ecr.aws/docker/library/node:23.8-alpine3.21
FROM public.ecr.aws/docker/library/node:24.9-alpine3.21
RUN mkdir /tmp/npm && chmod 2777 /tmp/npm && chown 1000:1000 /tmp/npm && npm config set cache /tmp/npm --global
RUN npm config set fetch-retries 5
@@ -17,6 +17,7 @@ ARG APP_VERSION
ENV GIT_SHA=${GIT_SHA}
ENV APP_VERSION=${APP_VERSION}
ENV PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1
# IF APP_VERSION is not set, set it to 1.0.0

View File

@@ -1,5 +1,6 @@
import BaseAPI from "Common/Server/API/BaseAPI";
import BaseAnalyticsAPI from "Common/Server/API/BaseAnalyticsAPI";
import BillingAPI from "Common/Server/API/BillingAPI";
import BillingInvoiceAPI from "Common/Server/API/BillingInvoiceAPI";
import BillingPaymentMethodAPI from "Common/Server/API/BillingPaymentMethodAPI";
import CopilotCodeRepositoryAPI from "Common/Server/API/CopilotCodeRepositoryAPI";
@@ -23,12 +24,14 @@ import WorkspaceNotificationRuleAPI from "Common/Server/API/WorkspaceNotificatio
import StatusPageDomainAPI from "Common/Server/API/StatusPageDomainAPI";
import StatusPageSubscriberAPI from "Common/Server/API/StatusPageSubscriberAPI";
import UserCallAPI from "Common/Server/API/UserCallAPI";
import UserTwoFactorAuthAPI from "Common/Server/API/UserTwoFactorAuthAPI";
import UserTotpAuthAPI from "Common/Server/API/UserTotpAuthAPI";
import UserWebAuthnAPI from "Common/Server/API/UserWebAuthnAPI";
import MonitorTest from "Common/Models/DatabaseModels/MonitorTest";
// User Notification methods.
import UserEmailAPI from "Common/Server/API/UserEmailAPI";
import UserNotificationLogTimelineAPI from "Common/Server/API/UserOnCallLogTimelineAPI";
import UserSMSAPI from "Common/Server/API/UserSmsAPI";
import UserWhatsAppAPI from "Common/Server/API/UserWhatsAppAPI";
import UserPushAPI from "Common/Server/API/UserPushAPI";
import ApiKeyPermissionService, {
Service as ApiKeyPermissionServiceType,
@@ -282,6 +285,12 @@ import ShortLinkService, {
import SmsLogService, {
Service as SmsLogServiceType,
} from "Common/Server/Services/SmsLogService";
import WhatsAppLogService, {
Service as WhatsAppLogServiceType,
} from "Common/Server/Services/WhatsAppLogService";
import PushNotificationLogService, {
Service as PushNotificationLogServiceType,
} from "Common/Server/Services/PushNotificationLogService";
import SpanService, {
SpanService as SpanServiceType,
} from "Common/Server/Services/SpanService";
@@ -324,6 +333,9 @@ import TeamMemberService, {
import TeamPermissionService, {
Service as TeamPermissionServiceType,
} from "Common/Server/Services/TeamPermissionService";
import TeamComplianceSettingService, {
TeamComplianceSettingService as TeamComplianceSettingServiceType,
} from "Common/Server/Services/TeamComplianceSettingService";
import TeamService, {
Service as TeamServiceType,
} from "Common/Server/Services/TeamService";
@@ -379,6 +391,8 @@ import Span from "Common/Models/AnalyticsModels/Span";
import ApiKey from "Common/Models/DatabaseModels/ApiKey";
import ApiKeyPermission from "Common/Models/DatabaseModels/ApiKeyPermission";
import CallLog from "Common/Models/DatabaseModels/CallLog";
import PushNotificationLog from "Common/Models/DatabaseModels/PushNotificationLog";
import WorkspaceNotificationLog from "Common/Models/DatabaseModels/WorkspaceNotificationLog";
import Domain from "Common/Models/DatabaseModels/Domain";
import EmailLog from "Common/Models/DatabaseModels/EmailLog";
import EmailVerificationToken from "Common/Models/DatabaseModels/EmailVerificationToken";
@@ -448,6 +462,7 @@ import ServiceCatalogOwnerUser from "Common/Models/DatabaseModels/ServiceCatalog
import ServiceCopilotCodeRepository from "Common/Models/DatabaseModels/ServiceCopilotCodeRepository";
import ShortLink from "Common/Models/DatabaseModels/ShortLink";
import SmsLog from "Common/Models/DatabaseModels/SmsLog";
import WhatsAppLog from "Common/Models/DatabaseModels/WhatsAppLog";
import StatusPageAnnouncement from "Common/Models/DatabaseModels/StatusPageAnnouncement";
// Custom Fields API
import StatusPageCustomField from "Common/Models/DatabaseModels/StatusPageCustomField";
@@ -464,6 +479,7 @@ import StatusPageSSO from "Common/Models/DatabaseModels/StatusPageSso";
import Team from "Common/Models/DatabaseModels/Team";
import TeamMember from "Common/Models/DatabaseModels/TeamMember";
import TeamPermission from "Common/Models/DatabaseModels/TeamPermission";
import TeamComplianceSetting from "Common/Models/DatabaseModels/TeamComplianceSetting";
import TelemetryService from "Common/Models/DatabaseModels/TelemetryService";
import TelemetryUsageBilling from "Common/Models/DatabaseModels/TelemetryUsageBilling";
import User from "Common/Models/DatabaseModels/User";
@@ -480,6 +496,9 @@ import TelemetryAttribute from "Common/Models/AnalyticsModels/TelemetryAttribute
import ExceptionInstance from "Common/Models/AnalyticsModels/ExceptionInstance";
import TelemetyException from "Common/Models/DatabaseModels/TelemetryException";
import CopilotActionTypePriority from "Common/Models/DatabaseModels/CopilotActionTypePriority";
import WorkspaceNotificationLogService, {
Service as WorkspaceNotificationLogServiceType,
} from "Common/Server/Services/WorkspaceNotificationLogService";
// scheduled maintenance template
import ScheduledMaintenanceTemplate from "Common/Models/DatabaseModels/ScheduledMaintenanceTemplate";
@@ -513,6 +532,7 @@ import ScheduledMaintenanceFeedService, {
} from "Common/Server/Services/ScheduledMaintenanceFeedService";
import SlackAPI from "Common/Server/API/SlackAPI";
import MicrosoftTeamsAPI from "Common/Server/API/MicrosoftTeamsAPI";
import WorkspaceProjectAuthToken from "Common/Models/DatabaseModels/WorkspaceProjectAuthToken";
import WorkspaceProjectAuthTokenService, {
@@ -530,11 +550,6 @@ import WorkspaceSettingService, {
Service as WorkspaceSettingServiceType,
} from "Common/Server/Services/WorkspaceSettingService";
import ProjectUser from "Common/Models/DatabaseModels/ProjectUser";
import ProjectUserService, {
Service as ProjectUserServiceType,
} from "Common/Server/Services/ProjectUserService";
import MonitorFeed from "Common/Models/DatabaseModels/MonitorFeed";
import MonitorFeedService, {
Service as MonitorFeedServiceType,
@@ -547,10 +562,7 @@ import MetricTypeService, {
import MetricType from "Common/Models/DatabaseModels/MetricType";
import OnCallDutyPolicyAPI from "Common/Server/API/OnCallDutyPolicyAPI";
// OnCallDutyPolicyOwnerTeam
// OnCallDutyPolicyOwnerUser
// OnCallDutyPolicyFeed
import TeamComplianceAPI from "Common/Server/API/TeamComplianceAPI";
import OnCallDutyPolicyFeed from "Common/Models/DatabaseModels/OnCallDutyPolicyFeed";
import OnCallDutyPolicyFeedService, {
@@ -583,6 +595,18 @@ import StatusPageAnnouncementTemplateService, {
Service as StatusPageAnnouncementTemplateServiceType,
} from "Common/Server/Services/StatusPageAnnouncementTemplateService";
// ProjectSCIM
import ProjectSCIM from "Common/Models/DatabaseModels/ProjectSCIM";
import ProjectSCIMService, {
Service as ProjectSCIMServiceType,
} from "Common/Server/Services/ProjectSCIMService";
// StatusPageSCIM
import StatusPageSCIM from "Common/Models/DatabaseModels/StatusPageSCIM";
import StatusPageSCIMService, {
Service as StatusPageSCIMServiceType,
} from "Common/Server/Services/StatusPageSCIMService";
// Open API Spec
import OpenAPI from "Common/Server/API/OpenAPI";
@@ -618,6 +642,24 @@ const BaseAPIFeatureSet: FeatureSet = {
).getRouter(),
);
// Project SCIM
app.use(
`/${APP_NAME.toLocaleLowerCase()}`,
new BaseAPI<ProjectSCIM, ProjectSCIMServiceType>(
ProjectSCIM,
ProjectSCIMService,
).getRouter(),
);
// Status Page SCIM
app.use(
`/${APP_NAME.toLocaleLowerCase()}`,
new BaseAPI<StatusPageSCIM, StatusPageSCIMServiceType>(
StatusPageSCIM,
StatusPageSCIMService,
).getRouter(),
);
// status page announcement templates
app.use(
`/${APP_NAME.toLocaleLowerCase()}`,
@@ -698,14 +740,6 @@ const BaseAPIFeatureSet: FeatureSet = {
).getRouter(),
);
app.use(
`/${APP_NAME.toLocaleLowerCase()}`,
new BaseAPI<ProjectUser, ProjectUserServiceType>(
ProjectUser,
ProjectUserService,
).getRouter(),
);
//service provider setting
app.use(
`/${APP_NAME.toLocaleLowerCase()}`,
@@ -1142,6 +1176,14 @@ const BaseAPIFeatureSet: FeatureSet = {
).getRouter(),
);
app.use(
`/${APP_NAME.toLocaleLowerCase()}`,
new BaseAPI<TeamComplianceSetting, TeamComplianceSettingServiceType>(
TeamComplianceSetting,
TeamComplianceSettingService,
).getRouter(),
);
app.use(
`/${APP_NAME.toLocaleLowerCase()}`,
new BaseAPI<MonitorStatus, MonitorStatusServiceType>(
@@ -1501,6 +1543,30 @@ const BaseAPIFeatureSet: FeatureSet = {
new BaseAPI<SmsLog, SmsLogServiceType>(SmsLog, SmsLogService).getRouter(),
);
app.use(
`/${APP_NAME.toLocaleLowerCase()}`,
new BaseAPI<WhatsAppLog, WhatsAppLogServiceType>(
WhatsAppLog,
WhatsAppLogService,
).getRouter(),
);
app.use(
`/${APP_NAME.toLocaleLowerCase()}`,
new BaseAPI<PushNotificationLog, PushNotificationLogServiceType>(
PushNotificationLog,
PushNotificationLogService,
).getRouter(),
);
app.use(
`/${APP_NAME.toLocaleLowerCase()}`,
new BaseAPI<
WorkspaceNotificationLog,
WorkspaceNotificationLogServiceType
>(WorkspaceNotificationLog, WorkspaceNotificationLogService).getRouter(),
);
app.use(
`/${APP_NAME.toLocaleLowerCase()}`,
new BaseAPI<EmailLog, EmailLogServiceType>(
@@ -1540,7 +1606,6 @@ const BaseAPIFeatureSet: FeatureSet = {
MonitorTimelineStatusService,
).getRouter(),
);
app.use(`/${APP_NAME.toLocaleLowerCase()}`, new ShortLinkAPI().getRouter());
app.use(`/${APP_NAME.toLocaleLowerCase()}`, new MonitorAPI().getRouter());
app.use(
@@ -1554,6 +1619,12 @@ const BaseAPIFeatureSet: FeatureSet = {
new OnCallDutyPolicyAPI().getRouter(),
);
// TeamComplianceAPI
app.use(
`/${APP_NAME.toLocaleLowerCase()}`,
new TeamComplianceAPI().getRouter(),
);
app.use(
`/${APP_NAME.toLocaleLowerCase()}`,
new WorkspaceNotificationRuleAPI().getRouter(),
@@ -1578,6 +1649,10 @@ const BaseAPIFeatureSet: FeatureSet = {
new ResellerPlanAPI().getRouter(),
);
app.use(`/${APP_NAME.toLocaleLowerCase()}`, new SlackAPI().getRouter());
app.use(
`/${APP_NAME.toLocaleLowerCase()}`,
new MicrosoftTeamsAPI().getRouter(),
);
app.use(
`/${APP_NAME.toLocaleLowerCase()}`,
new GlobalConfigAPI().getRouter(),
@@ -1605,10 +1680,18 @@ const BaseAPIFeatureSet: FeatureSet = {
app.use(`/${APP_NAME.toLocaleLowerCase()}`, new UserCallAPI().getRouter());
app.use(
`/${APP_NAME.toLocaleLowerCase()}`,
new UserTwoFactorAuthAPI().getRouter(),
new UserTotpAuthAPI().getRouter(),
);
app.use(
`/${APP_NAME.toLocaleLowerCase()}`,
new UserWebAuthnAPI().getRouter(),
);
app.use(`/${APP_NAME.toLocaleLowerCase()}`, new UserEmailAPI().getRouter());
app.use(`/${APP_NAME.toLocaleLowerCase()}`, new UserSMSAPI().getRouter());
app.use(
`/${APP_NAME.toLocaleLowerCase()}`,
new UserWhatsAppAPI().getRouter(),
);
app.use(`/${APP_NAME.toLocaleLowerCase()}`, new UserPushAPI().getRouter());
app.use(`/${APP_NAME.toLocaleLowerCase()}`, new ProbeAPI().getRouter());
@@ -1629,6 +1712,8 @@ const BaseAPIFeatureSet: FeatureSet = {
new BillingInvoiceAPI().getRouter(),
);
app.use(`/${APP_NAME.toLocaleLowerCase()}`, new BillingAPI().getRouter());
app.use(
`/${APP_NAME.toLocaleLowerCase()}`,
new BaseAPI<

View File

@@ -24,7 +24,7 @@ import AccessTokenService from "Common/Server/Services/AccessTokenService";
import EmailVerificationTokenService from "Common/Server/Services/EmailVerificationTokenService";
import MailService from "Common/Server/Services/MailService";
import UserService from "Common/Server/Services/UserService";
import UserTwoFactorAuthService from "Common/Server/Services/UserTwoFactorAuthService";
import UserTotpAuthService from "Common/Server/Services/UserTotpAuthService";
import CookieUtil from "Common/Server/Utils/Cookie";
import Express, {
ExpressRequest,
@@ -34,10 +34,12 @@ import Express, {
} from "Common/Server/Utils/Express";
import logger from "Common/Server/Utils/Logger";
import Response from "Common/Server/Utils/Response";
import TwoFactorAuth from "Common/Server/Utils/TwoFactorAuth";
import TotpAuth from "Common/Server/Utils/TotpAuth";
import EmailVerificationToken from "Common/Models/DatabaseModels/EmailVerificationToken";
import User from "Common/Models/DatabaseModels/User";
import UserTwoFactorAuth from "Common/Models/DatabaseModels/UserTwoFactorAuth";
import UserTotpAuth from "Common/Models/DatabaseModels/UserTotpAuth";
import UserWebAuthn from "Common/Models/DatabaseModels/UserWebAuthn";
import UserWebAuthnService from "Common/Server/Services/UserWebAuthnService";
const router: ExpressRouter = Express.getRouter();
@@ -503,7 +505,7 @@ router.post(
);
router.post(
"/verify-two-factor-auth",
"/verify-totp-auth",
async (
req: ExpressRequest,
res: ExpressResponse,
@@ -513,7 +515,25 @@ router.post(
req: req,
res: res,
next: next,
verifyTwoFactorAuth: true,
verifyTotpAuth: true,
verifyWebAuthn: false,
});
},
);
router.post(
"/verify-webauthn-auth",
async (
req: ExpressRequest,
res: ExpressResponse,
next: NextFunction,
): Promise<void> => {
return login({
req: req,
res: res,
next: next,
verifyTotpAuth: false,
verifyWebAuthn: true,
});
},
);
@@ -529,62 +549,99 @@ router.post(
req: req,
res: res,
next: next,
verifyTwoFactorAuth: false,
verifyTotpAuth: false,
verifyWebAuthn: false,
});
},
);
type FetchTwoFactorAuthListFunction = (
userId: ObjectID,
) => Promise<Array<UserTwoFactorAuth>>;
type FetchTotpAuthListFunction = (userId: ObjectID) => Promise<{
totpAuthList: Array<UserTotpAuth>;
webAuthnList: Array<UserWebAuthn>;
}>;
const fetchTwoFactorAuthList: FetchTwoFactorAuthListFunction = async (
const fetchTotpAuthList: FetchTotpAuthListFunction = async (
userId: ObjectID,
): Promise<Array<UserTwoFactorAuth>> => {
const twoFactorAuthList: Array<UserTwoFactorAuth> =
await UserTwoFactorAuthService.findBy({
query: {
userId: userId,
isVerified: true,
},
select: {
_id: true,
userId: true,
name: true,
},
limit: LIMIT_PER_PROJECT,
skip: 0,
props: {
isRoot: true,
},
});
): Promise<{
totpAuthList: Array<UserTotpAuth>;
webAuthnList: Array<UserWebAuthn>;
}> => {
const totpAuthList: Array<UserTotpAuth> = await UserTotpAuthService.findBy({
query: {
userId: userId,
isVerified: true,
},
select: {
_id: true,
userId: true,
name: true,
},
limit: LIMIT_PER_PROJECT,
skip: 0,
props: {
isRoot: true,
},
});
return twoFactorAuthList;
const webAuthnList: Array<UserWebAuthn> = await UserWebAuthnService.findBy({
query: {
userId: userId,
isVerified: true,
},
select: {
_id: true,
userId: true,
name: true,
},
limit: LIMIT_PER_PROJECT,
skip: 0,
props: {
isRoot: true,
},
});
return {
totpAuthList: totpAuthList || [],
webAuthnList: webAuthnList || [],
};
};
type LoginFunction = (options: {
req: ExpressRequest;
res: ExpressResponse;
next: NextFunction;
verifyTwoFactorAuth: boolean;
verifyTotpAuth: boolean;
verifyWebAuthn: boolean;
}) => Promise<void>;
const login: LoginFunction = async (options: {
req: ExpressRequest;
res: ExpressResponse;
next: NextFunction;
verifyTwoFactorAuth: boolean;
verifyTotpAuth: boolean;
verifyWebAuthn: boolean;
}): Promise<void> => {
const req: ExpressRequest = options.req;
const res: ExpressResponse = options.res;
const next: NextFunction = options.next;
const verifyTwoFactorAuth: boolean = options.verifyTwoFactorAuth;
const verifyTotpAuth: boolean = options.verifyTotpAuth;
const verifyWebAuthn: boolean = options.verifyWebAuthn;
try {
const data: JSONObject = req.body["data"];
logger.debug("Login request data: " + JSON.stringify(req.body, null, 2));
const user: User = BaseModel.fromJSON(data as JSONObject, User) as User;
if (!user.email || !user.password) {
return Response.sendErrorResponse(
req,
res,
new BadDataException("Email and password are required."),
);
}
await user.password?.hashValue(EncryptionSecret);
const alreadySavedUser: User | null = await UserService.findOneBy({
@@ -638,13 +695,21 @@ const login: LoginFunction = async (options: {
);
}
if (alreadySavedUser.enableTwoFactorAuth && !verifyTwoFactorAuth) {
if (
alreadySavedUser.enableTwoFactorAuth &&
!verifyTotpAuth &&
!verifyWebAuthn
) {
// If two factor auth is enabled then we will send the user to the two factor auth page.
const twoFactorAuthList: Array<UserTwoFactorAuth> =
await fetchTwoFactorAuthList(alreadySavedUser.id!);
const { totpAuthList, webAuthnList } = await fetchTotpAuthList(
alreadySavedUser.id!,
);
if (!twoFactorAuthList || twoFactorAuthList.length === 0) {
if (
(!totpAuthList || totpAuthList.length === 0) &&
(!webAuthnList || webAuthnList.length === 0)
) {
const errorMessage: string = IsBillingEnabled
? "Two Factor Authentication is enabled but no two factor auth is setup. Please contact OneUptime support for help."
: "Two Factor Authentication is enabled but no two factor auth is setup. Please contact your server admin to disable two factor auth for this account.";
@@ -656,62 +721,68 @@ const login: LoginFunction = async (options: {
);
}
return Response.sendEntityResponse(req, res, user, User, {
return Response.sendEntityResponse(req, res, alreadySavedUser, User, {
miscData: {
twoFactorAuthList: UserTwoFactorAuth.toJSONArray(
twoFactorAuthList,
UserTwoFactorAuth,
),
twoFactorAuth: true,
totpAuthList: UserTotpAuth.toJSONArray(totpAuthList, UserTotpAuth),
webAuthnList: UserWebAuthn.toJSONArray(webAuthnList, UserWebAuthn),
},
});
}
if (verifyTwoFactorAuth) {
// code from req
const code: string = data["code"] as string;
const twoFactorAuthId: string = data["twoFactorAuthId"] as string;
if (verifyTotpAuth || verifyWebAuthn) {
if (verifyTotpAuth) {
// code from req
const code: string = data["code"] as string;
const twoFactorAuthId: string = data["twoFactorAuthId"] as string;
const twoFactorAuth: UserTwoFactorAuth | null =
await UserTwoFactorAuthService.findOneBy({
query: {
_id: twoFactorAuthId,
userId: alreadySavedUser.id!,
isVerified: true,
},
select: {
_id: true,
twoFactorSecret: true,
},
props: {
isRoot: true,
},
const totpAuth: UserTotpAuth | null =
await UserTotpAuthService.findOneBy({
query: {
_id: twoFactorAuthId,
userId: alreadySavedUser.id!,
isVerified: true,
},
select: {
_id: true,
twoFactorSecret: true,
},
props: {
isRoot: true,
},
});
if (!totpAuth) {
return Response.sendErrorResponse(
req,
res,
new BadDataException("Invalid two factor auth id."),
);
}
const isVerified: boolean = TotpAuth.verifyToken({
token: code,
secret: totpAuth.twoFactorSecret!,
email: alreadySavedUser.email!,
});
if (!twoFactorAuth) {
return Response.sendErrorResponse(
req,
res,
new BadDataException("Invalid two factor auth id."),
);
if (!isVerified) {
return Response.sendErrorResponse(
req,
res,
new BadDataException("Invalid code."),
);
}
} else if (verifyWebAuthn) {
const expectedChallenge: string = data["challenge"] as string;
const credential: any = data["credential"];
await UserWebAuthnService.verifyAuthentication({
userId: alreadySavedUser.id!.toString(),
challenge: expectedChallenge,
credential: credential,
});
}
const isVerified: boolean = TwoFactorAuth.verifyToken({
token: code,
secret: twoFactorAuth.twoFactorSecret!,
email: alreadySavedUser.email!,
});
if (!isVerified) {
return Response.sendErrorResponse(
req,
res,
new BadDataException("Invalid code."),
);
}
}
// Refresh Permissions for this user here.
} // Refresh Permissions for this user here.
await AccessTokenService.refreshUserAllPermissions(alreadySavedUser.id!);
if (alreadySavedUser.password.toString() === user.password!.toString()) {

File diff suppressed because it is too large Load Diff

View File

@@ -40,8 +40,10 @@ import Name from "Common/Types/Name";
const router: ExpressRouter = Express.getRouter();
// This route is used to get the SSO config for the user.
// when the user logs in from OneUptime and not from the IDP.
/*
* This route is used to get the SSO config for the user.
* when the user logs in from OneUptime and not from the IDP.
*/
router.get(
"/service-provider-login",
@@ -434,8 +436,10 @@ const loginUserWithSso: LoginUserWithSsoFunction = async (
isNewUser = true;
}
// If he does not then add him to teams that he should belong and log in.
// This should never happen because email is verified before he logs in with SSO.
/*
* If he does not then add him to teams that he should belong and log in.
* This should never happen because email is verified before he logs in with SSO.
*/
if (!alreadySavedUser.isEmailVerified && !isNewUser) {
await AuthenticationEmail.sendVerificationEmail(alreadySavedUser!);

View File

@@ -168,6 +168,7 @@ router.post(
},
{
projectId: statusPage.projectId!,
statusPageId: statusPage.id!,
},
).catch((err: Error) => {
logger.error(err);
@@ -306,6 +307,7 @@ router.post(
},
{
projectId: statusPage.projectId!,
statusPageId: statusPage.id!,
},
).catch((err: Error) => {
logger.error(err);

View File

@@ -0,0 +1,570 @@
import SCIMMiddleware from "Common/Server/Middleware/SCIMAuthorization";
import StatusPagePrivateUserService from "Common/Server/Services/StatusPagePrivateUserService";
import Express, {
ExpressRequest,
ExpressResponse,
ExpressRouter,
OneUptimeRequest,
} from "Common/Server/Utils/Express";
import Response from "Common/Server/Utils/Response";
import logger from "Common/Server/Utils/Logger";
import ObjectID from "Common/Types/ObjectID";
import Email from "Common/Types/Email";
import { JSONObject } from "Common/Types/JSON";
import StatusPagePrivateUser from "Common/Models/DatabaseModels/StatusPagePrivateUser";
import StatusPageSCIM from "Common/Models/DatabaseModels/StatusPageSCIM";
import BadRequestException from "Common/Types/Exception/BadRequestException";
import NotFoundException from "Common/Types/Exception/NotFoundException";
import LIMIT_MAX, { LIMIT_PER_PROJECT } from "Common/Types/Database/LimitMax";
import {
formatUserForSCIM,
generateServiceProviderConfig,
} from "../Utils/SCIMUtils";
import Text from "Common/Types/Text";
import HashedString from "Common/Types/HashedString";
const router: ExpressRouter = Express.getRouter();
// SCIM Service Provider Configuration - GET /status-page-scim/v2/ServiceProviderConfig
router.get(
"/status-page-scim/v2/:statusPageScimId/ServiceProviderConfig",
SCIMMiddleware.isAuthorizedSCIMRequest,
async (req: ExpressRequest, res: ExpressResponse): Promise<void> => {
try {
logger.debug(
`Status Page SCIM ServiceProviderConfig - scimId: ${req.params["statusPageScimId"]!}`,
);
const serviceProviderConfig: JSONObject = generateServiceProviderConfig(
req,
req.params["statusPageScimId"]!,
"status-page",
);
return Response.sendJsonObjectResponse(req, res, serviceProviderConfig);
} catch (err) {
logger.error(err);
return Response.sendErrorResponse(req, res, err as BadRequestException);
}
},
);
// Status Page Users endpoint - GET /status-page-scim/v2/Users
router.get(
"/status-page-scim/v2/:statusPageScimId/Users",
SCIMMiddleware.isAuthorizedSCIMRequest,
async (req: ExpressRequest, res: ExpressResponse): Promise<void> => {
try {
logger.debug(
`Status Page SCIM Users list request for statusPageScimId: ${req.params["statusPageScimId"]}`,
);
const oneuptimeRequest: OneUptimeRequest = req as OneUptimeRequest;
const bearerData: JSONObject =
oneuptimeRequest.bearerTokenData as JSONObject;
const statusPageId: ObjectID = bearerData["statusPageId"] as ObjectID;
// Parse query parameters
const startIndex: number =
parseInt(req.query["startIndex"] as string) || 1;
const count: number = Math.min(
parseInt(req.query["count"] as string) || 100,
LIMIT_PER_PROJECT,
);
const filter: string = req.query["filter"] as string;
logger.debug(
`Status Page SCIM Users - statusPageId: ${statusPageId}, startIndex: ${startIndex}, count: ${count}, filter: ${filter || "none"}`,
);
// Build query for status page users
const query: any = {
statusPageId: statusPageId,
};
// Handle SCIM filter for userName
if (filter) {
const emailMatch: RegExpMatchArray | null = filter.match(
/userName eq "([^"]+)"/i,
);
if (emailMatch) {
const email: string = emailMatch[1]!;
logger.debug(
`Status Page SCIM Users list - statusPageScimId: ${req.params["statusPageScimId"]!}, filter by email: ${email}`,
);
if (email) {
if (Email.isValid(email)) {
query.email = new Email(email);
logger.debug(
`Status Page SCIM Users list - statusPageScimId: ${req.params["statusPageScimId"]!}, filtering by email: ${email}`,
);
} else {
logger.debug(
`Status Page SCIM Users list - statusPageScimId: ${req.params["statusPageScimId"]!}, invalid email format in filter: ${email}`,
);
return Response.sendJsonObjectResponse(req, res, {
schemas: ["urn:ietf:params:scim:api:messages:2.0:ListResponse"],
totalResults: 0,
startIndex: startIndex,
itemsPerPage: 0,
Resources: [],
});
}
}
}
}
// Get all private users for this status page
const statusPageUsers: Array<StatusPagePrivateUser> =
await StatusPagePrivateUserService.findBy({
query: query,
select: {
_id: true,
email: true,
createdAt: true,
updatedAt: true,
},
skip: 0,
limit: LIMIT_MAX,
props: { isRoot: true },
});
logger.debug(
`Status Page SCIM Users - found ${statusPageUsers.length} users`,
);
// Format users for SCIM
const users: Array<JSONObject> = statusPageUsers.map(
(user: StatusPagePrivateUser) => {
return formatUserForSCIM(
user,
req,
req.params["statusPageScimId"]!,
"status-page",
);
},
);
// Paginate the results
const paginatedUsers: Array<JSONObject> = users.slice(
(startIndex - 1) * count,
startIndex * count,
);
logger.debug(
`Status Page SCIM Users response prepared with ${users.length} users`,
);
return Response.sendJsonObjectResponse(req, res, {
schemas: ["urn:ietf:params:scim:api:messages:2.0:ListResponse"],
totalResults: users.length,
startIndex: startIndex,
itemsPerPage: paginatedUsers.length,
Resources: paginatedUsers,
});
} catch (err) {
logger.error(err);
return Response.sendErrorResponse(req, res, err as BadRequestException);
}
},
);
// Get Individual Status Page User - GET /status-page-scim/v2/Users/{id}
router.get(
"/status-page-scim/v2/:statusPageScimId/Users/:userId",
SCIMMiddleware.isAuthorizedSCIMRequest,
async (req: ExpressRequest, res: ExpressResponse): Promise<void> => {
try {
logger.debug(
`Status Page SCIM Get individual user request for userId: ${req.params["userId"]}, statusPageScimId: ${req.params["statusPageScimId"]}`,
);
const oneuptimeRequest: OneUptimeRequest = req as OneUptimeRequest;
const bearerData: JSONObject =
oneuptimeRequest.bearerTokenData as JSONObject;
const statusPageId: ObjectID = bearerData["statusPageId"] as ObjectID;
const userId: string = req.params["userId"]!;
logger.debug(
`Status Page SCIM Get user - statusPageId: ${statusPageId}, userId: ${userId}`,
);
if (!userId) {
throw new BadRequestException("User ID is required");
}
// Check if user exists and belongs to this status page
const statusPageUser: StatusPagePrivateUser | null =
await StatusPagePrivateUserService.findOneBy({
query: {
statusPageId: statusPageId,
_id: new ObjectID(userId),
},
select: {
_id: true,
email: true,
createdAt: true,
updatedAt: true,
},
props: { isRoot: true },
});
if (!statusPageUser) {
logger.debug(
`Status Page SCIM Get user - user not found for userId: ${userId}`,
);
throw new NotFoundException(
"User not found or not part of this status page",
);
}
const user: JSONObject = formatUserForSCIM(
statusPageUser,
req,
req.params["statusPageScimId"]!,
"status-page",
);
logger.debug(
`Status Page SCIM Get user - returning user with id: ${statusPageUser.id}`,
);
return Response.sendJsonObjectResponse(req, res, user);
} catch (err) {
logger.error(err);
return Response.sendErrorResponse(req, res, err as BadRequestException);
}
},
);
// Create Status Page User - POST /status-page-scim/v2/Users
router.post(
"/status-page-scim/v2/:statusPageScimId/Users",
SCIMMiddleware.isAuthorizedSCIMRequest,
async (req: ExpressRequest, res: ExpressResponse): Promise<void> => {
try {
logger.debug(
`Status Page SCIM Create user request for statusPageScimId: ${req.params["statusPageScimId"]}`,
);
const oneuptimeRequest: OneUptimeRequest = req as OneUptimeRequest;
const bearerData: JSONObject =
oneuptimeRequest.bearerTokenData as JSONObject;
const statusPageId: ObjectID = bearerData["statusPageId"] as ObjectID;
const scimConfig: StatusPageSCIM = bearerData[
"scimConfig"
] as StatusPageSCIM;
if (!scimConfig.autoProvisionUsers) {
throw new BadRequestException(
"Auto-provisioning is disabled for this status page",
);
}
const scimUser: JSONObject = req.body;
logger.debug(
`Status Page SCIM Create user - statusPageId: ${statusPageId}`,
);
logger.debug(
`Request body for Status Page SCIM Create user: ${JSON.stringify(scimUser, null, 2)}`,
);
// Extract user data from SCIM payload
const email: string =
(scimUser["userName"] as string) ||
((scimUser["emails"] as JSONObject[])?.[0]?.["value"] as string);
if (!email) {
throw new BadRequestException("Email is required for user creation");
}
logger.debug(`Status Page SCIM Create user - email: ${email}`);
// Check if user already exists for this status page
let user: StatusPagePrivateUser | null =
await StatusPagePrivateUserService.findOneBy({
query: {
statusPageId: statusPageId,
email: new Email(email),
},
select: {
_id: true,
email: true,
createdAt: true,
updatedAt: true,
},
props: { isRoot: true },
});
if (!user) {
logger.debug(
`Status Page SCIM Create user - creating new user with email: ${email}`,
);
const privateUser: StatusPagePrivateUser = new StatusPagePrivateUser();
privateUser.statusPageId = statusPageId;
privateUser.email = new Email(email);
privateUser.password = new HashedString(Text.generateRandomText(32));
privateUser.projectId = bearerData["projectId"] as ObjectID;
// Create new status page private user
user = await StatusPagePrivateUserService.create({
data: privateUser as any,
props: { isRoot: true },
});
} else {
logger.debug(
`Status Page SCIM Create user - user already exists with id: ${user.id}`,
);
}
const createdUser: JSONObject = formatUserForSCIM(
user,
req,
req.params["statusPageScimId"]!,
"status-page",
);
logger.debug(
`Status Page SCIM Create user - returning created user with id: ${user.id}`,
);
res.status(201);
return Response.sendJsonObjectResponse(req, res, createdUser);
} catch (err) {
logger.error(err);
return Response.sendErrorResponse(req, res, err as BadRequestException);
}
},
);
// Update Status Page User - PUT /status-page-scim/v2/Users/{id}
router.put(
"/status-page-scim/v2/:statusPageScimId/Users/:userId",
SCIMMiddleware.isAuthorizedSCIMRequest,
async (req: ExpressRequest, res: ExpressResponse): Promise<void> => {
try {
logger.debug(
`Status Page SCIM Update user request for userId: ${req.params["userId"]}, statusPageScimId: ${req.params["statusPageScimId"]}`,
);
const oneuptimeRequest: OneUptimeRequest = req as OneUptimeRequest;
const bearerData: JSONObject =
oneuptimeRequest.bearerTokenData as JSONObject;
const statusPageId: ObjectID = bearerData["statusPageId"] as ObjectID;
const userId: string = req.params["userId"]!;
const scimUser: JSONObject = req.body;
logger.debug(
`Status Page SCIM Update user - statusPageId: ${statusPageId}, userId: ${userId}`,
);
logger.debug(
`Request body for Status Page SCIM Update user: ${JSON.stringify(scimUser, null, 2)}`,
);
if (!userId) {
throw new BadRequestException("User ID is required");
}
// Check if user exists and belongs to this status page
const statusPageUser: StatusPagePrivateUser | null =
await StatusPagePrivateUserService.findOneBy({
query: {
statusPageId: statusPageId,
_id: new ObjectID(userId),
},
select: {
_id: true,
email: true,
createdAt: true,
updatedAt: true,
},
props: { isRoot: true },
});
if (!statusPageUser) {
logger.debug(
`Status Page SCIM Update user - user not found for userId: ${userId}`,
);
throw new NotFoundException(
"User not found or not part of this status page",
);
}
// Update user information
const email: string =
(scimUser["userName"] as string) ||
((scimUser["emails"] as JSONObject[])?.[0]?.["value"] as string);
const active: boolean = scimUser["active"] as boolean;
logger.debug(
`Status Page SCIM Update user - email: ${email}, active: ${active}`,
);
// Handle user deactivation by deleting from status page
if (active === false) {
logger.debug(
`Status Page SCIM Update user - user marked as inactive, removing from status page`,
);
const scimConfig: StatusPageSCIM = bearerData[
"scimConfig"
] as StatusPageSCIM;
if (scimConfig.autoDeprovisionUsers) {
await StatusPagePrivateUserService.deleteOneById({
id: new ObjectID(userId),
props: { isRoot: true },
});
logger.debug(
`Status Page SCIM Update user - user removed from status page`,
);
// Return empty response for deleted user
return Response.sendJsonObjectResponse(req, res, {});
}
}
// Prepare update data
const updateData: {
email?: Email;
} = {};
if (email && email !== statusPageUser.email?.toString()) {
updateData.email = new Email(email);
}
// Only update if there are changes
if (Object.keys(updateData).length > 0) {
logger.debug(
`Status Page SCIM Update user - updating user with data: ${JSON.stringify(updateData)}`,
);
await StatusPagePrivateUserService.updateOneById({
id: new ObjectID(userId),
data: updateData,
props: { isRoot: true },
});
logger.debug(
`Status Page SCIM Update user - user updated successfully`,
);
// Fetch updated user
const updatedUser: StatusPagePrivateUser | null =
await StatusPagePrivateUserService.findOneById({
id: new ObjectID(userId),
select: {
_id: true,
email: true,
createdAt: true,
updatedAt: true,
},
props: { isRoot: true },
});
if (updatedUser) {
const user: JSONObject = formatUserForSCIM(
updatedUser,
req,
req.params["statusPageScimId"]!,
"status-page",
);
return Response.sendJsonObjectResponse(req, res, user);
}
}
logger.debug(
`Status Page SCIM Update user - no updates made, returning existing user`,
);
// If no updates were made, return the existing user
const user: JSONObject = formatUserForSCIM(
statusPageUser,
req,
req.params["statusPageScimId"]!,
"status-page",
);
return Response.sendJsonObjectResponse(req, res, user);
} catch (err) {
logger.error(err);
return Response.sendErrorResponse(req, res, err as BadRequestException);
}
},
);
// Delete Status Page User - DELETE /status-page-scim/v2/Users/{id}
router.delete(
"/status-page-scim/v2/:statusPageScimId/Users/:userId",
SCIMMiddleware.isAuthorizedSCIMRequest,
async (req: ExpressRequest, res: ExpressResponse): Promise<void> => {
try {
logger.debug(
`Status Page SCIM Delete user request for userId: ${req.params["userId"]}, statusPageScimId: ${req.params["statusPageScimId"]}`,
);
const oneuptimeRequest: OneUptimeRequest = req as OneUptimeRequest;
const bearerData: JSONObject =
oneuptimeRequest.bearerTokenData as JSONObject;
const statusPageId: ObjectID = bearerData["statusPageId"] as ObjectID;
const scimConfig: StatusPageSCIM = bearerData[
"scimConfig"
] as StatusPageSCIM;
const userId: string = req.params["userId"]!;
if (!scimConfig.autoDeprovisionUsers) {
throw new BadRequestException(
"Auto-deprovisioning is disabled for this status page",
);
}
logger.debug(
`Status Page SCIM Delete user - statusPageId: ${statusPageId}, userId: ${userId}`,
);
if (!userId) {
throw new BadRequestException("User ID is required");
}
// Check if user exists and belongs to this status page
const statusPageUser: StatusPagePrivateUser | null =
await StatusPagePrivateUserService.findOneBy({
query: {
statusPageId: statusPageId,
_id: new ObjectID(userId),
},
select: {
_id: true,
},
props: { isRoot: true },
});
if (!statusPageUser) {
logger.debug(
`Status Page SCIM Delete user - user not found for userId: ${userId}`,
);
// SCIM spec says to return 404 for non-existent resources
throw new NotFoundException("User not found");
}
// Delete the user from status page
await StatusPagePrivateUserService.deleteOneById({
id: new ObjectID(userId),
props: { isRoot: true },
});
logger.debug(
`Status Page SCIM Delete user - user deleted successfully for userId: ${userId}`,
);
// Return 204 No Content for successful deletion
res.status(204);
return Response.sendEmptySuccessResponse(req, res);
} catch (err) {
logger.error(err);
return Response.sendErrorResponse(req, res, err as BadRequestException);
}
},
);
export default router;

View File

@@ -1,8 +1,10 @@
import AuthenticationAPI from "./API/Authentication";
import ResellerAPI from "./API/Reseller";
import SsoAPI from "./API/SSO";
import SCIMAPI from "./API/SCIM";
import StatusPageAuthenticationAPI from "./API/StatusPageAuthentication";
import StatusPageSsoAPI from "./API/StatusPageSSO";
import StatusPageSCIMAPI from "./API/StatusPageSCIM";
import FeatureSet from "Common/Server/Types/FeatureSet";
import Express, { ExpressApplication } from "Common/Server/Utils/Express";
import "ejs";
@@ -19,6 +21,10 @@ const IdentityFeatureSet: FeatureSet = {
app.use([`/${APP_NAME}`, "/"], SsoAPI);
app.use([`/${APP_NAME}`, "/"], SCIMAPI);
app.use([`/${APP_NAME}`, "/"], StatusPageSCIMAPI);
app.use([`/${APP_NAME}`, "/"], StatusPageSsoAPI);
app.use(

View File

@@ -35,6 +35,8 @@ export default class AuthenticationEmail {
const host: Hostname = await DatabaseConfig.getHost();
const httpProtocol: Protocol = await DatabaseConfig.getHttpProtocol();
logger.debug("Sending verification email");
MailService.sendMail({
toEmail: user.email!,
subject: "Please verify email.",
@@ -50,8 +52,13 @@ export default class AuthenticationEmail {
).toString(),
homeUrl: new URL(httpProtocol, host).toString(),
},
}).catch((err: Error) => {
logger.error(err);
});
})
.then(() => {
logger.debug("Verification email sent");
})
.catch((err: Error) => {
logger.debug("Error sending verification email");
logger.error(err);
});
}
}

View File

@@ -0,0 +1,265 @@
import { ExpressRequest } from "Common/Server/Utils/Express";
import logger from "Common/Server/Utils/Logger";
import { JSONObject } from "Common/Types/JSON";
import Email from "Common/Types/Email";
import Name from "Common/Types/Name";
import ObjectID from "Common/Types/ObjectID";
/**
* Shared SCIM utility functions for both Project SCIM and Status Page SCIM
*/
// Base interface for SCIM user-like objects - compatible with User model
export interface SCIMUser {
id?: ObjectID | null;
email?: Email;
name?: Name | string;
createdAt?: Date;
updatedAt?: Date;
}
/**
* Parse name information from SCIM user payload
*/
export const parseNameFromSCIM: (scimUser: JSONObject) => string = (
scimUser: JSONObject,
): string => {
logger.debug(
`SCIM - Parsing name from SCIM user: ${JSON.stringify(scimUser, null, 2)}`,
);
const givenName: string =
((scimUser["name"] as JSONObject)?.["givenName"] as string) || "";
const familyName: string =
((scimUser["name"] as JSONObject)?.["familyName"] as string) || "";
const formattedName: string = (scimUser["name"] as JSONObject)?.[
"formatted"
] as string;
// Construct full name: prefer formatted, then combine given+family, then fallback to displayName
if (formattedName) {
return formattedName;
} else if (givenName || familyName) {
return `${givenName} ${familyName}`.trim();
} else if (scimUser["displayName"]) {
return scimUser["displayName"] as string;
}
return "";
};
/**
* Parse full name into SCIM name format
*/
export const parseNameToSCIMFormat: (fullName: string) => {
givenName: string;
familyName: string;
formatted: string;
} = (
fullName: string,
): { givenName: string; familyName: string; formatted: string } => {
const nameParts: string[] = fullName.trim().split(/\s+/);
const givenName: string = nameParts[0] || "";
const familyName: string = nameParts.slice(1).join(" ") || "";
return {
givenName,
familyName,
formatted: fullName,
};
};
/**
* Format user object for SCIM response
*/
export const formatUserForSCIM: (
user: SCIMUser,
req: ExpressRequest,
scimId: string,
scimType: "project" | "status-page",
) => JSONObject = (
user: SCIMUser,
req: ExpressRequest,
scimId: string,
scimType: "project" | "status-page",
): JSONObject => {
const baseUrl: string = `${req.protocol}://${req.get("host")}`;
const userName: string = user.email?.toString() || "";
const fullName: string =
user.name?.toString() || userName.split("@")[0] || "Unknown User";
const nameData: { givenName: string; familyName: string; formatted: string } =
parseNameToSCIMFormat(fullName);
// Determine the correct endpoint path based on SCIM type
const endpointPath: string =
scimType === "project"
? `/scim/v2/${scimId}/Users/${user.id?.toString()}`
: `/status-page-scim/v2/${scimId}/Users/${user.id?.toString()}`;
return {
schemas: ["urn:ietf:params:scim:schemas:core:2.0:User"],
id: user.id?.toString(),
userName: userName,
displayName: nameData.formatted,
name: {
formatted: nameData.formatted,
familyName: nameData.familyName,
givenName: nameData.givenName,
},
emails: [
{
value: userName,
type: "work",
primary: true,
},
],
active: true,
meta: {
resourceType: "User",
created: user.createdAt?.toISOString(),
lastModified: user.updatedAt?.toISOString(),
location: `${baseUrl}${endpointPath}`,
},
};
};
/**
* Extract email from SCIM user payload
*/
export const extractEmailFromSCIM: (scimUser: JSONObject) => string = (
scimUser: JSONObject,
): string => {
return (
(scimUser["userName"] as string) ||
((scimUser["emails"] as JSONObject[])?.[0]?.["value"] as string) ||
""
);
};
/**
* Extract active status from SCIM user payload
*/
export const extractActiveFromSCIM: (scimUser: JSONObject) => boolean = (
scimUser: JSONObject,
): boolean => {
return scimUser["active"] !== false; // Default to true if not specified
};
/**
* Generate SCIM ServiceProviderConfig response
*/
export const generateServiceProviderConfig: (
req: ExpressRequest,
scimId: string,
scimType: "project" | "status-page",
documentationUrl?: string,
) => JSONObject = (
req: ExpressRequest,
scimId: string,
scimType: "project" | "status-page",
documentationUrl: string = "https://oneuptime.com/docs/identity/scim",
): JSONObject => {
const baseUrl: string = `${req.protocol}://${req.get("host")}`;
const endpointPath: string =
scimType === "project"
? `/scim/v2/${scimId}`
: `/status-page-scim/v2/${scimId}`;
return {
schemas: ["urn:ietf:params:scim:schemas:core:2.0:ServiceProviderConfig"],
documentationUri: documentationUrl,
patch: {
supported: true,
},
bulk: {
supported: true,
maxOperations: 1000,
maxPayloadSize: 1048576,
},
filter: {
supported: true,
maxResults: 200,
},
changePassword: {
supported: false,
},
sort: {
supported: true,
},
etag: {
supported: false,
},
authenticationSchemes: [
{
type: "httpbearer",
name: "HTTP Bearer",
description: "Authentication scheme using HTTP Bearer Token",
primary: true,
},
],
meta: {
location: `${baseUrl}${endpointPath}/ServiceProviderConfig`,
resourceType: "ServiceProviderConfig",
created: "2023-01-01T00:00:00Z",
lastModified: "2023-01-01T00:00:00Z",
},
};
};
/**
* Generate SCIM ListResponse for users
*/
export const generateUsersListResponse: (
users: JSONObject[],
startIndex: number,
totalResults: number,
) => JSONObject = (
users: JSONObject[],
startIndex: number,
totalResults: number,
): JSONObject => {
return {
schemas: ["urn:ietf:params:scim:api:messages:2.0:ListResponse"],
totalResults: totalResults,
startIndex: startIndex,
itemsPerPage: users.length,
Resources: users,
};
};
/**
* Generate SCIM ListResponse for groups
*/
export const generateGroupsListResponse: (
groups: JSONObject[],
startIndex: number,
totalResults: number,
) => JSONObject = (
groups: JSONObject[],
startIndex: number,
totalResults: number,
): JSONObject => {
return {
schemas: ["urn:ietf:params:scim:api:messages:2.0:ListResponse"],
totalResults: totalResults,
startIndex: startIndex,
itemsPerPage: groups.length,
Resources: groups,
};
};
/**
* Parse query parameters for SCIM list requests
*/
export const parseSCIMQueryParams: (req: ExpressRequest) => {
startIndex: number;
count: number;
} = (req: ExpressRequest): { startIndex: number; count: number } => {
const startIndex: number = parseInt(req.query["startIndex"] as string) || 1;
const count: number = Math.min(
parseInt(req.query["count"] as string) || 100,
200, // SCIM recommended max
);
return { startIndex, count };
};

View File

@@ -184,15 +184,17 @@ export default class SSOUtil {
return null;
}
// get displayName attribute.
// {
// "$": {
// "Name": "http://schemas.microsoft.com/identity/claims/displayname"
// },
// "AttributeValue": [
// "Nawaz Dhandala"
// ]
// },
/*
* get displayName attribute.
* {
* "$": {
* "Name": "http://schemas.microsoft.com/identity/claims/displayname"
* },
* "AttributeValue": [
* "Nawaz Dhandala"
* ]
* },
*/
for (let i: number = 0; i < samlAttribute.length; i++) {
const attribute: JSONObject = samlAttribute[i] as JSONObject;

View File

@@ -67,7 +67,6 @@
<link rel="apple-touch-icon-precomposed" href="/img/ou-wb.svg">
<link rel="icon" href="/img/ou-wb.svg">
<link rel="image_src" type="image/png" href="/img/hou-wb.svg">
<link rel="canonical" href="/">
<link rel="manifest" href="/manifest.json">
<meta property="og:title" content="OneUptime - One Complete Observability platform.">
<meta property="og:url" content="https://oneuptime.com">

View File

@@ -31,6 +31,22 @@ router.post(
userOnCallLogTimelineId:
(body["userOnCallLogTimelineId"] as ObjectID) || undefined,
customTwilioConfig: body["customTwilioConfig"] as any,
incidentId: (body["incidentId"] as ObjectID) || undefined,
alertId: (body["alertId"] as ObjectID) || undefined,
scheduledMaintenanceId:
(body["scheduledMaintenanceId"] as ObjectID) || undefined,
statusPageId: (body["statusPageId"] as ObjectID) || undefined,
statusPageAnnouncementId:
(body["statusPageAnnouncementId"] as ObjectID) || undefined,
userId: (body["userId"] as ObjectID) || undefined,
onCallPolicyId: (body["onCallPolicyId"] as ObjectID) || undefined,
onCallPolicyEscalationRuleId:
(body["onCallPolicyEscalationRuleId"] as ObjectID) || undefined,
onCallDutyPolicyExecutionLogTimelineId:
(body["onCallDutyPolicyExecutionLogTimelineId"] as ObjectID) ||
undefined,
onCallScheduleId: (body["onCallScheduleId"] as ObjectID) || undefined,
teamId: (body["teamId"] as ObjectID) || undefined,
});
return Response.sendEmptySuccessResponse(req, res);

View File

@@ -43,6 +43,43 @@ router.post(
emailServer: mailServer,
userOnCallLogTimelineId:
(body["userOnCallLogTimelineId"] as ObjectID) || undefined,
incidentId: body["incidentId"]
? new ObjectID(body["incidentId"].toString())
: undefined,
alertId: body["alertId"]
? new ObjectID(body["alertId"].toString())
: undefined,
scheduledMaintenanceId: body["scheduledMaintenanceId"]
? new ObjectID(body["scheduledMaintenanceId"].toString())
: undefined,
statusPageId: body["statusPageId"]
? new ObjectID(body["statusPageId"].toString())
: undefined,
statusPageAnnouncementId: body["statusPageAnnouncementId"]
? new ObjectID(body["statusPageAnnouncementId"].toString())
: undefined,
userId: body["userId"]
? new ObjectID(body["userId"].toString())
: undefined,
onCallPolicyId: body["onCallPolicyId"]
? new ObjectID(body["onCallPolicyId"].toString())
: undefined,
onCallPolicyEscalationRuleId: body["onCallPolicyEscalationRuleId"]
? new ObjectID(body["onCallPolicyEscalationRuleId"].toString())
: undefined,
onCallDutyPolicyExecutionLogTimelineId: body[
"onCallDutyPolicyExecutionLogTimelineId"
]
? new ObjectID(
body["onCallDutyPolicyExecutionLogTimelineId"].toString(),
)
: undefined,
onCallScheduleId: body["onCallScheduleId"]
? new ObjectID(body["onCallScheduleId"].toString())
: undefined,
teamId: body["teamId"]
? new ObjectID(body["teamId"].toString())
: undefined,
});
return Response.sendEmptySuccessResponse(req, res);

View File

@@ -0,0 +1,65 @@
import PushService from "../Services/PushNotificationService";
import ClusterKeyAuthorization from "Common/Server/Middleware/ClusterKeyAuthorization";
import ObjectID from "Common/Types/ObjectID";
import Express, {
ExpressRequest,
ExpressResponse,
ExpressRouter,
} from "Common/Server/Utils/Express";
import Response from "Common/Server/Utils/Response";
import { JSONObject } from "Common/Types/JSON";
import JSONFunctions from "Common/Types/JSONFunctions";
const router: ExpressRouter = Express.getRouter();
router.post(
"/send",
ClusterKeyAuthorization.isAuthorizedServiceMiddleware,
async (req: ExpressRequest, res: ExpressResponse) => {
const body: JSONObject = JSONFunctions.deserialize(req.body);
// Support both new devices format and legacy deviceTokens/deviceNames format
let devices: Array<{ token: string; name?: string }> = [];
if (body["devices"]) {
// New format: devices as array of objects
devices = body["devices"] as Array<{ token: string; name?: string }>;
} else {
throw new Error("Invalid request format: 'devices' array is required.");
}
await PushService.send(
{
devices,
deviceType: (body["deviceType"] as any) || "web",
message: body["message"] as any,
},
{
projectId: (body["projectId"] as ObjectID) || undefined,
isSensitive: (body["isSensitive"] as boolean) || false,
userOnCallLogTimelineId:
(body["userOnCallLogTimelineId"] as ObjectID) || undefined,
incidentId: (body["incidentId"] as ObjectID) || undefined,
alertId: (body["alertId"] as ObjectID) || undefined,
scheduledMaintenanceId:
(body["scheduledMaintenanceId"] as ObjectID) || undefined,
statusPageId: (body["statusPageId"] as ObjectID) || undefined,
statusPageAnnouncementId:
(body["statusPageAnnouncementId"] as ObjectID) || undefined,
userId: (body["userId"] as ObjectID) || undefined,
onCallPolicyId: (body["onCallPolicyId"] as ObjectID) || undefined,
onCallPolicyEscalationRuleId:
(body["onCallPolicyEscalationRuleId"] as ObjectID) || undefined,
onCallDutyPolicyExecutionLogTimelineId:
(body["onCallDutyPolicyExecutionLogTimelineId"] as ObjectID) ||
undefined,
onCallScheduleId: (body["onCallScheduleId"] as ObjectID) || undefined,
teamId: (body["teamId"] as ObjectID) || undefined,
},
);
return Response.sendEmptySuccessResponse(req, res);
},
);
export default router;

View File

@@ -30,6 +30,22 @@ router.post(
userOnCallLogTimelineId:
(body["userOnCallLogTimelineId"] as ObjectID) || undefined,
customTwilioConfig: body["customTwilioConfig"] as any,
incidentId: (body["incidentId"] as ObjectID) || undefined,
alertId: (body["alertId"] as ObjectID) || undefined,
scheduledMaintenanceId:
(body["scheduledMaintenanceId"] as ObjectID) || undefined,
statusPageId: (body["statusPageId"] as ObjectID) || undefined,
statusPageAnnouncementId:
(body["statusPageAnnouncementId"] as ObjectID) || undefined,
userId: (body["userId"] as ObjectID) || undefined,
onCallPolicyId: (body["onCallPolicyId"] as ObjectID) || undefined,
onCallPolicyEscalationRuleId:
(body["onCallPolicyEscalationRuleId"] as ObjectID) || undefined,
onCallDutyPolicyExecutionLogTimelineId:
(body["onCallDutyPolicyExecutionLogTimelineId"] as ObjectID) ||
undefined,
onCallScheduleId: (body["onCallScheduleId"] as ObjectID) || undefined,
teamId: (body["teamId"] as ObjectID) || undefined,
});
return Response.sendEmptySuccessResponse(req, res);

View File

@@ -0,0 +1,156 @@
import WhatsAppService from "../Services/WhatsAppService";
import BadDataException from "Common/Types/Exception/BadDataException";
import { JSONObject } from "Common/Types/JSON";
import ObjectID from "Common/Types/ObjectID";
import Phone from "Common/Types/Phone";
import WhatsAppMessage from "Common/Types/WhatsApp/WhatsAppMessage";
import {
WhatsAppTemplateId,
WhatsAppTemplateIds,
WhatsAppTemplateLanguage,
} from "Common/Types/WhatsApp/WhatsAppTemplates";
import ClusterKeyAuthorization from "Common/Server/Middleware/ClusterKeyAuthorization";
import Express, {
ExpressRequest,
ExpressResponse,
ExpressRouter,
} from "Common/Server/Utils/Express";
import Response from "Common/Server/Utils/Response";
const router: ExpressRouter = Express.getRouter();
const toTemplateVariables: (
rawVariables: JSONObject | undefined,
) => Record<string, string> | undefined = (
rawVariables: JSONObject | undefined,
): Record<string, string> | undefined => {
if (!rawVariables) {
return undefined;
}
const result: Record<string, string> = {};
for (const key of Object.keys(rawVariables)) {
const value: unknown = rawVariables[key];
if (value !== null && value !== undefined) {
result[key] = String(value);
}
}
return Object.keys(result).length > 0 ? result : undefined;
};
router.post(
"/send",
ClusterKeyAuthorization.isAuthorizedServiceMiddleware,
async (req: ExpressRequest, res: ExpressResponse) => {
const body: JSONObject = req.body as JSONObject;
if (!body["to"]) {
throw new BadDataException("`to` phone number is required");
}
const toPhone: Phone = new Phone(body["to"] as string);
const message: WhatsAppMessage = {
to: toPhone,
body: (body["body"] as string) || "",
templateKey: (body["templateKey"] as string) || undefined,
templateVariables: toTemplateVariables(
body["templateVariables"] as JSONObject | undefined,
),
templateLanguageCode:
(body["templateLanguageCode"] as string) || undefined,
};
await WhatsAppService.sendWhatsApp(message, {
projectId: body["projectId"]
? new ObjectID(body["projectId"] as string)
: undefined,
isSensitive: (body["isSensitive"] as boolean) || false,
userOnCallLogTimelineId: body["userOnCallLogTimelineId"]
? new ObjectID(body["userOnCallLogTimelineId"] as string)
: undefined,
incidentId: body["incidentId"]
? new ObjectID(body["incidentId"] as string)
: undefined,
alertId: body["alertId"]
? new ObjectID(body["alertId"] as string)
: undefined,
scheduledMaintenanceId: body["scheduledMaintenanceId"]
? new ObjectID(body["scheduledMaintenanceId"] as string)
: undefined,
statusPageId: body["statusPageId"]
? new ObjectID(body["statusPageId"] as string)
: undefined,
statusPageAnnouncementId: body["statusPageAnnouncementId"]
? new ObjectID(body["statusPageAnnouncementId"] as string)
: undefined,
userId: body["userId"]
? new ObjectID(body["userId"] as string)
: undefined,
onCallPolicyId: body["onCallPolicyId"]
? new ObjectID(body["onCallPolicyId"] as string)
: undefined,
onCallPolicyEscalationRuleId: body["onCallPolicyEscalationRuleId"]
? new ObjectID(body["onCallPolicyEscalationRuleId"] as string)
: undefined,
onCallDutyPolicyExecutionLogTimelineId: body[
"onCallDutyPolicyExecutionLogTimelineId"
]
? new ObjectID(body["onCallDutyPolicyExecutionLogTimelineId"] as string)
: undefined,
onCallScheduleId: body["onCallScheduleId"]
? new ObjectID(body["onCallScheduleId"] as string)
: undefined,
teamId: body["teamId"]
? new ObjectID(body["teamId"] as string)
: undefined,
});
return Response.sendEmptySuccessResponse(req, res);
},
);
router.post("/test", async (req: ExpressRequest, res: ExpressResponse) => {
const body: JSONObject = req.body as JSONObject;
if (!body["toPhone"]) {
throw new BadDataException("toPhone is required");
}
const toPhone: Phone = new Phone(body["toPhone"] as string);
const templateKey: WhatsAppTemplateId = WhatsAppTemplateIds.TestNotification;
const templateLanguageCode: string =
WhatsAppTemplateLanguage[templateKey] || "en";
const message: WhatsAppMessage = {
to: toPhone,
body: "",
templateKey,
templateVariables: undefined,
templateLanguageCode,
};
try {
await WhatsAppService.sendWhatsApp(message, {
projectId: body["projectId"]
? new ObjectID(body["projectId"] as string)
: undefined,
isSensitive: false,
});
} catch (err) {
const errorMsg: string =
err instanceof Error && err.message
? err.message
: "Failed to send test WhatsApp message.";
return Response.sendErrorResponse(req, res, new BadDataException(errorMsg));
}
return Response.sendEmptySuccessResponse(req, res);
});
export default router;

View File

@@ -12,6 +12,8 @@ import Phone from "Common/Types/Phone";
type GetGlobalSMTPConfig = () => Promise<EmailServer | null>;
export const DEFAULT_META_WHATSAPP_API_VERSION: string = "v23.0";
export const getGlobalSMTPConfig: GetGlobalSMTPConfig =
async (): Promise<EmailServer | null> => {
const globalConfig: GlobalConfig | null =
@@ -222,6 +224,83 @@ export const SMSHighRiskCostInCents: number = process.env[
? parseInt(process.env["SMS_HIGH_RISK_COST_IN_CENTS"])
: 0;
export interface MetaWhatsAppConfig {
accessToken: string;
phoneNumberId: string;
businessAccountId?: string | undefined;
appId?: string | undefined;
appSecret?: string | undefined;
apiVersion?: string | undefined;
}
type GetMetaWhatsAppConfigFunction = () => Promise<MetaWhatsAppConfig>;
export const getMetaWhatsAppConfig: GetMetaWhatsAppConfigFunction =
async (): Promise<MetaWhatsAppConfig> => {
const globalConfig: GlobalConfig | null =
await GlobalConfigService.findOneBy({
query: {
_id: ObjectID.getZeroObjectID().toString(),
},
props: {
isRoot: true,
},
select: {
metaWhatsAppAccessToken: true,
metaWhatsAppPhoneNumberId: true,
metaWhatsAppBusinessAccountId: true,
metaWhatsAppAppId: true,
metaWhatsAppAppSecret: true,
},
});
if (!globalConfig) {
throw new BadDataException("Global Config not found");
}
const accessToken: string | undefined =
globalConfig.metaWhatsAppAccessToken?.trim();
const phoneNumberId: string | undefined =
globalConfig.metaWhatsAppPhoneNumberId?.trim();
if (!accessToken) {
throw new BadDataException(
"Meta WhatsApp access token not configured. Please set it in the Admin Dashboard: " +
AdminDashboardClientURL.toString(),
);
}
if (!phoneNumberId) {
throw new BadDataException(
"Meta WhatsApp phone number ID not configured. Please set it in the Admin Dashboard: " +
AdminDashboardClientURL.toString(),
);
}
const businessAccountId: string | undefined =
globalConfig.metaWhatsAppBusinessAccountId?.trim() || undefined;
const appId: string | undefined =
globalConfig.metaWhatsAppAppId?.trim() || undefined;
const appSecret: string | undefined =
globalConfig.metaWhatsAppAppSecret?.trim() || undefined;
const apiVersion: string = DEFAULT_META_WHATSAPP_API_VERSION;
return {
accessToken,
phoneNumberId,
businessAccountId,
appId,
appSecret,
apiVersion,
};
};
export const WhatsAppTextDefaultCostInCents: number = process.env[
"WHATSAPP_TEXT_DEFAULT_COST_IN_CENTS"
]
? parseInt(process.env["WHATSAPP_TEXT_DEFAULT_COST_IN_CENTS"])
: 0;
export const CallHighRiskCostInCentsPerMinute: number = process.env[
"CALL_HIGH_RISK_COST_IN_CENTS_PER_MINUTE"
]

View File

@@ -2,6 +2,8 @@ import CallAPI from "./API/Call";
// API
import MailAPI from "./API/Mail";
import SmsAPI from "./API/SMS";
import WhatsAppAPI from "./API/WhatsApp";
import PushNotificationAPI from "./API/PushNotification";
import SMTPConfigAPI from "./API/SMTPConfig";
import "./Utils/Handlebars";
import FeatureSet from "Common/Server/Types/FeatureSet";
@@ -15,6 +17,8 @@ const NotificationFeatureSet: FeatureSet = {
app.use([`/${APP_NAME}/email`, "/email"], MailAPI);
app.use([`/${APP_NAME}/sms`, "/sms"], SmsAPI);
app.use([`/${APP_NAME}/whatsapp`, "/whatsapp"], WhatsAppAPI);
app.use([`/${APP_NAME}/push`, "/push"], PushNotificationAPI);
app.use([`/${APP_NAME}/call`, "/call"], CallAPI);
app.use([`/${APP_NAME}/smtp-config`, "/smtp-config"], SMTPConfigAPI);
},

View File

@@ -28,6 +28,38 @@ import Twilio from "twilio";
import { CallInstance } from "twilio/lib/rest/api/v2010/account/call";
import Phone from "Common/Types/Phone";
/**
* Extracts the main sayMessage values from a CallRequest's data array for call summary.
* Excludes acknowledgment responses, error messages, and other system messages.
* @param callRequest The call request containing data array with various objects
* @returns A string containing main call content messages separated by newlines
*/
function extractSayMessagesFromCallRequest(callRequest: CallRequest): string {
const sayMessages: string[] = [];
if (callRequest.data && Array.isArray(callRequest.data)) {
for (const item of callRequest.data) {
// Check if the item is a Say object with sayMessage
if ((item as Say).sayMessage) {
sayMessages.push((item as Say).sayMessage);
}
// Check if the item is a GatherInput with introMessage
if ((item as GatherInput).introMessage) {
sayMessages.push((item as GatherInput).introMessage);
}
/*
* NOTE: Excluding noInputMessage and onInputCallRequest messages from summary
* as they contain system responses like "Good bye", "Invalid input", "You have acknowledged"
* which should not be included in the call summary according to user requirements
*/
}
}
return sayMessages.length > 0
? sayMessages.join(" ")
: "No message content found";
}
export default class CallService {
public static async makeCall(
callRequest: CallRequest,
@@ -36,6 +68,18 @@ export default class CallService {
isSensitive?: boolean; // if true, message will not be logged
userOnCallLogTimelineId?: ObjectID | undefined; // user notification log timeline id
customTwilioConfig?: TwilioConfig | undefined;
incidentId?: ObjectID | undefined;
alertId?: ObjectID | undefined;
scheduledMaintenanceId?: ObjectID | undefined;
statusPageId?: ObjectID | undefined;
statusPageAnnouncementId?: ObjectID | undefined;
userId?: ObjectID | undefined;
// On-call policy related fields
onCallPolicyId?: ObjectID | undefined;
onCallPolicyEscalationRuleId?: ObjectID | undefined;
onCallDutyPolicyExecutionLogTimelineId?: ObjectID | undefined;
onCallScheduleId?: ObjectID | undefined;
teamId?: ObjectID | undefined;
},
): Promise<void> {
let callError: Error | null = null;
@@ -82,14 +126,58 @@ export default class CallService {
callLog.fromNumber = fromNumber;
callLog.callData =
options && options.isSensitive
? { message: "This call is sensitive and is not logged" }
: JSON.parse(JSON.stringify(callRequest));
? ({ message: "This call is sensitive and is not logged" } as any)
: ({
message: extractSayMessagesFromCallRequest(callRequest),
} as any);
callLog.callCostInUSDCents = 0;
if (options.projectId) {
callLog.projectId = options.projectId;
}
if (options.incidentId) {
callLog.incidentId = options.incidentId;
}
if (options.alertId) {
callLog.alertId = options.alertId;
}
if (options.scheduledMaintenanceId) {
callLog.scheduledMaintenanceId = options.scheduledMaintenanceId;
}
if (options.statusPageId) {
callLog.statusPageId = options.statusPageId;
}
if (options.statusPageAnnouncementId) {
callLog.statusPageAnnouncementId = options.statusPageAnnouncementId;
}
if (options.userId) {
callLog.userId = options.userId;
}
if (options.teamId) {
callLog.teamId = options.teamId;
}
// Set OnCall-related fields
if (options.onCallPolicyId) {
callLog.onCallDutyPolicyId = options.onCallPolicyId;
}
if (options.onCallPolicyEscalationRuleId) {
callLog.onCallDutyPolicyEscalationRuleId =
options.onCallPolicyEscalationRuleId;
}
if (options.onCallScheduleId) {
callLog.onCallDutyPolicyScheduleId = options.onCallScheduleId;
}
let project: Project | null = null;
// make sure project has enough balance.

View File

@@ -37,11 +37,49 @@ class TransporterPool {
private static semaphore: Map<string, number> = new Map();
private static readonly MAX_CONCURRENT_CONNECTIONS = 100;
private static resolveConnectionSettings(emailServer: EmailServer): {
portNumber: number;
wantsSecureConnection: boolean;
secureConnection: boolean;
requireTLS: boolean;
mode: "implicit-tls" | "starttls" | "plain";
} {
const portNumber: number = emailServer.port.toNumber();
const wantsSecureConnection: boolean = emailServer.secure;
const isImplicitTLSPort: boolean = portNumber === 465;
const secureConnection: boolean = isImplicitTLSPort;
const requireTLS: boolean = wantsSecureConnection && !isImplicitTLSPort;
let mode: "implicit-tls" | "starttls" | "plain" = "plain";
if (secureConnection) {
mode = "implicit-tls";
} else if (requireTLS) {
mode = "starttls";
}
return {
portNumber,
wantsSecureConnection,
secureConnection,
requireTLS,
mode,
};
}
private static getPoolKey(emailServer: EmailServer): string {
const { portNumber, mode } = this.resolveConnectionSettings(emailServer);
const username: string = emailServer.username || "noauth";
return `${emailServer.host.toString()}:${portNumber}:${username}:${mode}`;
}
public static getTransporter(
emailServer: EmailServer,
options: { timeout?: number | undefined },
): Transporter {
const key: string = `${emailServer.host.toString()}:${emailServer.port.toNumber()}:${emailServer.username || "noauth"}`;
const key: string = this.getPoolKey(emailServer);
if (!this.pools.has(key)) {
const transporter: Transporter = this.createTransporter(
@@ -59,9 +97,12 @@ class TransporterPool {
emailServer: EmailServer,
options: { timeout?: number | undefined },
): Transporter {
const { portNumber, wantsSecureConnection, secureConnection, requireTLS } =
this.resolveConnectionSettings(emailServer);
let tlsOptions: tls.ConnectionOptions | undefined = undefined;
if (!emailServer.secure) {
if (!wantsSecureConnection) {
tlsOptions = {
rejectUnauthorized: false,
};
@@ -69,8 +110,9 @@ class TransporterPool {
return nodemailer.createTransport({
host: emailServer.host.toString(),
port: emailServer.port.toNumber(),
secure: emailServer.secure,
port: portNumber,
secure: secureConnection,
requireTLS,
tls: tlsOptions,
auth:
emailServer.username && emailServer.password
@@ -88,7 +130,7 @@ class TransporterPool {
public static async acquireConnection(
emailServer: EmailServer,
): Promise<void> {
const key: string = `${emailServer.host.toString()}:${emailServer.port.toNumber()}:${emailServer.username || "noauth"}`;
const key: string = this.getPoolKey(emailServer);
while ((this.semaphore.get(key) || 0) >= this.MAX_CONCURRENT_CONNECTIONS) {
await new Promise<void>((resolve: () => void) => {
@@ -100,7 +142,7 @@ class TransporterPool {
}
public static releaseConnection(emailServer: EmailServer): void {
const key: string = `${emailServer.host.toString()}:${emailServer.port.toNumber()}:${emailServer.username || "noauth"}`;
const key: string = this.getPoolKey(emailServer);
const current: number = this.semaphore.get(key) || 0;
this.semaphore.set(key, Math.max(0, current - 1));
}
@@ -345,6 +387,18 @@ export default class MailService {
emailServer?: EmailServer | undefined;
userOnCallLogTimelineId?: ObjectID | undefined;
timeout?: number | undefined;
incidentId?: ObjectID | undefined;
alertId?: ObjectID | undefined;
scheduledMaintenanceId?: ObjectID | undefined;
statusPageId?: ObjectID | undefined;
statusPageAnnouncementId?: ObjectID | undefined;
userId?: ObjectID | undefined;
// On-call policy related fields
onCallPolicyId?: ObjectID | undefined;
onCallPolicyEscalationRuleId?: ObjectID | undefined;
onCallDutyPolicyExecutionLogTimelineId?: ObjectID | undefined;
onCallScheduleId?: ObjectID | undefined;
teamId?: ObjectID | undefined;
}
| undefined,
): Promise<void> {
@@ -359,6 +413,48 @@ export default class MailService {
if (options.emailServer?.id) {
emailLog.projectSmtpConfigId = options.emailServer?.id;
}
if (options.incidentId) {
emailLog.incidentId = options.incidentId;
}
if (options.alertId) {
emailLog.alertId = options.alertId;
}
if (options.scheduledMaintenanceId) {
emailLog.scheduledMaintenanceId = options.scheduledMaintenanceId;
}
if (options.statusPageId) {
emailLog.statusPageId = options.statusPageId;
}
if (options.statusPageAnnouncementId) {
emailLog.statusPageAnnouncementId = options.statusPageAnnouncementId;
}
if (options.userId) {
emailLog.userId = options.userId;
}
if (options.teamId) {
emailLog.teamId = options.teamId;
}
// Set OnCall-related fields
if (options.onCallPolicyId) {
emailLog.onCallDutyPolicyId = options.onCallPolicyId;
}
if (options.onCallPolicyEscalationRuleId) {
emailLog.onCallDutyPolicyEscalationRuleId =
options.onCallPolicyEscalationRuleId;
}
if (options.onCallScheduleId) {
emailLog.onCallDutyPolicyScheduleId = options.onCallScheduleId;
}
}
// default vars.

View File

@@ -0,0 +1,44 @@
import PushNotificationRequest from "Common/Types/PushNotification/PushNotificationRequest";
import ObjectID from "Common/Types/ObjectID";
import PushNotificationServiceCommon from "Common/Server/Services/PushNotificationService";
export default class PushNotificationService {
public static async send(
request: PushNotificationRequest,
options: {
projectId?: ObjectID | undefined;
isSensitive?: boolean;
userOnCallLogTimelineId?: ObjectID | undefined;
incidentId?: ObjectID | undefined;
alertId?: ObjectID | undefined;
scheduledMaintenanceId?: ObjectID | undefined;
statusPageId?: ObjectID | undefined;
statusPageAnnouncementId?: ObjectID | undefined;
userId?: ObjectID | undefined;
onCallPolicyId?: ObjectID | undefined;
onCallPolicyEscalationRuleId?: ObjectID | undefined;
onCallDutyPolicyExecutionLogTimelineId?: ObjectID | undefined;
onCallScheduleId?: ObjectID | undefined;
teamId?: ObjectID | undefined;
} = {},
): Promise<void> {
// Delegate to Common service which now handles logging and timeline updates
await PushNotificationServiceCommon.sendPushNotification(request, {
projectId: options.projectId,
isSensitive: Boolean(options.isSensitive),
userOnCallLogTimelineId: options.userOnCallLogTimelineId,
incidentId: options.incidentId,
alertId: options.alertId,
scheduledMaintenanceId: options.scheduledMaintenanceId,
statusPageId: options.statusPageId,
statusPageAnnouncementId: options.statusPageAnnouncementId,
userId: options.userId,
onCallPolicyId: options.onCallPolicyId,
onCallPolicyEscalationRuleId: options.onCallPolicyEscalationRuleId,
onCallDutyPolicyExecutionLogTimelineId:
options.onCallDutyPolicyExecutionLogTimelineId,
onCallScheduleId: options.onCallScheduleId,
teamId: options.teamId,
});
}
}

View File

@@ -31,6 +31,18 @@ export default class SmsService {
customTwilioConfig?: TwilioConfig | undefined;
isSensitive?: boolean; // if true, message will not be logged
userOnCallLogTimelineId?: ObjectID | undefined;
incidentId?: ObjectID | undefined;
alertId?: ObjectID | undefined;
scheduledMaintenanceId?: ObjectID | undefined;
statusPageId?: ObjectID | undefined;
statusPageAnnouncementId?: ObjectID | undefined;
userId?: ObjectID | undefined;
// On-call policy related fields
onCallPolicyId?: ObjectID | undefined;
onCallPolicyEscalationRuleId?: ObjectID | undefined;
onCallDutyPolicyExecutionLogTimelineId?: ObjectID | undefined;
onCallScheduleId?: ObjectID | undefined;
teamId?: ObjectID | undefined;
},
): Promise<void> {
let smsError: Error | null = null;
@@ -71,6 +83,48 @@ export default class SmsService {
smsLog.projectId = options.projectId;
}
if (options.incidentId) {
smsLog.incidentId = options.incidentId;
}
if (options.alertId) {
smsLog.alertId = options.alertId;
}
if (options.scheduledMaintenanceId) {
smsLog.scheduledMaintenanceId = options.scheduledMaintenanceId;
}
if (options.statusPageId) {
smsLog.statusPageId = options.statusPageId;
}
if (options.statusPageAnnouncementId) {
smsLog.statusPageAnnouncementId = options.statusPageAnnouncementId;
}
if (options.userId) {
smsLog.userId = options.userId;
}
if (options.teamId) {
smsLog.teamId = options.teamId;
}
// Set OnCall-related fields
if (options.onCallPolicyId) {
smsLog.onCallDutyPolicyId = options.onCallPolicyId;
}
if (options.onCallPolicyEscalationRuleId) {
smsLog.onCallDutyPolicyEscalationRuleId =
options.onCallPolicyEscalationRuleId;
}
if (options.onCallScheduleId) {
smsLog.onCallDutyPolicyScheduleId = options.onCallScheduleId;
}
const twilioConfig: TwilioConfig | null =
options.customTwilioConfig || (await getTwilioConfig());

View File

@@ -0,0 +1,424 @@
import {
WhatsAppTextDefaultCostInCents,
getMetaWhatsAppConfig,
MetaWhatsAppConfig,
DEFAULT_META_WHATSAPP_API_VERSION,
} from "../Config";
import BadDataException from "Common/Types/Exception/BadDataException";
import ObjectID from "Common/Types/ObjectID";
import UserNotificationStatus from "Common/Types/UserNotification/UserNotificationStatus";
import WhatsAppMessage from "Common/Types/WhatsApp/WhatsAppMessage";
import WhatsAppStatus from "Common/Types/WhatsAppStatus";
import { JSONArray, JSONObject } from "Common/Types/JSON";
import { IsBillingEnabled } from "Common/Server/EnvironmentConfig";
import NotificationService from "Common/Server/Services/NotificationService";
import ProjectService from "Common/Server/Services/ProjectService";
import UserOnCallLogTimelineService from "Common/Server/Services/UserOnCallLogTimelineService";
import WhatsAppLogService from "Common/Server/Services/WhatsAppLogService";
import logger from "Common/Server/Utils/Logger";
import Project from "Common/Models/DatabaseModels/Project";
import WhatsAppLog from "Common/Models/DatabaseModels/WhatsAppLog";
import API from "Common/Utils/API";
import HTTPErrorResponse from "Common/Types/API/HTTPErrorResponse";
import HTTPResponse from "Common/Types/API/HTTPResponse";
import Protocol from "Common/Types/API/Protocol";
import Route from "Common/Types/API/Route";
import URL from "Common/Types/API/URL";
const SENSITIVE_MESSAGE_PLACEHOLDER: string =
"This message is sensitive and is not logged";
export default class WhatsAppService {
public static async sendWhatsApp(
message: WhatsAppMessage,
options: {
projectId?: ObjectID | undefined;
isSensitive?: boolean | undefined;
userOnCallLogTimelineId?: ObjectID | undefined;
incidentId?: ObjectID | undefined;
alertId?: ObjectID | undefined;
scheduledMaintenanceId?: ObjectID | undefined;
statusPageId?: ObjectID | undefined;
statusPageAnnouncementId?: ObjectID | undefined;
userId?: ObjectID | undefined;
onCallPolicyId?: ObjectID | undefined;
onCallPolicyEscalationRuleId?: ObjectID | undefined;
onCallDutyPolicyExecutionLogTimelineId?: ObjectID | undefined;
onCallScheduleId?: ObjectID | undefined;
teamId?: ObjectID | undefined;
} = {},
): Promise<void> {
let sendError: Error | null = null;
const whatsAppLog: WhatsAppLog = new WhatsAppLog();
try {
if (!message.to) {
throw new BadDataException(
"WhatsApp recipient phone number is required",
);
}
if (!message.body && !message.templateKey) {
throw new BadDataException(
"Either WhatsApp message body or template key must be provided",
);
}
const config: MetaWhatsAppConfig = await getMetaWhatsAppConfig();
const isSensitiveMessage: boolean = Boolean(options.isSensitive);
const messageSummary: string = isSensitiveMessage
? SENSITIVE_MESSAGE_PLACEHOLDER
: message.body ||
(message.templateKey
? `Template: ${message.templateKey}${
message.templateVariables
? " Variables: " + JSON.stringify(message.templateVariables)
: ""
}`
: "");
whatsAppLog.toNumber = message.to;
whatsAppLog.messageText = messageSummary;
whatsAppLog.whatsAppCostInUSDCents = 0;
if (options.projectId) {
whatsAppLog.projectId = options.projectId;
}
if (options.incidentId) {
whatsAppLog.incidentId = options.incidentId;
}
if (options.alertId) {
whatsAppLog.alertId = options.alertId;
}
if (options.scheduledMaintenanceId) {
whatsAppLog.scheduledMaintenanceId = options.scheduledMaintenanceId;
}
if (options.statusPageId) {
whatsAppLog.statusPageId = options.statusPageId;
}
if (options.statusPageAnnouncementId) {
whatsAppLog.statusPageAnnouncementId = options.statusPageAnnouncementId;
}
if (options.userId) {
whatsAppLog.userId = options.userId;
}
if (options.teamId) {
whatsAppLog.teamId = options.teamId;
}
if (options.onCallPolicyId) {
whatsAppLog.onCallDutyPolicyId = options.onCallPolicyId;
}
if (options.onCallPolicyEscalationRuleId) {
whatsAppLog.onCallDutyPolicyEscalationRuleId =
options.onCallPolicyEscalationRuleId;
}
if (options.onCallScheduleId) {
whatsAppLog.onCallDutyPolicyScheduleId = options.onCallScheduleId;
}
let messageCost: number = 0;
const shouldChargeForMessage: boolean = IsBillingEnabled;
if (shouldChargeForMessage) {
messageCost = WhatsAppTextDefaultCostInCents / 100;
}
let project: Project | null = null;
if (options.projectId) {
project = await ProjectService.findOneById({
id: options.projectId,
select: {
smsOrCallCurrentBalanceInUSDCents: true,
lowCallAndSMSBalanceNotificationSentToOwners: true,
name: true,
notEnabledSmsOrCallNotificationSentToOwners: true,
},
props: {
isRoot: true,
},
});
if (!project) {
whatsAppLog.status = WhatsAppStatus.Error;
whatsAppLog.statusMessage = `Project ${options.projectId.toString()} not found.`;
logger.error(whatsAppLog.statusMessage);
await WhatsAppLogService.create({
data: whatsAppLog,
props: {
isRoot: true,
},
});
return;
}
if (shouldChargeForMessage) {
let updatedBalance: number =
project.smsOrCallCurrentBalanceInUSDCents || 0;
try {
updatedBalance = await NotificationService.rechargeIfBalanceIsLow(
project.id!,
);
} catch (err) {
logger.error(err);
}
project.smsOrCallCurrentBalanceInUSDCents = updatedBalance;
if (!project.smsOrCallCurrentBalanceInUSDCents) {
whatsAppLog.status = WhatsAppStatus.LowBalance;
whatsAppLog.statusMessage = `Project ${options.projectId.toString()} does not have enough balance for WhatsApp messages.`;
logger.error(whatsAppLog.statusMessage);
await WhatsAppLogService.create({
data: whatsAppLog,
props: {
isRoot: true,
},
});
if (!project.lowCallAndSMSBalanceNotificationSentToOwners) {
await ProjectService.updateOneById({
id: project.id!,
data: {
lowCallAndSMSBalanceNotificationSentToOwners: true,
},
props: {
isRoot: true,
},
});
await ProjectService.sendEmailToProjectOwners(
project.id!,
`Low WhatsApp message balance for ${project.name || ""}`,
`We tried to send a WhatsApp message to ${message.to.toString()} with message:<br/><br/>${messageSummary}<br/><br/>The message was not sent because your project does not have enough balance for WhatsApp messages. Current balance is ${
(project.smsOrCallCurrentBalanceInUSDCents || 0) / 100
} USD. Required balance for this message is ${messageCost} USD. Please enable auto recharge or recharge manually.`,
);
}
return;
}
if (project.smsOrCallCurrentBalanceInUSDCents < messageCost * 100) {
whatsAppLog.status = WhatsAppStatus.LowBalance;
whatsAppLog.statusMessage = `Project does not have enough balance to send WhatsApp message. Current balance is ${
project.smsOrCallCurrentBalanceInUSDCents / 100
} USD. Required balance is ${messageCost} USD.`;
logger.error(whatsAppLog.statusMessage);
await WhatsAppLogService.create({
data: whatsAppLog,
props: {
isRoot: true,
},
});
if (!project.lowCallAndSMSBalanceNotificationSentToOwners) {
await ProjectService.updateOneById({
id: project.id!,
data: {
lowCallAndSMSBalanceNotificationSentToOwners: true,
},
props: {
isRoot: true,
},
});
await ProjectService.sendEmailToProjectOwners(
project.id!,
`Low WhatsApp message balance for ${project.name || ""}`,
`We tried to send a WhatsApp message to ${message.to.toString()} with message:<br/><br/>${messageSummary}<br/><br/>The message was not sent because your project does not have enough balance for WhatsApp messages. Current balance is ${
project.smsOrCallCurrentBalanceInUSDCents / 100
} USD. Required balance is ${messageCost} USD. Please enable auto recharge or recharge manually.`,
);
}
return;
}
}
}
const payload: JSONObject = {
messaging_product: "whatsapp",
to: message.to.toString(),
} as JSONObject;
if(!message.templateKey){
throw new BadDataException("WhatsApp message template key is required");
}
if (message.templateKey) {
const template: JSONObject = {
name: message.templateKey,
language: {
code: message.templateLanguageCode || "en",
},
} as JSONObject;
if (
message.templateVariables &&
Object.keys(message.templateVariables).length > 0
) {
const parameters: JSONArray = [];
for (const value of Object.values(message.templateVariables)) {
parameters.push({
type: "text",
text: value,
} as JSONObject);
}
if (parameters.length > 0) {
template["components"] = [
{
type: "body",
parameters,
},
] as JSONArray;
}
}
payload["type"] = "template";
payload["template"] = template;
} else {
payload["type"] = "text";
payload["text"] = {
body: message.body || "",
} as JSONObject;
}
const apiVersion: string =
config.apiVersion?.trim() || DEFAULT_META_WHATSAPP_API_VERSION;
const url: URL = new URL(
Protocol.HTTPS,
"graph.facebook.com",
new Route(`${apiVersion}/${config.phoneNumberId}/messages`),
);
logger.debug(`WhatsApp API request: ${JSON.stringify(payload)}`);
const response: HTTPResponse<JSONObject> | HTTPErrorResponse =
await API.post<JSONObject>({
url,
data: payload,
headers: {
Authorization: `Bearer ${config.accessToken}`,
"Content-Type": "application/json",
},
});
if (response instanceof HTTPErrorResponse) {
logger.error("Failed to send WhatsApp message.");
logger.error(response);
const responseDataAsJSONObject: JSONObject = response.data;
const responseJsonAsJSONObject: JSONObject | undefined =
(response.jsonData as JSONObject | undefined) || undefined;
const detailedErrorMessage: string | undefined =
((responseDataAsJSONObject["error"] as JSONObject | undefined)?.[
"message"
] as string | undefined) ||
((responseJsonAsJSONObject?.["error"] as JSONObject | undefined)?.[
"message"
] as string | undefined);
throw new BadDataException(
detailedErrorMessage || "Failed to send WhatsApp message.",
);
}
const responseData: JSONObject = (response.jsonData || {}) as JSONObject;
let messageId: string | undefined = undefined;
const messagesArray: JSONArray | undefined =
(responseData["messages"] as JSONArray) || undefined;
if (Array.isArray(messagesArray) && messagesArray.length > 0) {
const firstMessage: JSONObject = messagesArray[0] as JSONObject;
if (firstMessage["id"]) {
messageId = firstMessage["id"] as string;
}
}
whatsAppLog.status = WhatsAppStatus.Success;
whatsAppLog.statusMessage = messageId
? `Message ID: ${messageId}`
: "WhatsApp message sent successfully";
if (shouldChargeForMessage && project) {
const deduction: number = Math.floor(messageCost * 100);
whatsAppLog.whatsAppCostInUSDCents = deduction;
project.smsOrCallCurrentBalanceInUSDCents = Math.max(
0,
Math.floor(
(project.smsOrCallCurrentBalanceInUSDCents || 0) - deduction,
),
);
await ProjectService.updateOneById({
id: project.id!,
data: {
smsOrCallCurrentBalanceInUSDCents:
project.smsOrCallCurrentBalanceInUSDCents,
notEnabledSmsOrCallNotificationSentToOwners: false,
},
props: {
isRoot: true,
},
});
}
} catch (error: any) {
logger.error("Failed to send WhatsApp message.");
logger.error(error);
whatsAppLog.whatsAppCostInUSDCents = 0;
whatsAppLog.status = WhatsAppStatus.Error;
const errorMessage: string =
error && error.message ? error.message.toString() : `${error}`;
whatsAppLog.statusMessage = errorMessage;
sendError = error;
}
if (options.projectId) {
await WhatsAppLogService.create({
data: whatsAppLog,
props: {
isRoot: true,
},
});
}
if (options.userOnCallLogTimelineId) {
await UserOnCallLogTimelineService.updateOneById({
id: options.userOnCallLogTimelineId,
data: {
status:
whatsAppLog.status === WhatsAppStatus.Success
? UserNotificationStatus.Sent
: UserNotificationStatus.Error,
statusMessage: whatsAppLog.statusMessage,
},
props: {
isRoot: true,
},
});
}
if (sendError) {
throw sendError;
}
}
}

View File

@@ -42,10 +42,10 @@ loadPartials().catch((err: Error) => {
Handlebars.registerHelper("ifCond", function (v1, v2, options) {
if (v1 === v2) {
//@ts-ignore
//@ts-expect-error - Handlebars uses dynamic this context for template helpers
return options.fn(this);
}
//@ts-ignore
//@ts-expect-error - Handlebars uses dynamic this context for template helpers
return options.inverse(this);
});
@@ -56,9 +56,9 @@ Handlebars.registerHelper("concat", (v1: any, v2: any) => {
Handlebars.registerHelper("ifNotCond", function (v1, v2, options) {
if (v1 !== v2) {
//@ts-ignore
//@ts-expect-error - Handlebars uses dynamic this context for template helpers
return options.fn(this);
}
//@ts-ignore
//@ts-expect-error - Handlebars uses dynamic this context for template helpers
return options.inverse(this);
});

View File

@@ -1,8 +1,20 @@
{
"watch": ["./","../Common/Server", "../Common/Types", "../Common/Utils", "../Common/Models"],
"ext": "ts,json,tsx,env,js,jsx,hbs",
"ext": "ts,tsx",
"ignore": [
"./node_modules/**",
"./public/**",
"./bin/**",
"./build/**",
"greenlock.d/*"
],
"exec": "node --inspect=0.0.0.0:9229 --require ts-node/register Index.ts"
"watchOptions": {
"useFsEvents": false,
"interval": 500
},
"env": {
"TS_NODE_TRANSPILE_ONLY": "1",
"TS_NODE_FILES": "false"
},
"exec": "node -r ts-node/register/transpile-only Index.ts"
}

1
App/package-lock.json generated
View File

@@ -76,6 +76,7 @@
"crypto-js": "^4.2.0",
"dotenv": "^16.4.4",
"ejs": "^3.1.10",
"elkjs": "^0.10.0",
"esbuild": "^0.25.5",
"express": "^4.21.1",
"formik": "^2.4.6",

View File

@@ -1,5 +1,14 @@
import Project from "./Project";
import Incident from "./Incident";
import Alert from "./Alert";
import ScheduledMaintenance from "./ScheduledMaintenance";
import StatusPage from "./StatusPage";
import StatusPageAnnouncement from "./StatusPageAnnouncement";
import User from "./User";
import OnCallDutyPolicy from "./OnCallDutyPolicy";
import OnCallDutyPolicyEscalationRule from "./OnCallDutyPolicyEscalationRule";
import OnCallDutyPolicySchedule from "./OnCallDutyPolicySchedule";
import Team from "./Team";
import BaseModel from "./DatabaseBaseModel/DatabaseBaseModel";
import Route from "../../Types/API/Route";
import CallStatus from "../../Types/Call/CallStatus";
@@ -256,6 +265,575 @@ export default class CallLog extends BaseModel {
})
public callCostInUSDCents?: number = undefined;
// Relations to resources that triggered this Call (nullable)
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadCallLog,
],
update: [],
})
@TableColumn({
manyToOneRelationColumn: "incidentId",
type: TableColumnType.Entity,
modelType: Incident,
title: "Incident",
description: "Incident associated with this Call (if any)",
})
@ManyToOne(
() => {
return Incident;
},
{
eager: false,
nullable: true,
onDelete: "CASCADE",
orphanedRowAction: "nullify",
},
)
@JoinColumn({ name: "incidentId" })
public incident?: Incident = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadCallLog,
],
update: [],
})
@Index()
@TableColumn({
type: TableColumnType.ObjectID,
required: false,
canReadOnRelationQuery: true,
title: "Incident ID",
description: "ID of Incident associated with this Call (if any)",
})
@Column({
type: ColumnType.ObjectID,
nullable: true,
transformer: ObjectID.getDatabaseTransformer(),
})
public incidentId?: ObjectID = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadCallLog,
],
update: [],
})
@TableColumn({
manyToOneRelationColumn: "userId",
type: TableColumnType.Entity,
modelType: User,
title: "User",
description: "User who initiated this Call (if any)",
})
@ManyToOne(
() => {
return User;
},
{
eager: false,
nullable: true,
onDelete: "CASCADE",
orphanedRowAction: "nullify",
},
)
@JoinColumn({ name: "userId" })
public user?: User = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadCallLog,
],
update: [],
})
@Index()
@TableColumn({
type: TableColumnType.ObjectID,
required: false,
canReadOnRelationQuery: true,
title: "User ID",
description: "ID of User who initiated this Call (if any)",
})
@Column({
type: ColumnType.ObjectID,
nullable: true,
transformer: ObjectID.getDatabaseTransformer(),
})
public userId?: ObjectID = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadCallLog,
],
update: [],
})
@TableColumn({
manyToOneRelationColumn: "alertId",
type: TableColumnType.Entity,
modelType: Alert,
title: "Alert",
description: "Alert associated with this Call (if any)",
})
@ManyToOne(
() => {
return Alert;
},
{
eager: false,
nullable: true,
onDelete: "CASCADE",
orphanedRowAction: "nullify",
},
)
@JoinColumn({ name: "alertId" })
public alert?: Alert = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadCallLog,
],
update: [],
})
@Index()
@TableColumn({
type: TableColumnType.ObjectID,
required: false,
canReadOnRelationQuery: true,
title: "Alert ID",
description: "ID of Alert associated with this Call (if any)",
})
@Column({
type: ColumnType.ObjectID,
nullable: true,
transformer: ObjectID.getDatabaseTransformer(),
})
public alertId?: ObjectID = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadCallLog,
],
update: [],
})
@TableColumn({
manyToOneRelationColumn: "scheduledMaintenanceId",
type: TableColumnType.Entity,
modelType: ScheduledMaintenance,
title: "Scheduled Maintenance",
description: "Scheduled Maintenance associated with this Call (if any)",
})
@ManyToOne(
() => {
return ScheduledMaintenance;
},
{
eager: false,
nullable: true,
onDelete: "CASCADE",
orphanedRowAction: "nullify",
},
)
@JoinColumn({ name: "scheduledMaintenanceId" })
public scheduledMaintenance?: ScheduledMaintenance = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadCallLog,
],
update: [],
})
@Index()
@TableColumn({
type: TableColumnType.ObjectID,
required: false,
canReadOnRelationQuery: true,
title: "Scheduled Maintenance ID",
description:
"ID of Scheduled Maintenance associated with this Call (if any)",
})
@Column({
type: ColumnType.ObjectID,
nullable: true,
transformer: ObjectID.getDatabaseTransformer(),
})
public scheduledMaintenanceId?: ObjectID = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadCallLog,
],
update: [],
})
@TableColumn({
manyToOneRelationColumn: "statusPageId",
type: TableColumnType.Entity,
modelType: StatusPage,
title: "Status Page",
description: "Status Page associated with this Call (if any)",
})
@ManyToOne(
() => {
return StatusPage;
},
{
eager: false,
nullable: true,
onDelete: "CASCADE",
orphanedRowAction: "nullify",
},
)
@JoinColumn({ name: "statusPageId" })
public statusPage?: StatusPage = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadCallLog,
],
update: [],
})
@Index()
@TableColumn({
type: TableColumnType.ObjectID,
required: false,
canReadOnRelationQuery: true,
title: "Status Page ID",
description: "ID of Status Page associated with this Call (if any)",
})
@Column({
type: ColumnType.ObjectID,
nullable: true,
transformer: ObjectID.getDatabaseTransformer(),
})
public statusPageId?: ObjectID = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadCallLog,
],
update: [],
})
@TableColumn({
manyToOneRelationColumn: "statusPageAnnouncementId",
type: TableColumnType.Entity,
modelType: StatusPageAnnouncement,
title: "Status Page Announcement",
description: "Status Page Announcement associated with this Call (if any)",
})
@ManyToOne(
() => {
return StatusPageAnnouncement;
},
{
eager: false,
nullable: true,
onDelete: "CASCADE",
orphanedRowAction: "nullify",
},
)
@JoinColumn({ name: "statusPageAnnouncementId" })
public statusPageAnnouncement?: StatusPageAnnouncement = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadCallLog,
],
update: [],
})
@Index()
@TableColumn({
type: TableColumnType.ObjectID,
required: false,
canReadOnRelationQuery: true,
title: "Status Page Announcement ID",
description:
"ID of Status Page Announcement associated with this Call (if any)",
})
@Column({
type: ColumnType.ObjectID,
nullable: true,
transformer: ObjectID.getDatabaseTransformer(),
})
public statusPageAnnouncementId?: ObjectID = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadCallLog,
],
update: [],
})
@TableColumn({
manyToOneRelationColumn: "onCallDutyPolicyId",
type: TableColumnType.Entity,
modelType: OnCallDutyPolicy,
title: "On-Call Duty Policy",
description: "On-Call Duty Policy associated with this Call (if any)",
})
@ManyToOne(
() => {
return OnCallDutyPolicy;
},
{
eager: false,
nullable: true,
onDelete: "CASCADE",
orphanedRowAction: "nullify",
},
)
@JoinColumn({ name: "onCallDutyPolicyId" })
public onCallDutyPolicy?: OnCallDutyPolicy = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadCallLog,
],
update: [],
})
@Index()
@TableColumn({
type: TableColumnType.ObjectID,
required: false,
canReadOnRelationQuery: true,
title: "On-Call Duty Policy ID",
description: "ID of On-Call Duty Policy associated with this Call (if any)",
})
@Column({
type: ColumnType.ObjectID,
nullable: true,
transformer: ObjectID.getDatabaseTransformer(),
})
public onCallDutyPolicyId?: ObjectID = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadCallLog,
],
update: [],
})
@TableColumn({
manyToOneRelationColumn: "onCallDutyPolicyEscalationRuleId",
type: TableColumnType.Entity,
modelType: OnCallDutyPolicyEscalationRule,
title: "On-Call Duty Policy Escalation Rule",
description:
"On-Call Duty Policy Escalation Rule associated with this Call (if any)",
})
@ManyToOne(
() => {
return OnCallDutyPolicyEscalationRule;
},
{
eager: false,
nullable: true,
onDelete: "CASCADE",
orphanedRowAction: "nullify",
},
)
@JoinColumn({ name: "onCallDutyPolicyEscalationRuleId" })
public onCallDutyPolicyEscalationRule?: OnCallDutyPolicyEscalationRule =
undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadCallLog,
],
update: [],
})
@Index()
@TableColumn({
type: TableColumnType.ObjectID,
required: false,
canReadOnRelationQuery: true,
title: "On-Call Duty Policy Escalation Rule ID",
description:
"ID of On-Call Duty Policy Escalation Rule associated with this Call (if any)",
})
@Column({
type: ColumnType.ObjectID,
nullable: true,
transformer: ObjectID.getDatabaseTransformer(),
})
public onCallDutyPolicyEscalationRuleId?: ObjectID = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadCallLog,
],
update: [],
})
@TableColumn({
manyToOneRelationColumn: "onCallDutyPolicyScheduleId",
type: TableColumnType.Entity,
modelType: OnCallDutyPolicySchedule,
title: "On-Call Duty Policy Schedule",
description:
"On-Call Duty Policy Schedule associated with this Call (if any)",
})
@ManyToOne(
() => {
return OnCallDutyPolicySchedule;
},
{
eager: false,
nullable: true,
onDelete: "CASCADE",
orphanedRowAction: "nullify",
},
)
@JoinColumn({ name: "onCallDutyPolicyScheduleId" })
public onCallDutyPolicySchedule?: OnCallDutyPolicySchedule = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadCallLog,
],
update: [],
})
@Index()
@TableColumn({
type: TableColumnType.ObjectID,
required: false,
canReadOnRelationQuery: true,
title: "On-Call Duty Policy Schedule ID",
description:
"ID of On-Call Duty Policy Schedule associated with this Call (if any)",
})
@Column({
type: ColumnType.ObjectID,
nullable: true,
transformer: ObjectID.getDatabaseTransformer(),
})
public onCallDutyPolicyScheduleId?: ObjectID = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadCallLog,
],
update: [],
})
@TableColumn({
manyToOneRelationColumn: "teamId",
type: TableColumnType.Entity,
modelType: Team,
title: "Team",
description: "Team associated with this Call (if any)",
})
@ManyToOne(
() => {
return Team;
},
{
eager: false,
nullable: true,
onDelete: "CASCADE",
orphanedRowAction: "nullify",
},
)
@JoinColumn({ name: "teamId" })
public team?: Team = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadCallLog,
],
update: [],
})
@Index()
@TableColumn({
type: TableColumnType.ObjectID,
required: false,
canReadOnRelationQuery: true,
title: "Team ID",
description: "ID of Team associated with this Call (if any)",
})
@Column({
type: ColumnType.ObjectID,
nullable: true,
transformer: ObjectID.getDatabaseTransformer(),
})
public teamId?: ObjectID = undefined;
@ColumnAccessControl({
create: [],
read: [],

View File

@@ -729,6 +729,7 @@ export default class CopilotAction extends BaseModel {
type: TableColumnType.Boolean,
title: "Is Priority",
description: "Is Priority",
defaultValue: false,
})
@Column({
nullable: false,

View File

@@ -1,6 +1,15 @@
import Project from "./Project";
import Incident from "./Incident";
import Alert from "./Alert";
import ScheduledMaintenance from "./ScheduledMaintenance";
import StatusPage from "./StatusPage";
import ProjectSmtpConfig from "./ProjectSmtpConfig";
import StatusPageAnnouncement from "./StatusPageAnnouncement";
import User from "./User";
import OnCallDutyPolicy from "./OnCallDutyPolicy";
import OnCallDutyPolicyEscalationRule from "./OnCallDutyPolicyEscalationRule";
import OnCallDutyPolicySchedule from "./OnCallDutyPolicySchedule";
import Team from "./Team";
import BaseModel from "./DatabaseBaseModel/DatabaseBaseModel";
import Route from "../../Types/API/Route";
import ColumnAccessControl from "../../Types/Database/AccessControl/ColumnAccessControl";
@@ -289,6 +298,576 @@ export default class EmailLog extends BaseModel {
})
public projectSmtpConfigId?: ObjectID = undefined;
// Relations to resources that triggered this email (nullable)
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadEmailLog,
],
update: [],
})
@TableColumn({
manyToOneRelationColumn: "incidentId",
type: TableColumnType.Entity,
modelType: Incident,
title: "Incident",
description: "Incident associated with this email (if any)",
})
@ManyToOne(
() => {
return Incident;
},
{
eager: false,
nullable: true,
onDelete: "CASCADE",
orphanedRowAction: "nullify",
},
)
@JoinColumn({ name: "incidentId" })
public incident?: Incident = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadEmailLog,
],
update: [],
})
@Index()
@TableColumn({
type: TableColumnType.ObjectID,
required: false,
canReadOnRelationQuery: true,
title: "Incident ID",
description: "ID of Incident associated with this email (if any)",
})
@Column({
type: ColumnType.ObjectID,
nullable: true,
transformer: ObjectID.getDatabaseTransformer(),
})
public incidentId?: ObjectID = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadEmailLog,
],
update: [],
})
@TableColumn({
manyToOneRelationColumn: "userId",
type: TableColumnType.Entity,
modelType: User,
title: "User",
description: "User who initiated this email (if any)",
})
@ManyToOne(
() => {
return User;
},
{
eager: false,
nullable: true,
onDelete: "CASCADE",
orphanedRowAction: "nullify",
},
)
@JoinColumn({ name: "userId" })
public user?: User = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadEmailLog,
],
update: [],
})
@Index()
@TableColumn({
type: TableColumnType.ObjectID,
required: false,
canReadOnRelationQuery: true,
title: "User ID",
description: "ID of User who initiated this email (if any)",
})
@Column({
type: ColumnType.ObjectID,
nullable: true,
transformer: ObjectID.getDatabaseTransformer(),
})
public userId?: ObjectID = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadEmailLog,
],
update: [],
})
@TableColumn({
manyToOneRelationColumn: "alertId",
type: TableColumnType.Entity,
modelType: Alert,
title: "Alert",
description: "Alert associated with this email (if any)",
})
@ManyToOne(
() => {
return Alert;
},
{
eager: false,
nullable: true,
onDelete: "CASCADE",
orphanedRowAction: "nullify",
},
)
@JoinColumn({ name: "alertId" })
public alert?: Alert = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadEmailLog,
],
update: [],
})
@Index()
@TableColumn({
type: TableColumnType.ObjectID,
required: false,
canReadOnRelationQuery: true,
title: "Alert ID",
description: "ID of Alert associated with this email (if any)",
})
@Column({
type: ColumnType.ObjectID,
nullable: true,
transformer: ObjectID.getDatabaseTransformer(),
})
public alertId?: ObjectID = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadEmailLog,
],
update: [],
})
@TableColumn({
manyToOneRelationColumn: "scheduledMaintenanceId",
type: TableColumnType.Entity,
modelType: ScheduledMaintenance,
title: "Scheduled Maintenance",
description: "Scheduled Maintenance associated with this email (if any)",
})
@ManyToOne(
() => {
return ScheduledMaintenance;
},
{
eager: false,
nullable: true,
onDelete: "CASCADE",
orphanedRowAction: "nullify",
},
)
@JoinColumn({ name: "scheduledMaintenanceId" })
public scheduledMaintenance?: ScheduledMaintenance = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadEmailLog,
],
update: [],
})
@Index()
@TableColumn({
type: TableColumnType.ObjectID,
required: false,
canReadOnRelationQuery: true,
title: "Scheduled Maintenance ID",
description:
"ID of Scheduled Maintenance associated with this email (if any)",
})
@Column({
type: ColumnType.ObjectID,
nullable: true,
transformer: ObjectID.getDatabaseTransformer(),
})
public scheduledMaintenanceId?: ObjectID = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadEmailLog,
],
update: [],
})
@TableColumn({
manyToOneRelationColumn: "statusPageId",
type: TableColumnType.Entity,
modelType: StatusPage,
title: "Status Page",
description: "Status Page associated with this email (if any)",
})
@ManyToOne(
() => {
return StatusPage;
},
{
eager: false,
nullable: true,
onDelete: "CASCADE",
orphanedRowAction: "nullify",
},
)
@JoinColumn({ name: "statusPageId" })
public statusPage?: StatusPage = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadEmailLog,
],
update: [],
})
@Index()
@TableColumn({
type: TableColumnType.ObjectID,
required: false,
canReadOnRelationQuery: true,
title: "Status Page ID",
description: "ID of Status Page associated with this email (if any)",
})
@Column({
type: ColumnType.ObjectID,
nullable: true,
transformer: ObjectID.getDatabaseTransformer(),
})
public statusPageId?: ObjectID = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadEmailLog,
],
update: [],
})
@TableColumn({
manyToOneRelationColumn: "statusPageAnnouncementId",
type: TableColumnType.Entity,
modelType: StatusPageAnnouncement,
title: "Status Page Announcement",
description: "Status Page Announcement associated with this email (if any)",
})
@ManyToOne(
() => {
return StatusPageAnnouncement;
},
{
eager: false,
nullable: true,
onDelete: "CASCADE",
orphanedRowAction: "nullify",
},
)
@JoinColumn({ name: "statusPageAnnouncementId" })
public statusPageAnnouncement?: StatusPageAnnouncement = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadEmailLog,
],
update: [],
})
@Index()
@TableColumn({
type: TableColumnType.ObjectID,
required: false,
canReadOnRelationQuery: true,
title: "Status Page Announcement ID",
description:
"ID of Status Page Announcement associated with this email (if any)",
})
@Column({
type: ColumnType.ObjectID,
nullable: true,
transformer: ObjectID.getDatabaseTransformer(),
})
public statusPageAnnouncementId?: ObjectID = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadEmailLog,
],
update: [],
})
@TableColumn({
manyToOneRelationColumn: "onCallDutyPolicyId",
type: TableColumnType.Entity,
modelType: OnCallDutyPolicy,
title: "On-Call Duty Policy",
description: "On-Call Duty Policy associated with this email (if any)",
})
@ManyToOne(
() => {
return OnCallDutyPolicy;
},
{
eager: false,
nullable: true,
onDelete: "CASCADE",
orphanedRowAction: "nullify",
},
)
@JoinColumn({ name: "onCallDutyPolicyId" })
public onCallDutyPolicy?: OnCallDutyPolicy = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadEmailLog,
],
update: [],
})
@Index()
@TableColumn({
type: TableColumnType.ObjectID,
required: false,
canReadOnRelationQuery: true,
title: "On-Call Duty Policy ID",
description:
"ID of On-Call Duty Policy associated with this email (if any)",
})
@Column({
type: ColumnType.ObjectID,
nullable: true,
transformer: ObjectID.getDatabaseTransformer(),
})
public onCallDutyPolicyId?: ObjectID = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadEmailLog,
],
update: [],
})
@TableColumn({
manyToOneRelationColumn: "onCallDutyPolicyEscalationRuleId",
type: TableColumnType.Entity,
modelType: OnCallDutyPolicyEscalationRule,
title: "On-Call Duty Policy Escalation Rule",
description:
"On-Call Duty Policy Escalation Rule associated with this email (if any)",
})
@ManyToOne(
() => {
return OnCallDutyPolicyEscalationRule;
},
{
eager: false,
nullable: true,
onDelete: "CASCADE",
orphanedRowAction: "nullify",
},
)
@JoinColumn({ name: "onCallDutyPolicyEscalationRuleId" })
public onCallDutyPolicyEscalationRule?: OnCallDutyPolicyEscalationRule =
undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadEmailLog,
],
update: [],
})
@Index()
@TableColumn({
type: TableColumnType.ObjectID,
required: false,
canReadOnRelationQuery: true,
title: "On-Call Duty Policy Escalation Rule ID",
description:
"ID of On-Call Duty Policy Escalation Rule associated with this email (if any)",
})
@Column({
type: ColumnType.ObjectID,
nullable: true,
transformer: ObjectID.getDatabaseTransformer(),
})
public onCallDutyPolicyEscalationRuleId?: ObjectID = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadEmailLog,
],
update: [],
})
@TableColumn({
manyToOneRelationColumn: "onCallDutyPolicyScheduleId",
type: TableColumnType.Entity,
modelType: OnCallDutyPolicySchedule,
title: "On-Call Duty Policy Schedule",
description:
"On-Call Duty Policy Schedule associated with this email (if any)",
})
@ManyToOne(
() => {
return OnCallDutyPolicySchedule;
},
{
eager: false,
nullable: true,
onDelete: "CASCADE",
orphanedRowAction: "nullify",
},
)
@JoinColumn({ name: "onCallDutyPolicyScheduleId" })
public onCallDutyPolicySchedule?: OnCallDutyPolicySchedule = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadEmailLog,
],
update: [],
})
@Index()
@TableColumn({
type: TableColumnType.ObjectID,
required: false,
canReadOnRelationQuery: true,
title: "On-Call Duty Policy Schedule ID",
description:
"ID of On-Call Duty Policy Schedule associated with this email (if any)",
})
@Column({
type: ColumnType.ObjectID,
nullable: true,
transformer: ObjectID.getDatabaseTransformer(),
})
public onCallDutyPolicyScheduleId?: ObjectID = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadEmailLog,
],
update: [],
})
@TableColumn({
manyToOneRelationColumn: "teamId",
type: TableColumnType.Entity,
modelType: Team,
title: "Team",
description: "Team associated with this email (if any)",
})
@ManyToOne(
() => {
return Team;
},
{
eager: false,
nullable: true,
onDelete: "CASCADE",
orphanedRowAction: "nullify",
},
)
@JoinColumn({ name: "teamId" })
public team?: Team = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadEmailLog,
],
update: [],
})
@Index()
@TableColumn({
type: TableColumnType.ObjectID,
required: false,
canReadOnRelationQuery: true,
title: "Team ID",
description: "ID of Team associated with this email (if any)",
})
@Column({
type: ColumnType.ObjectID,
nullable: true,
transformer: ObjectID.getDatabaseTransformer(),
})
public teamId?: ObjectID = undefined;
@ColumnAccessControl({
create: [],
read: [],

View File

@@ -48,6 +48,7 @@ export default class GlobalConfig extends GlobalConfigModel {
type: TableColumnType.Boolean,
title: "Disable Signup",
description: "Should we disable new user sign up to this server?",
defaultValue: false,
})
@Column({
type: ColumnType.Boolean,
@@ -261,6 +262,96 @@ export default class GlobalConfig extends GlobalConfigModel {
})
public twilioSecondaryPhoneNumbers?: string = undefined; // phone numbers separated by comma
@ColumnAccessControl({
create: [],
read: [],
update: [],
})
@TableColumn({
type: TableColumnType.VeryLongText,
title: "Meta WhatsApp Access Token",
description:
"Access token generated from Meta for sending WhatsApp messages.",
})
@Column({
type: ColumnType.VeryLongText,
nullable: true,
unique: true,
})
public metaWhatsAppAccessToken?: string = undefined;
@ColumnAccessControl({
create: [],
read: [],
update: [],
})
@TableColumn({
type: TableColumnType.ShortText,
title: "Meta WhatsApp Phone Number ID",
description: "The WhatsApp Business phone number ID from Meta.",
})
@Column({
type: ColumnType.ShortText,
length: ColumnLength.ShortText,
nullable: true,
unique: true,
})
public metaWhatsAppPhoneNumberId?: string = undefined;
@ColumnAccessControl({
create: [],
read: [],
update: [],
})
@TableColumn({
type: TableColumnType.ShortText,
title: "Meta WhatsApp Business Account ID",
description: "Business account ID associated with your WhatsApp setup.",
})
@Column({
type: ColumnType.ShortText,
length: ColumnLength.ShortText,
nullable: true,
unique: true,
})
public metaWhatsAppBusinessAccountId?: string = undefined;
@ColumnAccessControl({
create: [],
read: [],
update: [],
})
@TableColumn({
type: TableColumnType.ShortText,
title: "Meta WhatsApp App ID",
description:
"Facebook App ID used for the WhatsApp Business Platform integration.",
})
@Column({
type: ColumnType.ShortText,
length: ColumnLength.ShortText,
nullable: true,
unique: true,
})
public metaWhatsAppAppId?: string = undefined;
@ColumnAccessControl({
create: [],
read: [],
update: [],
})
@TableColumn({
type: TableColumnType.VeryLongText,
title: "Meta WhatsApp App Secret",
description: "Facebook App Secret for the WhatsApp Business Platform.",
})
@Column({
type: ColumnType.VeryLongText,
nullable: true,
unique: true,
})
public metaWhatsAppAppSecret?: string = undefined;
@ColumnAccessControl({
create: [],
read: [],
@@ -338,6 +429,7 @@ export default class GlobalConfig extends GlobalConfigModel {
type: TableColumnType.Boolean,
title: "Is Master API Key Enabled",
description: "Is Master API Key Enabled?",
defaultValue: false,
})
@Column({
type: ColumnType.Boolean,

View File

@@ -27,6 +27,7 @@ import IconProp from "../../Types/Icon/IconProp";
import { JSONObject } from "../../Types/JSON";
import ObjectID from "../../Types/ObjectID";
import Permission from "../../Types/Permission";
import StatusPageSubscriberNotificationStatus from "../../Types/StatusPage/StatusPageSubscriberNotificationStatus";
import {
Column,
Entity,
@@ -733,29 +734,74 @@ export default class Incident extends BaseModel {
public changeMonitorStatusToId?: ObjectID = undefined;
@ColumnAccessControl({
create: [],
create: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.CreateProjectIncident,
],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadProjectIncident,
],
update: [],
update: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.EditProjectIncident,
],
})
@TableColumn({
isDefaultValueColumn: true,
computed: true,
hideColumnInDocumentation: true,
type: TableColumnType.Boolean,
title: "Are subscribers notified?",
description: "Are subscribers notified about this incident?",
defaultValue: false,
type: TableColumnType.ShortText,
title: "Subscriber Notification Status",
description:
"Status of notification sent to subscribers about this incident",
defaultValue: StatusPageSubscriberNotificationStatus.Pending,
})
@Column({
type: ColumnType.Boolean,
default: false,
type: ColumnType.ShortText,
default: StatusPageSubscriberNotificationStatus.Pending,
})
public isStatusPageSubscribersNotifiedOnIncidentCreated?: boolean = undefined;
public subscriberNotificationStatusOnIncidentCreated?: StatusPageSubscriberNotificationStatus =
undefined;
@ColumnAccessControl({
create: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.CreateProjectIncident,
],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadProjectIncident,
],
update: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.EditProjectIncident,
],
})
@TableColumn({
type: TableColumnType.VeryLongText,
title: "Notification Status Message",
description:
"Status message for subscriber notifications - includes success messages, failure reasons, or skip reasons",
required: false,
})
@Column({
type: ColumnType.VeryLongText,
nullable: true,
})
public subscriberNotificationStatusMessage?: string = undefined;
@ColumnAccessControl({
create: [

View File

@@ -17,6 +17,7 @@ import TenantColumn from "../../Types/Database/TenantColumn";
import IconProp from "../../Types/Icon/IconProp";
import ObjectID from "../../Types/ObjectID";
import Permission from "../../Types/Permission";
import StatusPageSubscriberNotificationStatus from "../../Types/StatusPage/StatusPageSubscriberNotificationStatus";
import { Column, Entity, Index, JoinColumn, ManyToOne } from "typeorm";
@EnableDocumentation()
@@ -341,29 +342,73 @@ export default class IncidentPublicNote extends BaseModel {
public note?: string = undefined;
@ColumnAccessControl({
create: [],
create: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.CreateIncidentPublicNote,
],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadIncidentPublicNote,
],
update: [],
update: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.EditIncidentPublicNote,
],
})
@TableColumn({
isDefaultValueColumn: true,
computed: true,
hideColumnInDocumentation: true,
type: TableColumnType.Boolean,
title: "Are subscribers notified?",
description: "Are subscribers notified about this note?",
defaultValue: false,
type: TableColumnType.ShortText,
title: "Subscriber Notification Status",
description: "Status of notification sent to subscribers about this note",
defaultValue: StatusPageSubscriberNotificationStatus.Pending,
})
@Column({
type: ColumnType.Boolean,
default: false,
type: ColumnType.ShortText,
default: StatusPageSubscriberNotificationStatus.Pending,
})
public isStatusPageSubscribersNotifiedOnNoteCreated?: boolean = undefined;
public subscriberNotificationStatusOnNoteCreated?: StatusPageSubscriberNotificationStatus =
undefined;
@ColumnAccessControl({
create: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.CreateIncidentPublicNote,
],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadIncidentPublicNote,
],
update: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.EditIncidentPublicNote,
],
})
@TableColumn({
type: TableColumnType.VeryLongText,
title: "Notification Status Message",
description:
"Status message for subscriber notifications - includes success messages, failure reasons, or skip reasons",
required: false,
})
@Column({
type: ColumnType.VeryLongText,
nullable: true,
})
public subscriberNotificationStatusMessage?: string = undefined;
@ColumnAccessControl({
create: [

View File

@@ -19,6 +19,7 @@ import IconProp from "../../Types/Icon/IconProp";
import { JSONObject } from "../../Types/JSON";
import ObjectID from "../../Types/ObjectID";
import Permission from "../../Types/Permission";
import StatusPageSubscriberNotificationStatus from "../../Types/StatusPage/StatusPageSubscriberNotificationStatus";
import { Column, Entity, Index, JoinColumn, ManyToOne } from "typeorm";
@EnableDocumentation()
@@ -391,29 +392,74 @@ export default class IncidentStateTimeline extends BaseModel {
public incidentStateId?: ObjectID = undefined;
@ColumnAccessControl({
create: [],
create: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.CreateIncidentStateTimeline,
],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadIncidentStateTimeline,
],
update: [],
update: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.EditIncidentStateTimeline,
],
})
@TableColumn({
isDefaultValueColumn: true,
computed: true,
hideColumnInDocumentation: true,
type: TableColumnType.Boolean,
title: "Are subscribers notified?",
description: "Are subscribers notified about this incident state change?",
defaultValue: false,
type: TableColumnType.ShortText,
title: "Subscriber Notification Status",
description:
"Status of notification sent to subscribers about this incident state change",
defaultValue: StatusPageSubscriberNotificationStatus.Pending,
})
@Column({
type: ColumnType.Boolean,
default: false,
type: ColumnType.ShortText,
default: StatusPageSubscriberNotificationStatus.Pending,
})
public isStatusPageSubscribersNotified?: boolean = undefined;
public subscriberNotificationStatus?: StatusPageSubscriberNotificationStatus =
undefined;
@ColumnAccessControl({
create: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.CreateIncidentStateTimeline,
],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadIncidentStateTimeline,
],
update: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.EditIncidentStateTimeline,
],
})
@TableColumn({
type: TableColumnType.VeryLongText,
title: "Notification Status Message",
description:
"Status message for subscriber notifications - includes success messages, failure reasons, or skip reasons",
required: false,
})
@Column({
type: ColumnType.VeryLongText,
nullable: true,
})
public subscriberNotificationStatusMessage?: string = undefined;
@ColumnAccessControl({
create: [
@@ -435,6 +481,7 @@ export default class IncidentStateTimeline extends BaseModel {
type: TableColumnType.Boolean,
title: "Should subscribers be notified?",
description: "Should subscribers be notified about this state change?",
defaultValue: true,
})
@Column({
type: ColumnType.Boolean,
@@ -461,6 +508,7 @@ export default class IncidentStateTimeline extends BaseModel {
isDefaultValueColumn: true,
title: "Are Owners Notified",
description: "Are owners notified of state change?",
defaultValue: false,
})
@Column({
type: ColumnType.Boolean,

View File

@@ -716,6 +716,77 @@ export default class IncidentTemplate extends BaseModel {
})
public changeMonitorStatusToId?: ObjectID = undefined;
@ColumnAccessControl({
create: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.CreateIncidentTemplate,
],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadIncidentTemplate,
],
update: [],
})
@TableColumn({
manyToOneRelationColumn: "initialIncidentStateId",
type: TableColumnType.Entity,
modelType: IncidentState,
title: "Initial Incident State",
description:
"Relation to Incident State Object. Incidents created from this template will start in this state.",
})
@ManyToOne(
() => {
return IncidentState;
},
{
eager: false,
nullable: true,
orphanedRowAction: "nullify",
},
)
@JoinColumn({ name: "initialIncidentStateId" })
public initialIncidentState?: IncidentState = undefined;
@ColumnAccessControl({
create: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.CreateIncidentTemplate,
],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadIncidentTemplate,
],
update: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.EditIncidentTemplate,
],
})
@Index()
@TableColumn({
type: TableColumnType.ObjectID,
required: false,
title: "Initial Incident State ID",
description:
"Relation to Incident State Object ID. Incidents created from this template will start in this state.",
})
@Column({
type: ColumnType.ObjectID,
nullable: true,
transformer: ObjectID.getDatabaseTransformer(),
})
public initialIncidentStateId?: ObjectID = undefined;
@ColumnAccessControl({
create: [
Permission.ProjectOwner,

View File

@@ -100,6 +100,9 @@ import ServiceCopilotCodeRepository from "./ServiceCopilotCodeRepository";
import ShortLink from "./ShortLink";
// SMS
import SmsLog from "./SmsLog";
import WhatsAppLog from "./WhatsAppLog";
import PushNotificationLog from "./PushNotificationLog";
import WorkspaceNotificationLog from "./WorkspaceNotificationLog";
// Status Page
import StatusPage from "./StatusPage";
import StatusPageAnnouncement from "./StatusPageAnnouncement";
@@ -114,12 +117,14 @@ import StatusPageOwnerTeam from "./StatusPageOwnerTeam";
import StatusPageOwnerUser from "./StatusPageOwnerUser";
import StatusPagePrivateUser from "./StatusPagePrivateUser";
import StatusPageResource from "./StatusPageResource";
import StatusPageSCIM from "./StatusPageSCIM";
import StatusPageSSO from "./StatusPageSso";
import StatusPageSubscriber from "./StatusPageSubscriber";
// Team
import Team from "./Team";
import TeamMember from "./TeamMember";
import TeamPermission from "./TeamPermission";
import TeamComplianceSetting from "./TeamComplianceSetting";
import TelemetryService from "./TelemetryService";
import UsageBilling from "./TelemetryUsageBilling";
import User from "./User";
@@ -127,6 +132,7 @@ import UserCall from "./UserCall";
// Notification Methods
import UserEmail from "./UserEmail";
import UserPush from "./UserPush";
import UserWhatsApp from "./UserWhatsApp";
// User Notification Rules
import UserNotificationRule from "./UserNotificationRule";
import UserNotificationSetting from "./UserNotificationSetting";
@@ -142,7 +148,8 @@ import ServiceCatalogDependency from "./ServiceCatalogDependency";
import ServiceCatalogMonitor from "./ServiceCatalogMonitor";
import ServiceCatalogTelemetryService from "./ServiceCatalogTelemetryService";
import UserTwoFactorAuth from "./UserTwoFactorAuth";
import UserTotpAuth from "./UserTotpAuth";
import UserWebAuthn from "./UserWebAuthn";
import TelemetryIngestionKey from "./TelemetryIngestionKey";
@@ -175,10 +182,11 @@ import WorkspaceUserAuthToken from "./WorkspaceUserAuthToken";
import WorkspaceProjectAuthToken from "./WorkspaceProjectAuthToken";
import WorkspaceSetting from "./WorkspaceSetting";
import WorkspaceNotificationRule from "./WorkspaceNotificationRule";
import ProjectUser from "./ProjectUser";
import OnCallDutyPolicyUserOverride from "./OnCallDutyPolicyUserOverride";
import MonitorFeed from "./MonitorFeed";
import MetricType from "./MetricType";
import ProjectSCIM from "./ProjectSCIM";
const AllModelTypes: Array<{
new (): BaseModel;
@@ -192,6 +200,7 @@ const AllModelTypes: Array<{
Team,
TeamMember,
TeamPermission,
TeamComplianceSetting,
ApiKey,
Label,
ApiKeyPermission,
@@ -276,6 +285,7 @@ const AllModelTypes: Array<{
ProjectSSO,
StatusPageSSO,
StatusPageSCIM,
MonitorProbe,
@@ -289,6 +299,9 @@ const AllModelTypes: Array<{
StatusPageOwnerUser,
SmsLog,
WhatsAppLog,
PushNotificationLog,
WorkspaceNotificationLog,
CallLog,
EmailLog,
@@ -296,6 +309,7 @@ const AllModelTypes: Array<{
UserSms,
UserCall,
UserPush,
UserWhatsApp,
UserNotificationRule,
UserOnCallLog,
@@ -357,7 +371,8 @@ const AllModelTypes: Array<{
ProbeOwnerTeam,
ProbeOwnerUser,
UserTwoFactorAuth,
UserTotpAuth,
UserWebAuthn,
TelemetryIngestionKey,
@@ -373,13 +388,13 @@ const AllModelTypes: Array<{
WorkspaceSetting,
WorkspaceNotificationRule,
ProjectUser,
MonitorFeed,
MetricType,
OnCallDutyPolicyTimeLog,
ProjectSCIM,
];
const modelTypeMap: { [key: string]: { new (): BaseModel } } = {};

View File

@@ -456,6 +456,7 @@ export default class MonitorStatus extends BaseModel {
canReadOnRelationQuery: true,
title: "Is Offline State",
description: "Is this monitor in offline state?",
defaultValue: false,
})
@Column({
type: ColumnType.Boolean,

View File

@@ -29,10 +29,17 @@ import {
ManyToOne,
} from "typeorm";
import NotificationRuleWorkspaceChannel from "../../Types/Workspace/NotificationRules/NotificationRuleWorkspaceChannel";
import EnableWorkflow from "../../Types/Database/EnableWorkflow";
@EnableDocumentation()
@AccessControlColumn("labels")
@TenantColumn("projectId")
@EnableWorkflow({
create: true,
delete: true,
update: true,
read: true,
})
@TableAccessControl({
create: [
Permission.ProjectOwner,
@@ -455,7 +462,7 @@ export default class OnCallDutyPolicy extends BaseModel {
@TableColumn({
required: true,
isDefaultValueColumn: true,
type: TableColumnType.Boolean,
type: TableColumnType.Number,
canReadOnRelationQuery: true,
title: "Repeat Policy Times If No One Acknowledges",
description:

View File

@@ -46,7 +46,7 @@ import { Column, Entity, Index, JoinColumn, ManyToOne } from "typeorm";
Permission.EditProjectOnCallDutyPolicyEscalationRule,
],
})
@CrudApiEndpoint(new Route("/on-call-duty-policy-esclation-rule"))
@CrudApiEndpoint(new Route("/on-call-duty-policy-escalation-rule"))
@Entity({
name: "OnCallDutyPolicyEscalationRule",
})

View File

@@ -47,7 +47,7 @@ import { Column, Entity, Index, JoinColumn, ManyToOne } from "typeorm";
Permission.EditProjectOnCallDutyPolicyEscalationRuleSchedule,
],
})
@CrudApiEndpoint(new Route("/on-call-duty-policy-esclation-rule-schedule"))
@CrudApiEndpoint(new Route("/on-call-duty-policy-escalation-rule-schedule"))
@Entity({
name: "OnCallDutyPolicyEscalationRuleSchedule",
})

View File

@@ -47,7 +47,7 @@ import { Column, Entity, Index, JoinColumn, ManyToOne } from "typeorm";
Permission.EditProjectOnCallDutyPolicyEscalationRuleTeam,
],
})
@CrudApiEndpoint(new Route("/on-call-duty-policy-esclation-rule-team"))
@CrudApiEndpoint(new Route("/on-call-duty-policy-escalation-rule-team"))
@Entity({
name: "OnCallDutyPolicyEscalationRuleTeam",
})

View File

@@ -46,7 +46,7 @@ import { Column, Entity, Index, JoinColumn, ManyToOne } from "typeorm";
Permission.EditProjectOnCallDutyPolicyEscalationRuleUser,
],
})
@CrudApiEndpoint(new Route("/on-call-duty-policy-esclation-rule-user"))
@CrudApiEndpoint(new Route("/on-call-duty-policy-escalation-rule-user"))
@Entity({
name: "OnCallDutyPolicyEscalationRuleUser",
})

View File

@@ -25,6 +25,7 @@ import Permission from "../../Types/Permission";
import UserNotificationEventType from "../../Types/UserNotification/UserNotificationEventType";
import { Column, Entity, Index, JoinColumn, ManyToOne } from "typeorm";
import Alert from "./Alert";
import EnableWorkflow from "../../Types/Database/EnableWorkflow";
@TableBillingAccessControl({
create: PlanType.Growth,
@@ -32,6 +33,12 @@ import Alert from "./Alert";
update: PlanType.Growth,
delete: PlanType.Growth,
})
@EnableWorkflow({
create: true,
delete: true,
update: true,
read: true,
})
@EnableDocumentation()
@TenantColumn("projectId")
@TableAccessControl({

View File

@@ -29,6 +29,7 @@ import {
ManyToMany,
ManyToOne,
} from "typeorm";
import EnableWorkflow from "../../Types/Database/EnableWorkflow";
@EnableDocumentation()
@TableBillingAccessControl({
@@ -37,6 +38,12 @@ import {
update: PlanType.Growth,
delete: PlanType.Growth,
})
@EnableWorkflow({
create: true,
delete: true,
update: true,
read: true,
})
@AccessControlColumn("labels")
@TenantColumn("projectId")
@TableAccessControl({

View File

@@ -17,6 +17,7 @@ import Permission from "../../Types/Permission";
import { Column, Entity, Index, JoinColumn, ManyToOne } from "typeorm";
import TableBillingAccessControl from "../../Types/Database/AccessControl/TableBillingAccessControl";
import { PlanType } from "../../Types/Billing/SubscriptionPlan";
import EnableWorkflow from "../../Types/Database/EnableWorkflow";
@EnableDocumentation()
@TableBillingAccessControl({
@@ -26,6 +27,12 @@ import { PlanType } from "../../Types/Billing/SubscriptionPlan";
delete: PlanType.Growth,
})
@TenantColumn("projectId")
@EnableWorkflow({
create: true,
delete: true,
update: true,
read: true,
})
@TableAccessControl({
create: [
Permission.ProjectOwner,

View File

@@ -132,9 +132,11 @@ export default class OnCallDutyPolicyUserOverride extends BaseModel {
})
public projectId?: ObjectID = undefined;
// If this is null then it's a global override
// If this is set then it's a policy specific override
// Policy specifc override will take precedence over global override
/*
* If this is null then it's a global override
* If this is set then it's a policy specific override
* Policy specifc override will take precedence over global override
*/
@ColumnAccessControl({
create: [

View File

@@ -248,6 +248,84 @@ export default class Project extends TenantModel {
})
public paymentProviderCustomerId?: string = undefined;
@ColumnAccessControl({
create: [Permission.ProjectOwner, Permission.ManageProjectBilling],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadProject,
Permission.UnAuthorizedSsoUser,
Permission.ProjectUser,
],
update: [Permission.ProjectOwner, Permission.ManageProjectBilling],
})
@TableColumn({
type: TableColumnType.LongText,
title: "Business Details / Billing Address",
description:
"Business legal name, address and any tax information to appear on invoices.",
})
@Column({
type: ColumnType.LongText,
length: ColumnLength.LongText,
nullable: true,
unique: false,
})
public businessDetails?: string = undefined;
@ColumnAccessControl({
create: [Permission.ProjectOwner, Permission.ManageProjectBilling],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadProject,
Permission.UnAuthorizedSsoUser,
Permission.ProjectUser,
],
update: [Permission.ProjectOwner, Permission.ManageProjectBilling],
})
@TableColumn({
type: TableColumnType.ShortText,
title: "Business Country (ISO Alpha-2)",
description:
"Two-letter ISO country code for billing address (e.g., US, GB, DE).",
})
@Column({
type: ColumnType.ShortText,
length: ColumnLength.ShortText,
nullable: true,
unique: false,
})
public businessDetailsCountry?: string = undefined;
@ColumnAccessControl({
create: [Permission.ProjectOwner, Permission.ManageProjectBilling],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadProject,
Permission.UnAuthorizedSsoUser,
Permission.ProjectUser,
],
update: [Permission.ProjectOwner, Permission.ManageProjectBilling],
})
@TableColumn({
type: TableColumnType.Email,
title: "Finance / Accounting Email",
description:
"Invoices, receipts and billing related notifications will be sent to this email in addition to project owner.",
})
@Column({
type: ColumnType.Email,
length: ColumnLength.Email,
nullable: true,
unique: false,
})
public financeAccountingEmail?: string = undefined;
@ColumnAccessControl({
create: [],
read: [
@@ -720,6 +798,33 @@ export default class Project extends TenantModel {
})
public enableSmsNotifications?: boolean = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadProject,
Permission.UnAuthorizedSsoUser,
Permission.ProjectUser,
],
update: [Permission.ProjectOwner, Permission.ManageProjectBilling],
})
@TableColumn({
required: true,
isDefaultValueColumn: true,
type: TableColumnType.Boolean,
title: "Enable WhatsApp Notifications",
description: "Enable WhatsApp notifications for this project.",
defaultValue: false,
})
@Column({
nullable: false,
default: false,
type: ColumnType.Boolean,
})
public enableWhatsAppNotifications?: boolean = undefined;
@ColumnAccessControl({
create: [],
read: [
@@ -1213,4 +1318,63 @@ export default class Project extends TenantModel {
type: ColumnType.Boolean,
})
public letCustomerSupportAccessProject?: boolean = undefined;
/*
* This is an internal field. This is used for internal analytics for example: Metabase.
* Values can be between 0 and 100.
*/
@ColumnAccessControl({
create: [],
read: [],
update: [],
})
@TableColumn({
required: true,
type: TableColumnType.Number,
isDefaultValueColumn: true,
hideColumnInDocumentation: true,
title: "Discount Percent",
description: "Discount percentage applied to the project billing",
defaultValue: 0,
})
@Column({
type: ColumnType.Number,
nullable: false,
unique: false,
default: 0,
})
public discountPercent?: number = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadProject,
Permission.UnAuthorizedSsoUser,
Permission.ProjectUser,
],
update: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.EditProject,
],
})
@TableColumn({
required: false,
type: TableColumnType.Boolean,
isDefaultValueColumn: false,
title: "Do NOT auto-add Global Probes to new monitors",
description:
"If enabled, global probes will NOT be automatically added to new monitors. Enable this only if you are using ONLY custom probes to monitor your resources.",
defaultValue: false,
})
@Column({
type: ColumnType.Boolean,
nullable: false,
unique: false,
default: false,
})
public doNotAddGlobalProbesByDefaultOnNewMonitors?: boolean = undefined;
}

View File

@@ -0,0 +1,482 @@
import Project from "./Project";
import Team from "./Team";
import User from "./User";
import BaseModel from "./DatabaseBaseModel/DatabaseBaseModel";
import Route from "../../Types/API/Route";
import { PlanType } from "../../Types/Billing/SubscriptionPlan";
import ColumnAccessControl from "../../Types/Database/AccessControl/ColumnAccessControl";
import TableAccessControl from "../../Types/Database/AccessControl/TableAccessControl";
import TableBillingAccessControl from "../../Types/Database/AccessControl/TableBillingAccessControl";
import ColumnLength from "../../Types/Database/ColumnLength";
import ColumnType from "../../Types/Database/ColumnType";
import CrudApiEndpoint from "../../Types/Database/CrudApiEndpoint";
import TableColumn from "../../Types/Database/TableColumn";
import TableColumnType from "../../Types/Database/TableColumnType";
import TableMetadata from "../../Types/Database/TableMetadata";
import TenantColumn from "../../Types/Database/TenantColumn";
import UniqueColumnBy from "../../Types/Database/UniqueColumnBy";
import IconProp from "../../Types/Icon/IconProp";
import ObjectID from "../../Types/ObjectID";
import Permission from "../../Types/Permission";
import {
Column,
Entity,
Index,
JoinColumn,
JoinTable,
ManyToMany,
ManyToOne,
} from "typeorm";
@TableBillingAccessControl({
create: PlanType.Scale,
read: PlanType.Scale,
update: PlanType.Scale,
delete: PlanType.Scale,
})
@TenantColumn("projectId")
@TableAccessControl({
create: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.CreateProjectSSO,
],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadProjectSSO,
],
delete: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.DeleteProjectSSO,
],
update: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.EditProjectSSO,
],
})
@CrudApiEndpoint(new Route("/project-scim"))
@TableMetadata({
tableName: "ProjectSCIM",
singularName: "SCIM",
pluralName: "SCIM",
icon: IconProp.Lock,
tableDescription: "Manage SCIM auto-provisioning for your project",
})
@Entity({
name: "ProjectSCIM",
})
export default class ProjectSCIM extends BaseModel {
@ColumnAccessControl({
create: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.CreateProjectSSO,
],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadProjectSSO,
],
update: [],
})
@TableColumn({
manyToOneRelationColumn: "projectId",
type: TableColumnType.Entity,
modelType: Project,
title: "Project",
description: "Relation to Project Resource in which this object belongs",
})
@ManyToOne(
() => {
return Project;
},
{
eager: false,
nullable: true,
onDelete: "CASCADE",
orphanedRowAction: "nullify",
},
)
@JoinColumn({ name: "projectId" })
public project?: Project = undefined;
@ColumnAccessControl({
create: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.CreateProjectSSO,
],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadProjectSSO,
],
update: [],
})
@Index()
@TableColumn({
type: TableColumnType.ObjectID,
required: true,
canReadOnRelationQuery: true,
title: "Project ID",
description: "ID of your OneUptime Project in which this object belongs",
})
@Column({
type: ColumnType.ObjectID,
nullable: false,
transformer: ObjectID.getDatabaseTransformer(),
})
public projectId?: ObjectID = undefined;
@ColumnAccessControl({
create: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.CreateProjectSSO,
],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadProjectSSO,
],
update: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.EditProjectSSO,
],
})
@TableColumn({
required: true,
type: TableColumnType.ShortText,
canReadOnRelationQuery: true,
title: "Name",
description: "Any friendly name for this SCIM configuration",
})
@Column({
nullable: false,
type: ColumnType.ShortText,
length: ColumnLength.ShortText,
})
@UniqueColumnBy("projectId")
public name?: string = undefined;
@ColumnAccessControl({
create: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.CreateProjectSSO,
],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadProjectSSO,
],
update: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.EditProjectSSO,
],
})
@TableColumn({
required: false,
type: TableColumnType.LongText,
title: "Description",
description: "Friendly description to help you remember",
})
@Column({
nullable: true,
type: ColumnType.LongText,
length: ColumnLength.LongText,
})
public description?: string = undefined;
@ColumnAccessControl({
create: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.CreateProjectSSO,
],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ReadProjectSSO,
],
update: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.EditProjectSSO,
],
})
@TableColumn({
required: true,
type: TableColumnType.LongText,
title: "Bearer Token",
description: "Bearer token for SCIM authentication. Keep this secure.",
})
@Column({
nullable: false,
type: ColumnType.LongText,
length: ColumnLength.LongText,
})
public bearerToken?: string = undefined;
@ColumnAccessControl({
create: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.CreateProjectSSO,
],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadProjectSSO,
],
update: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.EditProjectSSO,
],
})
@TableColumn({
required: false,
type: TableColumnType.EntityArray,
modelType: Team,
title: "Default Teams",
description: "Default teams that new users will be added to via SCIM",
})
@ManyToMany(
() => {
return Team;
},
{ eager: false },
)
@JoinTable({
name: "ProjectScimTeam",
inverseJoinColumn: {
name: "teamId",
referencedColumnName: "_id",
},
joinColumn: {
name: "projectScimId",
referencedColumnName: "_id",
},
})
public teams?: Array<Team> = undefined;
@ColumnAccessControl({
create: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.CreateProjectSSO,
],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadProjectSSO,
],
update: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.EditProjectSSO,
],
})
@TableColumn({
isDefaultValueColumn: true,
type: TableColumnType.Boolean,
title: "Auto Provision Users",
description: "Automatically create users when they are added via SCIM",
defaultValue: true,
})
@Column({
type: ColumnType.Boolean,
default: true,
})
public autoProvisionUsers?: boolean = undefined;
@ColumnAccessControl({
create: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.CreateProjectSSO,
],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadProjectSSO,
],
update: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.EditProjectSSO,
],
})
@TableColumn({
isDefaultValueColumn: true,
type: TableColumnType.Boolean,
title: "Auto Deprovision Users",
description: "Automatically remove users when they are removed via SCIM",
defaultValue: true,
})
@Column({
type: ColumnType.Boolean,
default: true,
})
public autoDeprovisionUsers?: boolean = undefined;
@ColumnAccessControl({
create: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.CreateProjectSSO,
],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadProjectSSO,
],
update: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.EditProjectSSO,
],
})
@TableColumn({
isDefaultValueColumn: true,
type: TableColumnType.Boolean,
title: "Enable Push Groups",
description: "Enable push groups provisioning instead of default teams",
defaultValue: false,
})
@Column({
type: ColumnType.Boolean,
default: false,
})
public enablePushGroups?: boolean = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadProjectSSO,
],
update: [],
})
@TableColumn({
manyToOneRelationColumn: "createdByUserId",
type: TableColumnType.Entity,
modelType: User,
title: "Created by User",
description:
"Relation to User who created this object (if this object was created by a User)",
})
@ManyToOne(
() => {
return User;
},
{
eager: false,
nullable: true,
onDelete: "SET NULL",
orphanedRowAction: "nullify",
},
)
@JoinColumn({ name: "createdByUserId" })
public createdByUser?: User = undefined;
@ColumnAccessControl({
create: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.CreateProjectSSO,
],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadProjectSSO,
],
update: [],
})
@TableColumn({
type: TableColumnType.ObjectID,
title: "Created by User ID",
description:
"User ID who created this object (if this object was created by a User)",
})
@Column({
type: ColumnType.ObjectID,
nullable: true,
transformer: ObjectID.getDatabaseTransformer(),
})
public createdByUserId?: ObjectID = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadProjectSSO,
],
update: [],
})
@TableColumn({
manyToOneRelationColumn: "deletedByUserId",
type: TableColumnType.Entity,
modelType: User,
title: "Deleted by User",
description:
"Relation to User who deleted this object (if this object was deleted by a User)",
})
@ManyToOne(
() => {
return User;
},
{
cascade: false,
eager: false,
nullable: true,
onDelete: "SET NULL",
orphanedRowAction: "nullify",
},
)
@JoinColumn({ name: "deletedByUserId" })
public deletedByUser?: User = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadProjectSSO,
],
update: [],
})
@TableColumn({
type: TableColumnType.ObjectID,
title: "Deleted by User ID",
description:
"User ID who deleted this object (if this object was deleted by a User)",
})
@Column({
type: ColumnType.ObjectID,
nullable: true,
transformer: ObjectID.getDatabaseTransformer(),
})
public deletedByUserId?: ObjectID = undefined;
}

View File

@@ -336,8 +336,10 @@ export default class ProjectSmtpConfig extends BaseModel {
})
public deletedByUserId?: ObjectID = undefined;
// This is not required because some SMTP servers do not require authentication.
// eg: https://learn.microsoft.com/en-us/exchange/mail-flow-best-practices/how-to-set-up-a-multifunction-device-or-application-to-send-email-using-microsoft-365-or-office-365#option-2-send-mail-directly-from-your-printer-or-application-to-microsoft-365-or-office-365-direct-send
/*
* This is not required because some SMTP servers do not require authentication.
* eg: https://learn.microsoft.com/en-us/exchange/mail-flow-best-practices/how-to-set-up-a-multifunction-device-or-application-to-send-email-using-microsoft-365-or-office-365#option-2-send-mail-directly-from-your-printer-or-application-to-microsoft-365-or-office-365-direct-send
*/
@ColumnAccessControl({
create: [
Permission.ProjectOwner,

View File

@@ -578,7 +578,11 @@ export default class ProjectSSO extends BaseModel {
],
update: [],
})
@TableColumn({ isDefaultValueColumn: true, type: TableColumnType.Boolean })
@TableColumn({
isDefaultValueColumn: true,
type: TableColumnType.Boolean,
defaultValue: false,
})
@Column({
type: ColumnType.Boolean,
default: false,

View File

@@ -0,0 +1,877 @@
import Project from "./Project";
import Incident from "./Incident";
import Alert from "./Alert";
import ScheduledMaintenance from "./ScheduledMaintenance";
import StatusPage from "./StatusPage";
import StatusPageAnnouncement from "./StatusPageAnnouncement";
import User from "./User";
import OnCallDutyPolicy from "./OnCallDutyPolicy";
import OnCallDutyPolicyEscalationRule from "./OnCallDutyPolicyEscalationRule";
import OnCallDutyPolicySchedule from "./OnCallDutyPolicySchedule";
import Team from "./Team";
import BaseModel from "./DatabaseBaseModel/DatabaseBaseModel";
import Route from "../../Types/API/Route";
import ColumnAccessControl from "../../Types/Database/AccessControl/ColumnAccessControl";
import TableAccessControl from "../../Types/Database/AccessControl/TableAccessControl";
import ColumnLength from "../../Types/Database/ColumnLength";
import ColumnType from "../../Types/Database/ColumnType";
import CrudApiEndpoint from "../../Types/Database/CrudApiEndpoint";
import EnableDocumentation from "../../Types/Database/EnableDocumentation";
import EnableWorkflow from "../../Types/Database/EnableWorkflow";
import TableColumn from "../../Types/Database/TableColumn";
import TableColumnType from "../../Types/Database/TableColumnType";
import TableMetadata from "../../Types/Database/TableMetadata";
import TenantColumn from "../../Types/Database/TenantColumn";
import IconProp from "../../Types/Icon/IconProp";
import ObjectID from "../../Types/ObjectID";
import Permission from "../../Types/Permission";
import PushStatus from "../../Types/PushNotification/PushStatus";
import { Column, Entity, Index, JoinColumn, ManyToOne } from "typeorm";
@EnableDocumentation()
@TenantColumn("projectId")
@TableAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadPushLog,
],
delete: [],
update: [],
})
@CrudApiEndpoint(new Route("/push-notification-log"))
@Entity({
name: "PushNotificationLog",
})
@EnableWorkflow({
create: true,
delete: false,
update: false,
})
@TableMetadata({
tableName: "PushNotificationLog",
singularName: "Push Notification Log",
pluralName: "Push Notification Logs",
icon: IconProp.Bell,
tableDescription:
"Logs of all the Push Notifications sent out to all users and subscribers for this project.",
})
export default class PushNotificationLog extends BaseModel {
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadPushLog,
],
update: [],
})
@TableColumn({
manyToOneRelationColumn: "projectId",
type: TableColumnType.Entity,
modelType: Project,
title: "Project",
description: "Relation to Project Resource in which this object belongs",
})
@ManyToOne(
() => {
return Project;
},
{
eager: false,
nullable: true,
onDelete: "CASCADE",
orphanedRowAction: "nullify",
},
)
@JoinColumn({ name: "projectId" })
public project?: Project = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadPushLog,
],
update: [],
})
@Index()
@TableColumn({
type: TableColumnType.ObjectID,
required: true,
canReadOnRelationQuery: true,
title: "Project ID",
description: "ID of your OneUptime Project in which this object belongs",
})
@Column({
type: ColumnType.ObjectID,
nullable: false,
transformer: ObjectID.getDatabaseTransformer(),
})
public projectId?: ObjectID = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadPushLog,
],
update: [],
})
@TableColumn({
required: true,
type: TableColumnType.LongText,
title: "Title",
description: "Title of the push notification",
canReadOnRelationQuery: false,
})
@Column({
nullable: false,
type: ColumnType.LongText,
length: ColumnLength.LongText,
})
public title?: string = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadPushLog,
],
update: [],
})
@TableColumn({
required: false,
type: TableColumnType.VeryLongText,
title: "Body",
description: "Body of the push notification",
canReadOnRelationQuery: false,
})
@Column({
nullable: true,
type: ColumnType.VeryLongText,
})
public body?: string = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadPushLog,
],
update: [],
})
@TableColumn({
required: false,
type: TableColumnType.ShortText,
title: "Device Type",
description: "Type of device this was sent to (e.g., web)",
canReadOnRelationQuery: false,
})
@Column({
nullable: true,
type: ColumnType.ShortText,
length: ColumnLength.ShortText,
})
public deviceType?: string = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadPushLog,
],
update: [],
})
@TableColumn({
required: false,
type: TableColumnType.ShortText,
title: "Device Name",
description: "Name of the device this was sent to",
canReadOnRelationQuery: false,
})
@Column({
nullable: true,
type: ColumnType.ShortText,
length: ColumnLength.ShortText,
})
public deviceName?: string = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadPushLog,
],
update: [],
})
@TableColumn({
required: false,
type: TableColumnType.LongText,
title: "Status Message",
description: "Status Message (if any)",
canReadOnRelationQuery: false,
})
@Column({
nullable: true,
type: ColumnType.LongText,
length: ColumnLength.LongText,
})
public statusMessage?: string = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadPushLog,
],
update: [],
})
@TableColumn({
required: true,
type: TableColumnType.ShortText,
title: "Status",
description: "Status of the push notification",
canReadOnRelationQuery: false,
})
@Column({
nullable: false,
type: ColumnType.ShortText,
length: ColumnLength.ShortText,
})
public status?: PushStatus = undefined;
// Relations to resources that triggered this push notification (nullable)
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadPushLog,
],
update: [],
})
@TableColumn({
manyToOneRelationColumn: "incidentId",
type: TableColumnType.Entity,
modelType: Incident,
title: "Incident",
description: "Incident associated with this Push (if any)",
})
@ManyToOne(
() => {
return Incident;
},
{
eager: false,
nullable: true,
onDelete: "CASCADE",
orphanedRowAction: "nullify",
},
)
@JoinColumn({ name: "incidentId" })
public incident?: Incident = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadPushLog,
],
update: [],
})
@Index()
@TableColumn({
type: TableColumnType.ObjectID,
required: false,
canReadOnRelationQuery: true,
title: "Incident ID",
description: "ID of Incident associated with this Push (if any)",
})
@Column({
type: ColumnType.ObjectID,
nullable: true,
transformer: ObjectID.getDatabaseTransformer(),
})
public incidentId?: ObjectID = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadPushLog,
],
update: [],
})
@TableColumn({
manyToOneRelationColumn: "userId",
type: TableColumnType.Entity,
modelType: User,
title: "User",
description: "User who initiated this Push notification (if any)",
})
@ManyToOne(
() => {
return User;
},
{
eager: false,
nullable: true,
onDelete: "CASCADE",
orphanedRowAction: "nullify",
},
)
@JoinColumn({ name: "userId" })
public user?: User = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadPushLog,
],
update: [],
})
@Index()
@TableColumn({
type: TableColumnType.ObjectID,
required: false,
canReadOnRelationQuery: true,
title: "User ID",
description: "ID of User who initiated this Push notification (if any)",
})
@Column({
type: ColumnType.ObjectID,
nullable: true,
transformer: ObjectID.getDatabaseTransformer(),
})
public userId?: ObjectID = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadPushLog,
],
update: [],
})
@TableColumn({
manyToOneRelationColumn: "alertId",
type: TableColumnType.Entity,
modelType: Alert,
title: "Alert",
description: "Alert associated with this Push (if any)",
})
@ManyToOne(
() => {
return Alert;
},
{
eager: false,
nullable: true,
onDelete: "CASCADE",
orphanedRowAction: "nullify",
},
)
@JoinColumn({ name: "alertId" })
public alert?: Alert = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadPushLog,
],
update: [],
})
@Index()
@TableColumn({
type: TableColumnType.ObjectID,
required: false,
canReadOnRelationQuery: true,
title: "Alert ID",
description: "ID of Alert associated with this Push (if any)",
})
@Column({
type: ColumnType.ObjectID,
nullable: true,
transformer: ObjectID.getDatabaseTransformer(),
})
public alertId?: ObjectID = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadPushLog,
],
update: [],
})
@TableColumn({
manyToOneRelationColumn: "scheduledMaintenanceId",
type: TableColumnType.Entity,
modelType: ScheduledMaintenance,
title: "Scheduled Maintenance",
description: "Scheduled Maintenance associated with this Push (if any)",
})
@ManyToOne(
() => {
return ScheduledMaintenance;
},
{
eager: false,
nullable: true,
onDelete: "CASCADE",
orphanedRowAction: "nullify",
},
)
@JoinColumn({ name: "scheduledMaintenanceId" })
public scheduledMaintenance?: ScheduledMaintenance = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadPushLog,
],
update: [],
})
@Index()
@TableColumn({
type: TableColumnType.ObjectID,
required: false,
canReadOnRelationQuery: true,
title: "Scheduled Maintenance ID",
description:
"ID of Scheduled Maintenance associated with this Push (if any)",
})
@Column({
type: ColumnType.ObjectID,
nullable: true,
transformer: ObjectID.getDatabaseTransformer(),
})
public scheduledMaintenanceId?: ObjectID = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadPushLog,
],
update: [],
})
@TableColumn({
manyToOneRelationColumn: "statusPageId",
type: TableColumnType.Entity,
modelType: StatusPage,
title: "Status Page",
description: "Status Page associated with this Push (if any)",
})
@ManyToOne(
() => {
return StatusPage;
},
{
eager: false,
nullable: true,
onDelete: "CASCADE",
orphanedRowAction: "nullify",
},
)
@JoinColumn({ name: "statusPageId" })
public statusPage?: StatusPage = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadPushLog,
],
update: [],
})
@Index()
@TableColumn({
type: TableColumnType.ObjectID,
required: false,
canReadOnRelationQuery: true,
title: "Status Page ID",
description: "ID of Status Page associated with this Push (if any)",
})
@Column({
type: ColumnType.ObjectID,
nullable: true,
transformer: ObjectID.getDatabaseTransformer(),
})
public statusPageId?: ObjectID = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadPushLog,
],
update: [],
})
@TableColumn({
manyToOneRelationColumn: "statusPageAnnouncementId",
type: TableColumnType.Entity,
modelType: StatusPageAnnouncement,
title: "Status Page Announcement",
description: "Status Page Announcement associated with this Push (if any)",
})
@ManyToOne(
() => {
return StatusPageAnnouncement;
},
{
eager: false,
nullable: true,
onDelete: "CASCADE",
orphanedRowAction: "nullify",
},
)
@JoinColumn({ name: "statusPageAnnouncementId" })
public statusPageAnnouncement?: StatusPageAnnouncement = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadPushLog,
],
update: [],
})
@Index()
@TableColumn({
type: TableColumnType.ObjectID,
required: false,
canReadOnRelationQuery: true,
title: "Status Page Announcement ID",
description:
"ID of Status Page Announcement associated with this Push (if any)",
})
@Column({
type: ColumnType.ObjectID,
nullable: true,
transformer: ObjectID.getDatabaseTransformer(),
})
public statusPageAnnouncementId?: ObjectID = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadPushLog,
],
update: [],
})
@TableColumn({
manyToOneRelationColumn: "onCallDutyPolicyId",
type: TableColumnType.Entity,
modelType: OnCallDutyPolicy,
title: "On-Call Duty Policy",
description:
"On-Call Duty Policy associated with this Push Notification (if any)",
})
@ManyToOne(
() => {
return OnCallDutyPolicy;
},
{
eager: false,
nullable: true,
onDelete: "CASCADE",
orphanedRowAction: "nullify",
},
)
@JoinColumn({ name: "onCallDutyPolicyId" })
public onCallDutyPolicy?: OnCallDutyPolicy = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadPushLog,
],
update: [],
})
@Index()
@TableColumn({
type: TableColumnType.ObjectID,
required: false,
canReadOnRelationQuery: true,
title: "On-Call Duty Policy ID",
description:
"ID of On-Call Duty Policy associated with this Push Notification (if any)",
})
@Column({
type: ColumnType.ObjectID,
nullable: true,
transformer: ObjectID.getDatabaseTransformer(),
})
public onCallDutyPolicyId?: ObjectID = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadPushLog,
],
update: [],
})
@TableColumn({
manyToOneRelationColumn: "onCallDutyPolicyEscalationRuleId",
type: TableColumnType.Entity,
modelType: OnCallDutyPolicyEscalationRule,
title: "On-Call Duty Policy Escalation Rule",
description:
"On-Call Duty Policy Escalation Rule associated with this Push Notification (if any)",
})
@ManyToOne(
() => {
return OnCallDutyPolicyEscalationRule;
},
{
eager: false,
nullable: true,
onDelete: "CASCADE",
orphanedRowAction: "nullify",
},
)
@JoinColumn({ name: "onCallDutyPolicyEscalationRuleId" })
public onCallDutyPolicyEscalationRule?: OnCallDutyPolicyEscalationRule =
undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadPushLog,
],
update: [],
})
@Index()
@TableColumn({
type: TableColumnType.ObjectID,
required: false,
canReadOnRelationQuery: true,
title: "On-Call Duty Policy Escalation Rule ID",
description:
"ID of On-Call Duty Policy Escalation Rule associated with this Push Notification (if any)",
})
@Column({
type: ColumnType.ObjectID,
nullable: true,
transformer: ObjectID.getDatabaseTransformer(),
})
public onCallDutyPolicyEscalationRuleId?: ObjectID = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadPushLog,
],
update: [],
})
@TableColumn({
manyToOneRelationColumn: "onCallDutyPolicyScheduleId",
type: TableColumnType.Entity,
modelType: OnCallDutyPolicySchedule,
title: "On-Call Duty Policy Schedule",
description:
"On-Call Duty Policy Schedule associated with this Push Notification (if any)",
})
@ManyToOne(
() => {
return OnCallDutyPolicySchedule;
},
{
eager: false,
nullable: true,
onDelete: "CASCADE",
orphanedRowAction: "nullify",
},
)
@JoinColumn({ name: "onCallDutyPolicyScheduleId" })
public onCallDutyPolicySchedule?: OnCallDutyPolicySchedule = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadPushLog,
],
update: [],
})
@Index()
@TableColumn({
type: TableColumnType.ObjectID,
required: false,
canReadOnRelationQuery: true,
title: "On-Call Duty Policy Schedule ID",
description:
"ID of On-Call Duty Policy Schedule associated with this Push Notification (if any)",
})
@Column({
type: ColumnType.ObjectID,
nullable: true,
transformer: ObjectID.getDatabaseTransformer(),
})
public onCallDutyPolicyScheduleId?: ObjectID = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadPushLog,
],
update: [],
})
@TableColumn({
manyToOneRelationColumn: "teamId",
type: TableColumnType.Entity,
modelType: Team,
title: "Team",
description: "Team associated with this Push Notification (if any)",
})
@ManyToOne(
() => {
return Team;
},
{
eager: false,
nullable: true,
onDelete: "CASCADE",
orphanedRowAction: "nullify",
},
)
@JoinColumn({ name: "teamId" })
public team?: Team = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadPushLog,
],
update: [],
})
@Index()
@TableColumn({
type: TableColumnType.ObjectID,
required: false,
canReadOnRelationQuery: true,
title: "Team ID",
description: "ID of Team associated with this Push Notification (if any)",
})
@Column({
type: ColumnType.ObjectID,
nullable: true,
transformer: ObjectID.getDatabaseTransformer(),
})
public teamId?: ObjectID = undefined;
@ColumnAccessControl({
create: [],
read: [],
update: [],
})
@TableColumn({
manyToOneRelationColumn: "deletedByUserId",
type: TableColumnType.Entity,
title: "Deleted by User",
modelType: User,
description:
"Relation to User who deleted this object (if this object was deleted by a User)",
})
@ManyToOne(
() => {
return User;
},
{
cascade: false,
eager: false,
nullable: true,
onDelete: "SET NULL",
orphanedRowAction: "nullify",
},
)
@JoinColumn({ name: "deletedByUserId" })
public deletedByUser?: User = undefined;
@ColumnAccessControl({
create: [],
read: [],
update: [],
})
@TableColumn({
type: TableColumnType.ObjectID,
title: "Deleted by User ID",
description:
"User ID who deleted this object (if this object was deleted by a User)",
})
@Column({
type: ColumnType.ObjectID,
nullable: true,
transformer: ObjectID.getDatabaseTransformer(),
})
public deletedByUserId?: ObjectID = undefined;
}

View File

@@ -269,6 +269,7 @@ export default class Reseller extends BaseModel {
canReadOnRelationQuery: true,
title: "Enable Telemetry Features",
description: "Should we enable telemetry features for this reseller?",
defaultValue: false,
})
@Column({
nullable: true,

View File

@@ -25,6 +25,7 @@ import IconProp from "../../Types/Icon/IconProp";
import { JSONObject } from "../../Types/JSON";
import ObjectID from "../../Types/ObjectID";
import Permission from "../../Types/Permission";
import StatusPageSubscriberNotificationStatus from "../../Types/StatusPage/StatusPageSubscriberNotificationStatus";
import {
Column,
Entity,
@@ -715,29 +716,74 @@ export default class ScheduledMaintenance extends BaseModel {
public endsAt?: Date = undefined;
@ColumnAccessControl({
create: [],
create: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.CreateProjectScheduledMaintenance,
],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadProjectScheduledMaintenance,
],
update: [],
update: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.EditProjectScheduledMaintenance,
],
})
@TableColumn({
isDefaultValueColumn: true,
computed: true,
hideColumnInDocumentation: true,
type: TableColumnType.Boolean,
title: "Status Page Subscribers Notified On Event Scheduled",
description: "Status Page Subscribers Notified On Event Scheduled",
defaultValue: false,
type: TableColumnType.ShortText,
title: "Subscriber Notification Status On Event Scheduled",
description:
"Status of notification sent to subscribers when event was scheduled",
defaultValue: StatusPageSubscriberNotificationStatus.Pending,
})
@Column({
type: ColumnType.Boolean,
default: false,
type: ColumnType.ShortText,
default: StatusPageSubscriberNotificationStatus.Pending,
})
public isStatusPageSubscribersNotifiedOnEventScheduled?: boolean = undefined;
public subscriberNotificationStatusOnEventScheduled?: StatusPageSubscriberNotificationStatus =
undefined;
@ColumnAccessControl({
create: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.CreateIncidentPublicNote,
],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadProjectScheduledMaintenance,
],
update: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.EditProjectScheduledMaintenance,
],
})
@TableColumn({
type: TableColumnType.VeryLongText,
title: "Notification Status Message On Event Scheduled",
description:
"Status message for subscriber notifications when event is scheduled - includes success messages, failure reasons, or skip reasons",
required: false,
})
@Column({
type: ColumnType.VeryLongText,
nullable: true,
})
public subscriberNotificationStatusMessage?: string = undefined;
@ColumnAccessControl({
create: [

View File

@@ -17,6 +17,7 @@ import TenantColumn from "../../Types/Database/TenantColumn";
import IconProp from "../../Types/Icon/IconProp";
import ObjectID from "../../Types/ObjectID";
import Permission from "../../Types/Permission";
import StatusPageSubscriberNotificationStatus from "../../Types/StatusPage/StatusPageSubscriberNotificationStatus";
import { Column, Entity, Index, JoinColumn, ManyToOne } from "typeorm";
@EnableDocumentation()
@@ -342,29 +343,73 @@ export default class ScheduledMaintenancePublicNote extends BaseModel {
public note?: string = undefined;
@ColumnAccessControl({
create: [],
create: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.CreateScheduledMaintenancePublicNote,
],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadScheduledMaintenancePublicNote,
],
update: [],
update: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.EditScheduledMaintenancePublicNote,
],
})
@TableColumn({
isDefaultValueColumn: true,
computed: true,
hideColumnInDocumentation: true,
type: TableColumnType.Boolean,
title: "Are subscribers notified?",
description: "Are subscribers notified about this note?",
defaultValue: false,
type: TableColumnType.ShortText,
title: "Subscriber Notification Status",
description: "Status of notification sent to subscribers about this note",
defaultValue: StatusPageSubscriberNotificationStatus.Pending,
})
@Column({
type: ColumnType.Boolean,
default: false,
type: ColumnType.ShortText,
default: StatusPageSubscriberNotificationStatus.Pending,
})
public isStatusPageSubscribersNotifiedOnNoteCreated?: boolean = undefined;
public subscriberNotificationStatusOnNoteCreated?: StatusPageSubscriberNotificationStatus =
undefined;
@ColumnAccessControl({
create: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.CreateScheduledMaintenancePublicNote,
],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadScheduledMaintenancePublicNote,
],
update: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.EditScheduledMaintenancePublicNote,
],
})
@TableColumn({
type: TableColumnType.VeryLongText,
title: "Notification Status Message",
description:
"Status message for subscriber notifications - includes success messages, failure reasons, or skip reasons",
required: false,
})
@Column({
type: ColumnType.VeryLongText,
nullable: true,
})
public subscriberNotificationStatusMessage?: string = undefined;
@ColumnAccessControl({
create: [

View File

@@ -423,6 +423,7 @@ export default class ScheduledMaintenanceState extends BaseModel {
canReadOnRelationQuery: true,
title: "Scheduled State",
description: "Is this state a scheduled state?",
defaultValue: false,
})
@Column({
type: ColumnType.Boolean,
@@ -456,6 +457,7 @@ export default class ScheduledMaintenanceState extends BaseModel {
canReadOnRelationQuery: true,
title: "Ongoing State",
description: "Is this state a ongoing state?",
defaultValue: false,
})
@Column({
type: ColumnType.Boolean,
@@ -489,6 +491,7 @@ export default class ScheduledMaintenanceState extends BaseModel {
canReadOnRelationQuery: true,
title: "Ended State",
description: "Is this state a ended state?",
defaultValue: false,
})
@Column({
type: ColumnType.Boolean,
@@ -522,6 +525,7 @@ export default class ScheduledMaintenanceState extends BaseModel {
canReadOnRelationQuery: true,
title: "Resolved State",
description: "Is this state a resolved state?",
defaultValue: false,
})
@Column({
type: ColumnType.Boolean,

View File

@@ -18,6 +18,7 @@ import TenantColumn from "../../Types/Database/TenantColumn";
import IconProp from "../../Types/Icon/IconProp";
import ObjectID from "../../Types/ObjectID";
import Permission from "../../Types/Permission";
import StatusPageSubscriberNotificationStatus from "../../Types/StatusPage/StatusPageSubscriberNotificationStatus";
import { Column, Entity, Index, JoinColumn, ManyToOne } from "typeorm";
@EnableDocumentation()
@@ -388,29 +389,74 @@ export default class ScheduledMaintenanceStateTimeline extends BaseModel {
public scheduledMaintenanceStateId?: ObjectID = undefined;
@ColumnAccessControl({
create: [],
create: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.CreateScheduledMaintenanceStateTimeline,
],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadScheduledMaintenanceStateTimeline,
],
update: [],
update: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.EditScheduledMaintenanceStateTimeline,
],
})
@TableColumn({
isDefaultValueColumn: true,
computed: true,
hideColumnInDocumentation: true,
type: TableColumnType.Boolean,
title: "Are subscribers notified?",
description: "Are subscribers notified about this incident state change?",
defaultValue: false,
type: TableColumnType.ShortText,
title: "Subscriber Notification Status",
description:
"Status of notification sent to subscribers about this scheduled maintenance state change",
defaultValue: StatusPageSubscriberNotificationStatus.Pending,
})
@Column({
type: ColumnType.Boolean,
default: false,
type: ColumnType.ShortText,
default: StatusPageSubscriberNotificationStatus.Pending,
})
public isStatusPageSubscribersNotified?: boolean = undefined;
public subscriberNotificationStatus?: StatusPageSubscriberNotificationStatus =
undefined;
@ColumnAccessControl({
create: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.CreateScheduledMaintenanceStateTimeline,
],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadScheduledMaintenanceStateTimeline,
],
update: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.EditScheduledMaintenanceStateTimeline,
],
})
@TableColumn({
type: TableColumnType.VeryLongText,
title: "Notification Status Message",
description:
"Status message for subscriber notifications - includes success messages, failure reasons, or skip reasons",
required: false,
})
@Column({
type: ColumnType.VeryLongText,
nullable: true,
})
public subscriberNotificationStatusMessage?: string = undefined;
@ColumnAccessControl({
create: [

View File

@@ -1,5 +1,14 @@
import Project from "./Project";
import Incident from "./Incident";
import Alert from "./Alert";
import ScheduledMaintenance from "./ScheduledMaintenance";
import StatusPage from "./StatusPage";
import StatusPageAnnouncement from "./StatusPageAnnouncement";
import User from "./User";
import OnCallDutyPolicy from "./OnCallDutyPolicy";
import OnCallDutyPolicyEscalationRule from "./OnCallDutyPolicyEscalationRule";
import OnCallDutyPolicySchedule from "./OnCallDutyPolicySchedule";
import Team from "./Team";
import BaseModel from "./DatabaseBaseModel/DatabaseBaseModel";
import Route from "../../Types/API/Route";
import ColumnAccessControl from "../../Types/Database/AccessControl/ColumnAccessControl";
@@ -256,6 +265,575 @@ export default class SmsLog extends BaseModel {
})
public smsCostInUSDCents?: number = undefined;
// Relations to resources that triggered this SMS (nullable)
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadSmsLog,
],
update: [],
})
@TableColumn({
manyToOneRelationColumn: "incidentId",
type: TableColumnType.Entity,
modelType: Incident,
title: "Incident",
description: "Incident associated with this SMS (if any)",
})
@ManyToOne(
() => {
return Incident;
},
{
eager: false,
nullable: true,
onDelete: "CASCADE",
orphanedRowAction: "nullify",
},
)
@JoinColumn({ name: "incidentId" })
public incident?: Incident = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadSmsLog,
],
update: [],
})
@Index()
@TableColumn({
type: TableColumnType.ObjectID,
required: false,
canReadOnRelationQuery: true,
title: "Incident ID",
description: "ID of Incident associated with this SMS (if any)",
})
@Column({
type: ColumnType.ObjectID,
nullable: true,
transformer: ObjectID.getDatabaseTransformer(),
})
public incidentId?: ObjectID = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadSmsLog,
],
update: [],
})
@TableColumn({
manyToOneRelationColumn: "userId",
type: TableColumnType.Entity,
modelType: User,
title: "User",
description: "User who initiated this SMS (if any)",
})
@ManyToOne(
() => {
return User;
},
{
eager: false,
nullable: true,
onDelete: "CASCADE",
orphanedRowAction: "nullify",
},
)
@JoinColumn({ name: "userId" })
public user?: User = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadSmsLog,
],
update: [],
})
@Index()
@TableColumn({
type: TableColumnType.ObjectID,
required: false,
canReadOnRelationQuery: true,
title: "User ID",
description: "ID of User who initiated this SMS (if any)",
})
@Column({
type: ColumnType.ObjectID,
nullable: true,
transformer: ObjectID.getDatabaseTransformer(),
})
public userId?: ObjectID = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadSmsLog,
],
update: [],
})
@TableColumn({
manyToOneRelationColumn: "alertId",
type: TableColumnType.Entity,
modelType: Alert,
title: "Alert",
description: "Alert associated with this SMS (if any)",
})
@ManyToOne(
() => {
return Alert;
},
{
eager: false,
nullable: true,
onDelete: "CASCADE",
orphanedRowAction: "nullify",
},
)
@JoinColumn({ name: "alertId" })
public alert?: Alert = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadSmsLog,
],
update: [],
})
@Index()
@TableColumn({
type: TableColumnType.ObjectID,
required: false,
canReadOnRelationQuery: true,
title: "Alert ID",
description: "ID of Alert associated with this SMS (if any)",
})
@Column({
type: ColumnType.ObjectID,
nullable: true,
transformer: ObjectID.getDatabaseTransformer(),
})
public alertId?: ObjectID = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadSmsLog,
],
update: [],
})
@TableColumn({
manyToOneRelationColumn: "scheduledMaintenanceId",
type: TableColumnType.Entity,
modelType: ScheduledMaintenance,
title: "Scheduled Maintenance",
description: "Scheduled Maintenance associated with this SMS (if any)",
})
@ManyToOne(
() => {
return ScheduledMaintenance;
},
{
eager: false,
nullable: true,
onDelete: "CASCADE",
orphanedRowAction: "nullify",
},
)
@JoinColumn({ name: "scheduledMaintenanceId" })
public scheduledMaintenance?: ScheduledMaintenance = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadSmsLog,
],
update: [],
})
@Index()
@TableColumn({
type: TableColumnType.ObjectID,
required: false,
canReadOnRelationQuery: true,
title: "Scheduled Maintenance ID",
description:
"ID of Scheduled Maintenance associated with this SMS (if any)",
})
@Column({
type: ColumnType.ObjectID,
nullable: true,
transformer: ObjectID.getDatabaseTransformer(),
})
public scheduledMaintenanceId?: ObjectID = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadSmsLog,
],
update: [],
})
@TableColumn({
manyToOneRelationColumn: "statusPageId",
type: TableColumnType.Entity,
modelType: StatusPage,
title: "Status Page",
description: "Status Page associated with this SMS (if any)",
})
@ManyToOne(
() => {
return StatusPage;
},
{
eager: false,
nullable: true,
onDelete: "CASCADE",
orphanedRowAction: "nullify",
},
)
@JoinColumn({ name: "statusPageId" })
public statusPage?: StatusPage = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadSmsLog,
],
update: [],
})
@Index()
@TableColumn({
type: TableColumnType.ObjectID,
required: false,
canReadOnRelationQuery: true,
title: "Status Page ID",
description: "ID of Status Page associated with this SMS (if any)",
})
@Column({
type: ColumnType.ObjectID,
nullable: true,
transformer: ObjectID.getDatabaseTransformer(),
})
public statusPageId?: ObjectID = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadSmsLog,
],
update: [],
})
@TableColumn({
manyToOneRelationColumn: "statusPageAnnouncementId",
type: TableColumnType.Entity,
modelType: StatusPageAnnouncement,
title: "Status Page Announcement",
description: "Status Page Announcement associated with this SMS (if any)",
})
@ManyToOne(
() => {
return StatusPageAnnouncement;
},
{
eager: false,
nullable: true,
onDelete: "CASCADE",
orphanedRowAction: "nullify",
},
)
@JoinColumn({ name: "statusPageAnnouncementId" })
public statusPageAnnouncement?: StatusPageAnnouncement = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadSmsLog,
],
update: [],
})
@Index()
@TableColumn({
type: TableColumnType.ObjectID,
required: false,
canReadOnRelationQuery: true,
title: "Status Page Announcement ID",
description:
"ID of Status Page Announcement associated with this SMS (if any)",
})
@Column({
type: ColumnType.ObjectID,
nullable: true,
transformer: ObjectID.getDatabaseTransformer(),
})
public statusPageAnnouncementId?: ObjectID = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadSmsLog,
],
update: [],
})
@TableColumn({
manyToOneRelationColumn: "onCallDutyPolicyId",
type: TableColumnType.Entity,
modelType: OnCallDutyPolicy,
title: "On-Call Duty Policy",
description: "On-Call Duty Policy associated with this SMS (if any)",
})
@ManyToOne(
() => {
return OnCallDutyPolicy;
},
{
eager: false,
nullable: true,
onDelete: "CASCADE",
orphanedRowAction: "nullify",
},
)
@JoinColumn({ name: "onCallDutyPolicyId" })
public onCallDutyPolicy?: OnCallDutyPolicy = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadSmsLog,
],
update: [],
})
@Index()
@TableColumn({
type: TableColumnType.ObjectID,
required: false,
canReadOnRelationQuery: true,
title: "On-Call Duty Policy ID",
description: "ID of On-Call Duty Policy associated with this SMS (if any)",
})
@Column({
type: ColumnType.ObjectID,
nullable: true,
transformer: ObjectID.getDatabaseTransformer(),
})
public onCallDutyPolicyId?: ObjectID = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadSmsLog,
],
update: [],
})
@TableColumn({
manyToOneRelationColumn: "onCallDutyPolicyEscalationRuleId",
type: TableColumnType.Entity,
modelType: OnCallDutyPolicyEscalationRule,
title: "On-Call Duty Policy Escalation Rule",
description:
"On-Call Duty Policy Escalation Rule associated with this SMS (if any)",
})
@ManyToOne(
() => {
return OnCallDutyPolicyEscalationRule;
},
{
eager: false,
nullable: true,
onDelete: "CASCADE",
orphanedRowAction: "nullify",
},
)
@JoinColumn({ name: "onCallDutyPolicyEscalationRuleId" })
public onCallDutyPolicyEscalationRule?: OnCallDutyPolicyEscalationRule =
undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadSmsLog,
],
update: [],
})
@Index()
@TableColumn({
type: TableColumnType.ObjectID,
required: false,
canReadOnRelationQuery: true,
title: "On-Call Duty Policy Escalation Rule ID",
description:
"ID of On-Call Duty Policy Escalation Rule associated with this SMS (if any)",
})
@Column({
type: ColumnType.ObjectID,
nullable: true,
transformer: ObjectID.getDatabaseTransformer(),
})
public onCallDutyPolicyEscalationRuleId?: ObjectID = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadSmsLog,
],
update: [],
})
@TableColumn({
manyToOneRelationColumn: "onCallDutyPolicyScheduleId",
type: TableColumnType.Entity,
modelType: OnCallDutyPolicySchedule,
title: "On-Call Duty Policy Schedule",
description:
"On-Call Duty Policy Schedule associated with this SMS (if any)",
})
@ManyToOne(
() => {
return OnCallDutyPolicySchedule;
},
{
eager: false,
nullable: true,
onDelete: "CASCADE",
orphanedRowAction: "nullify",
},
)
@JoinColumn({ name: "onCallDutyPolicyScheduleId" })
public onCallDutyPolicySchedule?: OnCallDutyPolicySchedule = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadSmsLog,
],
update: [],
})
@Index()
@TableColumn({
type: TableColumnType.ObjectID,
required: false,
canReadOnRelationQuery: true,
title: "On-Call Duty Policy Schedule ID",
description:
"ID of On-Call Duty Policy Schedule associated with this SMS (if any)",
})
@Column({
type: ColumnType.ObjectID,
nullable: true,
transformer: ObjectID.getDatabaseTransformer(),
})
public onCallDutyPolicyScheduleId?: ObjectID = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadSmsLog,
],
update: [],
})
@TableColumn({
manyToOneRelationColumn: "teamId",
type: TableColumnType.Entity,
modelType: Team,
title: "Team",
description: "Team associated with this SMS (if any)",
})
@ManyToOne(
() => {
return Team;
},
{
eager: false,
nullable: true,
onDelete: "CASCADE",
orphanedRowAction: "nullify",
},
)
@JoinColumn({ name: "teamId" })
public team?: Team = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadSmsLog,
],
update: [],
})
@Index()
@TableColumn({
type: TableColumnType.ObjectID,
required: false,
canReadOnRelationQuery: true,
title: "Team ID",
description: "ID of Team associated with this SMS (if any)",
})
@Column({
type: ColumnType.ObjectID,
nullable: true,
transformer: ObjectID.getDatabaseTransformer(),
})
public teamId?: ObjectID = undefined;
@ColumnAccessControl({
create: [],
read: [],

View File

@@ -1179,6 +1179,45 @@ export default class StatusPage extends BaseModel {
})
public enableSlackSubscribers?: boolean = undefined;
@ColumnAccessControl({
create: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.CreateProjectStatusPage,
],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadProjectStatusPage,
],
update: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.EditProjectStatusPage,
],
})
@TableColumn({
isDefaultValueColumn: true,
type: TableColumnType.Boolean,
title: "Enable Microsoft Teams Subscribers",
description:
"Can Microsoft Teams subscribers subscribe to this Status Page?",
defaultValue: false,
})
@Column({
type: ColumnType.Boolean,
default: false,
})
@ColumnBillingAccessControl({
read: PlanType.Free,
update: PlanType.Scale,
create: PlanType.Free,
})
public enableMicrosoftTeamsSubscribers?: boolean = undefined;
@ColumnAccessControl({
create: [
Permission.ProjectOwner,
@@ -1438,7 +1477,12 @@ export default class StatusPage extends BaseModel {
public callSmsConfigId?: ObjectID = undefined;
@ColumnAccessControl({
create: [],
create: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.CreateProjectStatusPage,
],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
@@ -1490,6 +1534,7 @@ export default class StatusPage extends BaseModel {
type: TableColumnType.Number,
required: true,
isDefaultValueColumn: true,
defaultValue: 14,
title: "Show incident history in days",
description:
"How many days of incident history should be shown on the status page (in days)?",
@@ -1526,6 +1571,7 @@ export default class StatusPage extends BaseModel {
type: TableColumnType.Number,
required: true,
isDefaultValueColumn: true,
defaultValue: 14,
title: "Show announcement history in days",
description:
"How many days of announcement history should be shown on the status page (in days)?",
@@ -1562,6 +1608,7 @@ export default class StatusPage extends BaseModel {
type: TableColumnType.Number,
required: true,
isDefaultValueColumn: true,
defaultValue: 14,
title: "Show scheduled event history in days",
description:
"How many days of scheduled event history should be shown on the status page (in days)?",
@@ -1633,6 +1680,7 @@ export default class StatusPage extends BaseModel {
type: TableColumnType.Boolean,
title: "Hide Powered By OneUptime Branding",
description: "Hide Powered By OneUptime Branding?",
defaultValue: false,
})
@Column({
type: ColumnType.Boolean,
@@ -1706,6 +1754,7 @@ export default class StatusPage extends BaseModel {
required: false,
type: TableColumnType.EntityArray,
modelType: MonitorStatus,
isDefaultValueColumn: true,
title: "Downtime Monitor Statuses",
description:
'List of monitors statuses that are considered as "down" for this status page.',
@@ -1788,6 +1837,7 @@ export default class StatusPage extends BaseModel {
type: TableColumnType.Boolean,
title: "Is Report Enabled",
description: "Is Report Enabled for this Status Page?",
defaultValue: false,
})
@Column({
type: ColumnType.Boolean,
@@ -1931,6 +1981,7 @@ export default class StatusPage extends BaseModel {
})
@TableColumn({
type: TableColumnType.Number,
defaultValue: 30,
title: "Report data for the last N days",
description: "How many days of data should be included in the report?",
})
@@ -1971,6 +2022,7 @@ export default class StatusPage extends BaseModel {
type: TableColumnType.Boolean,
title: "Show Overall Uptime Percent on Status Page",
description: "Show Overall Uptime Percent on Status Page?",
defaultValue: false,
})
@Column({
type: ColumnType.Boolean,
@@ -2007,6 +2059,7 @@ export default class StatusPage extends BaseModel {
type: TableColumnType.ShortText,
title: "Overall Uptime Percent Precision",
required: false,
defaultValue: UptimePrecision.TWO_DECIMAL,
description: "Overall Precision of uptime percent for this status page.",
})
@Column({
@@ -2109,6 +2162,7 @@ export default class StatusPage extends BaseModel {
type: TableColumnType.Boolean,
title: "Show Incidents on Status Page",
description: "Show Incidents on Status Page?",
defaultValue: true,
})
@Column({
type: ColumnType.Boolean,
@@ -2147,6 +2201,7 @@ export default class StatusPage extends BaseModel {
type: TableColumnType.Boolean,
title: "Show Announcements on Status Page",
description: "Show Announcements on Status Page?",
defaultValue: true,
})
@Column({
type: ColumnType.Boolean,
@@ -2185,6 +2240,7 @@ export default class StatusPage extends BaseModel {
type: TableColumnType.Boolean,
title: "Show Scheduled Maintenance Events on Status Page",
description: "Show Scheduled Maintenance Events on Status Page?",
defaultValue: true,
})
@Column({
type: ColumnType.Boolean,
@@ -2223,6 +2279,7 @@ export default class StatusPage extends BaseModel {
type: TableColumnType.Boolean,
title: "Show Subscriber Page on Status Page",
description: "Show Subscriber Page on Status Page?",
defaultValue: true,
})
@Column({
type: ColumnType.Boolean,

View File

@@ -1,3 +1,4 @@
import Monitor from "./Monitor";
import Project from "./Project";
import StatusPage from "./StatusPage";
import User from "./User";
@@ -21,6 +22,7 @@ import TenantColumn from "../../Types/Database/TenantColumn";
import IconProp from "../../Types/Icon/IconProp";
import ObjectID from "../../Types/ObjectID";
import Permission from "../../Types/Permission";
import StatusPageSubscriberNotificationStatus from "../../Types/StatusPage/StatusPageSubscriberNotificationStatus";
import {
Column,
Entity,
@@ -197,6 +199,53 @@ export default class StatusPageAnnouncement extends BaseModel {
})
public statusPages?: Array<StatusPage> = undefined;
@ColumnAccessControl({
create: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.CreateStatusPageAnnouncement,
],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadStatusPageAnnouncement,
],
update: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.EditStatusPageAnnouncement,
],
})
@TableColumn({
required: false,
type: TableColumnType.EntityArray,
modelType: Monitor,
title: "Monitors",
description:
"List of monitors affected by this announcement. If none are selected, all subscribers will be notified.",
})
@ManyToMany(
() => {
return Monitor;
},
{ eager: false },
)
@JoinTable({
name: "AnnouncementMonitor",
inverseJoinColumn: {
name: "monitorId",
referencedColumnName: "_id",
},
joinColumn: {
name: "announcementId",
referencedColumnName: "_id",
},
})
public monitors?: Array<Monitor> = undefined;
@ColumnAccessControl({
create: [
Permission.ProjectOwner,
@@ -443,27 +492,71 @@ export default class StatusPageAnnouncement extends BaseModel {
public deletedByUserId?: ObjectID = undefined;
@ColumnAccessControl({
create: [],
create: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.CreateStatusPageAnnouncement,
],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadStatusPageAnnouncement,
],
update: [],
update: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.EditStatusPageAnnouncement,
],
})
@TableColumn({
isDefaultValueColumn: true,
computed: true,
hideColumnInDocumentation: true,
type: TableColumnType.Boolean,
defaultValue: false,
type: TableColumnType.ShortText,
defaultValue: StatusPageSubscriberNotificationStatus.Pending,
})
@Column({
type: ColumnType.Boolean,
default: false,
type: ColumnType.ShortText,
default: StatusPageSubscriberNotificationStatus.Pending,
})
public isStatusPageSubscribersNotified?: boolean = undefined;
public subscriberNotificationStatus?: StatusPageSubscriberNotificationStatus =
undefined;
@ColumnAccessControl({
create: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.CreateStatusPageAnnouncement,
],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadStatusPageAnnouncement,
],
update: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.EditStatusPageAnnouncement,
],
})
@TableColumn({
type: TableColumnType.VeryLongText,
title: "Notification Status Message",
description:
"Status message for subscriber notifications - includes success messages, failure reasons, or skip reasons",
required: false,
})
@Column({
type: ColumnType.VeryLongText,
nullable: true,
})
public subscriberNotificationStatusMessage?: string = undefined;
@ColumnAccessControl({
create: [
@@ -485,6 +578,7 @@ export default class StatusPageAnnouncement extends BaseModel {
type: TableColumnType.Boolean,
title: "Should subscribers be notified?",
description: "Should subscribers be notified about this announcement?",
defaultValue: true,
})
@Column({
type: ColumnType.Boolean,
@@ -493,7 +587,12 @@ export default class StatusPageAnnouncement extends BaseModel {
public shouldStatusPageSubscribersBeNotified?: boolean = undefined;
@ColumnAccessControl({
create: [],
create: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.CreateStatusPageAnnouncement,
],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
@@ -511,6 +610,7 @@ export default class StatusPageAnnouncement extends BaseModel {
isDefaultValueColumn: true,
title: "Are Owners Notified",
description: "Are owners notified of this announcement?",
defaultValue: false,
})
@Column({
type: ColumnType.Boolean,

View File

@@ -1,3 +1,4 @@
import Monitor from "./Monitor";
import Project from "./Project";
import StatusPage from "./StatusPage";
import User from "./User";
@@ -328,6 +329,53 @@ export default class StatusPageAnnouncementTemplate extends BaseModel {
})
public statusPages?: Array<StatusPage> = undefined;
@ColumnAccessControl({
create: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.CreateStatusPageAnnouncementTemplate,
],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadStatusPageAnnouncementTemplate,
],
update: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.EditStatusPageAnnouncementTemplate,
],
})
@TableColumn({
required: false,
type: TableColumnType.EntityArray,
modelType: Monitor,
title: "Monitors",
description:
"List of monitors affected by this announcement template. If none are selected, all subscribers will be notified.",
})
@ManyToMany(
() => {
return Monitor;
},
{ eager: false },
)
@JoinTable({
name: "AnnouncementTemplateMonitor",
inverseJoinColumn: {
name: "monitorId",
referencedColumnName: "_id",
},
joinColumn: {
name: "announcementTemplateId",
referencedColumnName: "_id",
},
})
public monitors?: Array<Monitor> = undefined;
@ColumnAccessControl({
create: [
Permission.ProjectOwner,

View File

@@ -420,10 +420,12 @@ export default class StatusPageDomain extends BaseModel {
@JoinColumn({ name: "deletedByUserId" })
public deletedByUser?: User = undefined;
// This token is used by the Worker.
// worker pings the status page of customers - eg: status.company.com/verify-token/:id
// and the end point on Status Page project returns 200.
// when that happens the isCnameVerified is set to True and the certificate is added to Greenlock.
/*
* This token is used by the Worker.
* worker pings the status page of customers - eg: status.company.com/verify-token/:id
* and the end point on Status Page project returns 200.
* when that happens the isCnameVerified is set to True and the certificate is added to Greenlock.
*/
@ColumnAccessControl({
create: [
Permission.ProjectOwner,
@@ -474,7 +476,12 @@ export default class StatusPageDomain extends BaseModel {
public isCnameVerified?: boolean = undefined;
@ColumnAccessControl({
create: [],
create: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.CreateStatusPageDomain,
],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
@@ -489,6 +496,7 @@ export default class StatusPageDomain extends BaseModel {
type: TableColumnType.Boolean,
title: "SSL Ordered",
description: "Is SSL ordered?",
defaultValue: false,
})
@Column({
type: ColumnType.Boolean,
@@ -499,7 +507,12 @@ export default class StatusPageDomain extends BaseModel {
public isSslOrdered?: boolean = undefined;
@ColumnAccessControl({
create: [],
create: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.CreateStatusPageDomain,
],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
@@ -514,6 +527,7 @@ export default class StatusPageDomain extends BaseModel {
type: TableColumnType.Boolean,
title: "SSL Provisioned",
description: "Is SSL provisioned?",
defaultValue: false,
})
@Column({
type: ColumnType.Boolean,

View File

@@ -0,0 +1,469 @@
import Project from "./Project";
import StatusPage from "./StatusPage";
import User from "./User";
import BaseModel from "./DatabaseBaseModel/DatabaseBaseModel";
import Route from "../../Types/API/Route";
import { PlanType } from "../../Types/Billing/SubscriptionPlan";
import ColumnAccessControl from "../../Types/Database/AccessControl/ColumnAccessControl";
import TableAccessControl from "../../Types/Database/AccessControl/TableAccessControl";
import TableBillingAccessControl from "../../Types/Database/AccessControl/TableBillingAccessControl";
import CanAccessIfCanReadOn from "../../Types/Database/CanAccessIfCanReadOn";
import ColumnLength from "../../Types/Database/ColumnLength";
import ColumnType from "../../Types/Database/ColumnType";
import CrudApiEndpoint from "../../Types/Database/CrudApiEndpoint";
import EnableDocumentation from "../../Types/Database/EnableDocumentation";
import TableColumn from "../../Types/Database/TableColumn";
import TableColumnType from "../../Types/Database/TableColumnType";
import TableMetadata from "../../Types/Database/TableMetadata";
import TenantColumn from "../../Types/Database/TenantColumn";
import UniqueColumnBy from "../../Types/Database/UniqueColumnBy";
import IconProp from "../../Types/Icon/IconProp";
import ObjectID from "../../Types/ObjectID";
import Permission from "../../Types/Permission";
import { Column, Entity, Index, JoinColumn, ManyToOne } from "typeorm";
@EnableDocumentation()
@TableBillingAccessControl({
create: PlanType.Scale,
read: PlanType.Scale,
update: PlanType.Scale,
delete: PlanType.Scale,
})
@CanAccessIfCanReadOn("statusPage")
@TenantColumn("projectId")
@TableAccessControl({
create: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.CreateStatusPageSSO,
],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadStatusPageSSO,
],
delete: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.DeleteStatusPageSSO,
],
update: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.EditStatusPageSSO,
],
})
@CrudApiEndpoint(new Route("/status-page-scim"))
@TableMetadata({
tableName: "StatusPageSCIM",
singularName: "Status Page SCIM",
pluralName: "Status Page SCIM",
icon: IconProp.Lock,
tableDescription: "Manage SCIM auto-provisioning for your status page",
})
@Entity({
name: "StatusPageSCIM",
})
export default class StatusPageSCIM extends BaseModel {
@ColumnAccessControl({
create: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.CreateStatusPageSSO,
],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadStatusPageSSO,
],
update: [],
})
@TableColumn({
manyToOneRelationColumn: "projectId",
type: TableColumnType.Entity,
modelType: Project,
title: "Project",
description: "Relation to Project Resource in which this object belongs",
})
@ManyToOne(
() => {
return Project;
},
{
eager: false,
nullable: true,
onDelete: "CASCADE",
orphanedRowAction: "nullify",
},
)
@JoinColumn({ name: "projectId" })
public project?: Project = undefined;
@ColumnAccessControl({
create: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.CreateStatusPageSSO,
],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadStatusPageSSO,
],
update: [],
})
@Index()
@TableColumn({
type: TableColumnType.ObjectID,
required: true,
canReadOnRelationQuery: true,
title: "Project ID",
description: "ID of your OneUptime Project in which this object belongs",
})
@Column({
type: ColumnType.ObjectID,
nullable: false,
transformer: ObjectID.getDatabaseTransformer(),
})
public projectId?: ObjectID = undefined;
@ColumnAccessControl({
create: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.CreateStatusPageSSO,
],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadStatusPageSSO,
],
update: [],
})
@TableColumn({
manyToOneRelationColumn: "statusPageId",
type: TableColumnType.Entity,
modelType: StatusPage,
title: "Status Page",
description:
"Relation to Status Page Resource in which this object belongs",
})
@ManyToOne(
() => {
return StatusPage;
},
{
eager: false,
nullable: true,
onDelete: "CASCADE",
orphanedRowAction: "nullify",
},
)
@JoinColumn({ name: "statusPageId" })
public statusPage?: StatusPage = undefined;
@ColumnAccessControl({
create: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.CreateStatusPageSSO,
],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadStatusPageSSO,
],
update: [],
})
@Index()
@TableColumn({
type: TableColumnType.ObjectID,
required: true,
title: "Status Page ID",
description: "ID of your Status Page resource where this object belongs",
})
@Column({
type: ColumnType.ObjectID,
nullable: false,
transformer: ObjectID.getDatabaseTransformer(),
})
public statusPageId?: ObjectID = undefined;
@ColumnAccessControl({
create: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.CreateStatusPageSSO,
],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadStatusPageSSO,
],
update: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.EditStatusPageSSO,
],
})
@TableColumn({
required: true,
type: TableColumnType.ShortText,
canReadOnRelationQuery: true,
title: "Name",
description: "Any friendly name for this SCIM configuration",
})
@Column({
nullable: false,
type: ColumnType.ShortText,
length: ColumnLength.ShortText,
})
@UniqueColumnBy("statusPageId")
public name?: string = undefined;
@ColumnAccessControl({
create: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.CreateStatusPageSSO,
],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadStatusPageSSO,
],
update: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.EditStatusPageSSO,
],
})
@TableColumn({
required: false,
type: TableColumnType.LongText,
title: "Description",
description: "Friendly description to help you remember",
})
@Column({
nullable: true,
type: ColumnType.LongText,
length: ColumnLength.LongText,
})
public description?: string = undefined;
@ColumnAccessControl({
create: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.CreateStatusPageSSO,
],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ReadStatusPageSSO,
],
update: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.EditStatusPageSSO,
],
})
@TableColumn({
required: true,
type: TableColumnType.LongText,
title: "Bearer Token",
description: "Bearer token for SCIM authentication. Keep this secure.",
})
@Column({
nullable: false,
type: ColumnType.LongText,
length: ColumnLength.LongText,
})
public bearerToken?: string = undefined;
@ColumnAccessControl({
create: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.CreateStatusPageSSO,
],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadStatusPageSSO,
],
update: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.EditStatusPageSSO,
],
})
@TableColumn({
isDefaultValueColumn: true,
type: TableColumnType.Boolean,
title: "Auto Provision Users",
description:
"Automatically create status page users when they are added via SCIM",
defaultValue: true,
})
@Column({
type: ColumnType.Boolean,
default: true,
})
public autoProvisionUsers?: boolean = undefined;
@ColumnAccessControl({
create: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.CreateStatusPageSSO,
],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadStatusPageSSO,
],
update: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.EditStatusPageSSO,
],
})
@TableColumn({
isDefaultValueColumn: true,
type: TableColumnType.Boolean,
title: "Auto Deprovision Users",
description:
"Automatically remove status page users when they are removed via SCIM",
defaultValue: true,
})
@Column({
type: ColumnType.Boolean,
default: true,
})
public autoDeprovisionUsers?: boolean = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadStatusPageSSO,
],
update: [],
})
@TableColumn({
manyToOneRelationColumn: "createdByUserId",
type: TableColumnType.Entity,
modelType: User,
title: "Created by User",
description:
"Relation to User who created this object (if this object was created by a User)",
})
@ManyToOne(
() => {
return User;
},
{
eager: false,
nullable: true,
onDelete: "SET NULL",
orphanedRowAction: "nullify",
},
)
@JoinColumn({ name: "createdByUserId" })
public createdByUser?: User = undefined;
@ColumnAccessControl({
create: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.CreateStatusPageSSO,
],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadStatusPageSSO,
],
update: [],
})
@TableColumn({
type: TableColumnType.ObjectID,
title: "Created by User ID",
description:
"User ID who created this object (if this object was created by a User)",
})
@Column({
type: ColumnType.ObjectID,
nullable: true,
transformer: ObjectID.getDatabaseTransformer(),
})
public createdByUserId?: ObjectID = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadStatusPageSSO,
],
update: [],
})
@TableColumn({
manyToOneRelationColumn: "deletedByUserId",
type: TableColumnType.Entity,
modelType: User,
title: "Deleted by User",
description:
"Relation to User who deleted this object (if this object was deleted by a User)",
})
@ManyToOne(
() => {
return User;
},
{
cascade: false,
eager: false,
nullable: true,
onDelete: "SET NULL",
orphanedRowAction: "nullify",
},
)
@JoinColumn({ name: "deletedByUserId" })
public deletedByUser?: User = undefined;
@ColumnAccessControl({
create: [],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadStatusPageSSO,
],
update: [],
})
@TableColumn({
type: TableColumnType.ObjectID,
title: "Deleted by User ID",
description:
"User ID who deleted this object (if this object was deleted by a User)",
})
@Column({
type: ColumnType.ObjectID,
nullable: true,
transformer: ObjectID.getDatabaseTransformer(),
})
public deletedByUserId?: ObjectID = undefined;
}

View File

@@ -379,6 +379,65 @@ export default class StatusPageSubscriber extends BaseModel {
})
public slackWorkspaceName?: string = undefined;
@ColumnAccessControl({
create: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.CreateStatusPageSubscriber,
Permission.Public,
],
read: [],
update: [],
})
@TableColumn({
required: false,
type: TableColumnType.LongURL,
title: "Microsoft Teams Incoming Webhook URL",
description:
"Microsoft Teams incoming webhook URL to send notifications to Teams channel",
})
@Column({
nullable: true,
type: ColumnType.LongURL,
transformer: URL.getDatabaseTransformer(),
})
public microsoftTeamsIncomingWebhookUrl?: URL = undefined;
@ColumnAccessControl({
create: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.CreateStatusPageSubscriber,
Permission.Public,
],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadStatusPageSubscriber,
],
update: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.EditStatusPageSubscriber,
],
})
@TableColumn({
required: false,
type: TableColumnType.VeryLongText,
title: "Microsoft Teams Workspace Name",
description:
"Name of the Microsoft Teams workspace for validation and identification",
})
@Column({
nullable: true,
type: ColumnType.VeryLongText,
})
public microsoftTeamsWorkspaceName?: string = undefined;
@ColumnAccessControl({
create: [
Permission.ProjectOwner,
@@ -608,6 +667,7 @@ export default class StatusPageSubscriber extends BaseModel {
type: TableColumnType.Boolean,
title: "Send You Have Subscribed Message",
description: "Send You Have Subscribed Message when subscriber is created?",
defaultValue: true,
})
@Column({
type: ColumnType.Boolean,
@@ -642,6 +702,7 @@ export default class StatusPageSubscriber extends BaseModel {
title: "Is Subscribed to All Resources",
description:
"Is Subscriber Subscribed to All Resources on this status page?",
defaultValue: true,
})
@Column({
type: ColumnType.Boolean,
@@ -676,6 +737,7 @@ export default class StatusPageSubscriber extends BaseModel {
title: "Is Subscribed to All Event Types",
description:
"Is Subscriber Subscribed to All Event Types (like Incidents, Scheduled Events, Announcements) on this status page?",
defaultValue: true,
})
@Column({
type: ColumnType.Boolean,

View File

@@ -442,6 +442,7 @@ export default class TableView extends BaseModel {
type: TableColumnType.Number,
canReadOnRelationQuery: true,
description: "Items on page",
defaultValue: 10,
})
@Column({
type: ColumnType.Number,

View File

@@ -0,0 +1,403 @@
import Project from "./Project";
import Team from "./Team";
import User from "./User";
import BaseModel from "./DatabaseBaseModel/DatabaseBaseModel";
import Route from "../../Types/API/Route";
import { PlanType } from "../../Types/Billing/SubscriptionPlan";
import ColumnAccessControl from "../../Types/Database/AccessControl/ColumnAccessControl";
import TableAccessControl from "../../Types/Database/AccessControl/TableAccessControl";
import TableBillingAccessControl from "../../Types/Database/AccessControl/TableBillingAccessControl";
import ColumnLength from "../../Types/Database/ColumnLength";
import ColumnType from "../../Types/Database/ColumnType";
import CrudApiEndpoint from "../../Types/Database/CrudApiEndpoint";
import EnableDocumentation from "../../Types/Database/EnableDocumentation";
import EnableWorkflow from "../../Types/Database/EnableWorkflow";
import TableColumn from "../../Types/Database/TableColumn";
import TableColumnType from "../../Types/Database/TableColumnType";
import TableMetadata from "../../Types/Database/TableMetadata";
import TenantColumn from "../../Types/Database/TenantColumn";
import IconProp from "../../Types/Icon/IconProp";
import { JSONObject } from "../../Types/JSON";
import ObjectID from "../../Types/ObjectID";
import Permission from "../../Types/Permission";
import ComplianceRuleType from "../../Types/Team/ComplianceRuleType";
import { Column, Entity, Index, JoinColumn, ManyToOne } from "typeorm";
@TableBillingAccessControl({
create: PlanType.Scale,
read: PlanType.Free,
update: PlanType.Scale,
delete: PlanType.Free,
})
@EnableDocumentation()
@TableAccessControl({
create: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.EditProjectTeam,
],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadProjectTeam,
],
delete: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.EditProjectTeam,
],
update: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.EditProjectTeam,
],
})
@TenantColumn("projectId")
@CrudApiEndpoint(new Route("/team-compliance-setting"))
@Index(["teamId", "ruleType"], { unique: true })
@Entity({
name: "TeamComplianceSetting",
})
@EnableWorkflow({
create: false,
delete: false,
update: false,
read: false,
})
@TableMetadata({
tableName: "TeamComplianceSetting",
singularName: "Team Compliance Setting",
pluralName: "Team Compliance Settings",
icon: IconProp.CheckCircle,
tableDescription: "Compliance settings for your OneUptime team",
})
export default class TeamComplianceSetting extends BaseModel {
@ColumnAccessControl({
create: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.EditProjectTeam,
],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadProjectTeam,
],
update: [],
})
@TableColumn({
manyToOneRelationColumn: "projectId",
type: TableColumnType.Entity,
modelType: Project,
title: "Project",
description: "Relation to Project Resource in which this object belongs",
})
@ManyToOne(
() => {
return Project;
},
{
eager: false,
nullable: true,
onDelete: "CASCADE",
orphanedRowAction: "nullify",
},
)
@JoinColumn({ name: "projectId" })
public project?: Project = undefined;
@ColumnAccessControl({
create: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.EditProjectTeam,
],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadProjectTeam,
],
update: [],
})
@Index()
@TableColumn({
type: TableColumnType.ObjectID,
required: true,
canReadOnRelationQuery: true,
title: "Project ID",
description: "ID of your OneUptime Project in which this object belongs",
})
@Column({
type: ColumnType.ObjectID,
nullable: false,
transformer: ObjectID.getDatabaseTransformer(),
})
public projectId?: ObjectID = undefined;
@ColumnAccessControl({
create: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.EditProjectTeam,
],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadProjectTeam,
],
update: [],
})
@TableColumn({
manyToOneRelationColumn: "teamId",
type: TableColumnType.Entity,
modelType: Team,
title: "Team",
description: "Team this compliance setting belongs to.",
})
@ManyToOne(
() => {
return Team;
},
{
eager: false,
nullable: true,
onDelete: "CASCADE",
orphanedRowAction: "nullify",
},
)
@JoinColumn({ name: "teamId" })
public team?: Team = undefined;
@ColumnAccessControl({
create: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.EditProjectTeam,
],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadProjectTeam,
],
update: [],
})
@Index()
@TableColumn({
type: TableColumnType.ObjectID,
required: true,
title: "Team ID",
description: "ID of Team this compliance setting belongs to.",
})
@Column({
type: ColumnType.ObjectID,
nullable: false,
transformer: ObjectID.getDatabaseTransformer(),
})
public teamId?: ObjectID = undefined;
@ColumnAccessControl({
create: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.EditProjectTeam,
],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadProjectTeam,
],
update: [],
})
@TableColumn({
manyToOneRelationColumn: "createdByUserId",
type: TableColumnType.Entity,
modelType: User,
title: "Created by User",
description:
"Relation to User who created this object (if this object was created by a User)",
})
@ManyToOne(
() => {
return User;
},
{
eager: false,
nullable: true,
onDelete: "SET NULL",
orphanedRowAction: "nullify",
},
)
@JoinColumn({ name: "createdByUserId" })
public createdByUser?: User = undefined;
@ColumnAccessControl({
create: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.EditProjectTeam,
],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadProjectTeam,
],
update: [],
})
@TableColumn({
type: TableColumnType.ObjectID,
title: "Created by User ID",
description:
"User ID who created this object (if this object was created by a User)",
})
@Column({
type: ColumnType.ObjectID,
nullable: true,
transformer: ObjectID.getDatabaseTransformer(),
})
public createdByUserId?: ObjectID = undefined;
@ColumnAccessControl({
create: [],
read: [],
update: [],
})
@TableColumn({
manyToOneRelationColumn: "deletedByUserId",
type: TableColumnType.Entity,
title: "Deleted by User",
modelType: User,
description:
"Relation to User who deleted this object (if this object was deleted by a User)",
})
@ManyToOne(
() => {
return User;
},
{
cascade: false,
eager: false,
nullable: true,
onDelete: "SET NULL",
orphanedRowAction: "nullify",
},
)
@JoinColumn({ name: "deletedByUserId" })
public deletedByUser?: User = undefined;
@ColumnAccessControl({
create: [],
read: [],
update: [],
})
@TableColumn({
type: TableColumnType.ObjectID,
title: "Deleted by User ID",
description:
"User ID who deleted this object (if this object was deleted by a User)",
})
@Column({
type: ColumnType.ObjectID,
nullable: true,
transformer: ObjectID.getDatabaseTransformer(),
})
public deletedByUserId?: ObjectID = undefined;
@ColumnAccessControl({
create: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.EditProjectTeam,
],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadProjectTeam,
],
update: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.EditProjectTeam,
],
})
@TableColumn({
required: true,
type: TableColumnType.LongText,
title: "Rule Type",
description: "Type of compliance rule.",
})
@Column({
nullable: false,
type: ColumnType.LongText,
length: ColumnLength.LongText,
})
public ruleType?: ComplianceRuleType = undefined;
@ColumnAccessControl({
create: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.EditProjectTeam,
],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadProjectTeam,
],
update: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.EditProjectTeam,
],
})
@TableColumn({
isDefaultValueColumn: true,
type: TableColumnType.Boolean,
title: "Enabled",
description: "Whether this compliance rule is enabled.",
defaultValue: false,
})
@Column({
type: ColumnType.Boolean,
default: false,
})
public enabled?: boolean = undefined;
@ColumnAccessControl({
create: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.EditProjectTeam,
],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ProjectMember,
Permission.ReadProjectTeam,
],
update: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.EditProjectTeam,
],
})
@TableColumn({
required: false,
type: TableColumnType.JSON,
title: "Options",
description: "Additional options for this compliance rule.",
})
@Column({
type: ColumnType.JSON,
nullable: true,
})
public options?: JSONObject = undefined;
}

View File

@@ -146,7 +146,11 @@ export default class TelemetryUsageBilling extends BaseModel {
public productType?: ProductType = undefined;
@ColumnAccessControl({
create: [],
create: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
Permission.ManageProjectBilling,
],
read: [
Permission.ProjectOwner,
Permission.ProjectAdmin,
@@ -158,6 +162,7 @@ export default class TelemetryUsageBilling extends BaseModel {
type: TableColumnType.Number,
title: "Retain Telemetry Data For Days",
description: "Number of days to retain telemetry data for this service.",
defaultValue: DEFAULT_RETENTION_IN_DAYS,
})
@Column({
type: ColumnType.Number,

Some files were not shown because too many files have changed in this diff Show More