Compare commits

...

1041 Commits

Author SHA1 Message Date
Nawaz Dhandala
c6ab306a08 refactor: enhance MonitorCriteriaInstance and MonitorMetricType to support additional monitor types 2026-02-23 19:35:00 +00:00
Nawaz Dhandala
14cd9d249f feat: Add External Status Page Monitor functionality
- Introduced External Status Page Monitor to monitor third-party status pages.
- Implemented fetching logic for Atlassian Statuspage, RSS, and Atom feeds.
- Added new types and interfaces for handling external status page responses.
- Created UI components for configuring and displaying external status page monitors.
- Updated documentation to include details on the new monitor type and its configuration options.
2026-02-23 19:20:04 +00:00
Nawaz Dhandala
83149665e8 refactor: streamline Axios response handling and error parsing in VMRunner 2026-02-23 19:16:56 +00:00
Nawaz Dhandala
c2574d52da feat: extend crypto operations in VMRunner with randomUUID and randomInt 2026-02-23 19:03:58 +00:00
Nawaz Dhandala
1ede78e669 fix: update proxy_pass paths for documentation routes 2026-02-23 18:16:24 +00:00
Nawaz Dhandala
c3875137a5 Remove Alert Suppression Documentation and Migration Plans
- Deleted UI implementation document for Alert Suppression.
- Removed migration plan detailing database changes for Alert Suppression.
- Eliminated README file summarizing Alert Suppression implementation.
2026-02-23 18:14:55 +00:00
Nawaz Dhandala
b1ed2c00c4 Add documentation views and partials for error handling and content display
- Created NotFound.ejs for 404 error page with navigation and header.
- Added ServerError.ejs for 500 error page with retry and documentation link.
- Introduced Content.ejs partial for displaying documentation articles.
- Implemented Head.ejs partial for consistent head elements across pages.
- Developed Header.ejs partial for site header with navigation links.
- Created Nav.ejs partial for documentation navigation structure.
- Added OpenSourceCommitment.ejs partial to highlight open-source contributions.
- Implemented Pagination.ejs partial for navigation between documentation pages.
2026-02-23 18:14:15 +00:00
Nawaz Dhandala
1809a6f726 refactor: remove APIReference from nodemon watch and docker-compose volumes 2026-02-22 13:46:44 +00:00
Nawaz Dhandala
3e48478b4f Add sidenav partial for API reference documentation 2026-02-22 13:40:51 +00:00
Nawaz Dhandala
8ba6d5d058 refactor: remove SelfSignedSSL utility class 2026-02-22 09:18:16 +00:00
Nawaz Dhandala
d75e7f8d10 feat: add hostname and IP validation in DnsMonitorUtil 2026-02-22 09:15:56 +00:00
Nawaz Dhandala
b23236e85f chore: update version to 10.0.7 2026-02-22 08:58:05 +00:00
Nawaz Dhandala
2fe2d2c614 refactor: improve code formatting and comment clarity in NetworkPathMonitor and SyntheticMonitorWorker 2026-02-22 08:57:49 +00:00
Nawaz Dhandala
f2cce35a04 feat: enhance NetworkPathMonitor with destination validation and execFile usage 2026-02-22 08:56:30 +00:00
Nawaz Dhandala
93cead93fd Merge branch 'master' of https://github.com/OneUptime/oneuptime 2026-02-21 20:19:52 +00:00
Nawaz Dhandala
de2a1b463e feat: enhance error handling in SyntheticMonitor and add IPC flush timeout 2026-02-21 20:19:50 +00:00
Simon Larsen
88871cac78 Merge pull request #2314 from OneUptime/chore/npm-audit-fix
chore: npm audit fix
2026-02-21 15:43:11 +00:00
Nawaz Dhandala
39bf5da168 feat: handle SSO authorization exceptions in on-call policy fetch 2026-02-21 13:28:37 +00:00
Nawaz Dhandala
23d2341051 feat: filter projects based on SSO authentication before fetching on-call assignments 2026-02-21 13:09:55 +00:00
Nawaz Dhandala
c6d13d3647 feat: replace Text component with MarkdownContent for alert and incident descriptions 2026-02-21 12:49:09 +00:00
Nawaz Dhandala
1011f0704e chore: clean up code structure and remove unused code blocks 2026-02-21 12:45:54 +00:00
simlarsen
5ff6942b93 chore: npm audit fix 2026-02-21 02:14:33 +00:00
Nawaz Dhandala
93eff373a6 refactor: improve formatting of SSO token refresh effect in ProjectsScreen 2026-02-20 22:35:35 +00:00
Nawaz Dhandala
2fbc1b426d refactor: update comments for clarity and improve code formatting across multiple files 2026-02-20 22:30:17 +00:00
Nawaz Dhandala
288874be45 feat: add SSO authentication check and display warning for unauthenticated projects in HomeScreen 2026-02-20 22:29:04 +00:00
Nawaz Dhandala
47717fff17 feat: enhance ProjectsScreen layout and improve SSO authentication button styling 2026-02-20 21:46:48 +00:00
Nawaz Dhandala
5f58043b38 feat: refactor SSO provider fetching functions to improve data parsing and handling 2026-02-20 21:39:40 +00:00
Nawaz Dhandala
2bce40d993 feat: enhance SSO provider selection UI with project grouping and improved styling 2026-02-20 21:33:08 +00:00
Nawaz Dhandala
81e8931c19 feat: add SSO provider selection screen and integrate with ProjectsScreen 2026-02-20 21:23:40 +00:00
Nawaz Dhandala
314ec696ce feat: implement fetchSSOProvidersForProject function and update ProjectsScreen to use it 2026-02-20 21:11:14 +00:00
Nawaz Dhandala
3e80060ff8 fix: convert AxiosHeaders to plain object to prevent invalid Cookie headers 2026-02-20 20:31:10 +00:00
Nawaz Dhandala
8e5f5fc854 Merge branch 'master' of https://github.com/OneUptime/oneuptime 2026-02-20 16:34:37 +00:00
Nawaz Dhandala
5655d72fe1 fix: add headers property to request object in UserAuthorization tests 2026-02-20 16:34:33 +00:00
Simon Larsen
c4063fecdc Merge pull request #2313 from OneUptime/feat/ga4-cta-click-tracking
feat: Add GA4 CTA click tracking across all pages
2026-02-20 14:54:16 +00:00
Jamie Mallers
3e416c1679 feat: Add GA4 CTA click tracking across all pages
Track clicks on 'Get started free' and 'Request demo' CTAs via
gtag events (cta_get_started, cta_request_demo) and GTM dataLayer.

This enables GA4 conversion tracking for CTA clicks, which was
previously missing (GA audit found zero conversion events).

Uses event delegation so it automatically tracks all CTA links
including dynamically rendered ones.

Added to: homepage, pricing, demo, blog posts, blog list,
status page, and industry pages.
2026-02-20 14:46:19 +00:00
Nawaz Dhandala
006c41472c fix: improve formatting of error message in browser launch retries 2026-02-20 13:05:54 +00:00
Nawaz Dhandala
f6d8761fe8 feat: enhance browser launch functionality with retry logic and timeout settings 2026-02-20 12:56:56 +00:00
Nawaz Dhandala
a9e16a96d3 Merge branch 'master' of https://github.com/OneUptime/oneuptime 2026-02-20 12:48:59 +00:00
Nawaz Dhandala
387f948abe feat: add function to validate axios config against functions for security 2026-02-20 12:42:33 +00:00
Simon Larsen
e2ece21a8e Merge pull request #2312 from OneUptime/chore/npm-audit-fix
chore: npm audit fix
2026-02-20 09:38:58 +00:00
simlarsen
70651ace2e chore: npm audit fix 2026-02-20 02:27:39 +00:00
Nawaz Dhandala
3fd6582bc4 feat: implement SSO token management and projects screen for SSO authentication 2026-02-20 00:24:59 +00:00
Nawaz Dhandala
a6682624fc feat: add support for x-sso-tokens header to retrieve SSO tokens for mobile app flow 2026-02-20 00:24:38 +00:00
Nawaz Dhandala
7bb7837ec9 feat: implement SSO authentication flow and project management features 2026-02-20 00:23:29 +00:00
Nawaz Dhandala
f5d724a829 refactor: streamline SSO provider fetching logic and improve code readability 2026-02-19 23:29:57 +00:00
Nawaz Dhandala
b3081e109d feat: extend axios bridging to support additional HTTP methods and improve request handling 2026-02-19 23:25:45 +00:00
Nawaz Dhandala
4aafdb2d28 feat: enhance axios bridging to support additional HTTP methods and improved argument handling 2026-02-19 23:22:45 +00:00
Nawaz Dhandala
6afc99f54d feat: implement SSO login functionality and add SSO provider fetching 2026-02-19 23:20:15 +00:00
Nawaz Dhandala
b971482aaa feat: add additional Chromium arguments for stability in containerized environments 2026-02-19 23:12:14 +00:00
Nawaz Dhandala
00ff39a0cd refactor: update comment for pending notification processing in RootNavigator 2026-02-19 20:27:35 +00:00
Nawaz Dhandala
b14e319b66 feat: add push notification handling for incident episode creation 2026-02-19 20:12:56 +00:00
Nawaz Dhandala
43472de521 feat: add support for alert and incident number prefixes in notification templates 2026-02-19 20:10:55 +00:00
Nawaz Dhandala
cf72280ffa feat: enhance notification handling by processing pending notifications and improving navigation readiness 2026-02-19 20:08:22 +00:00
Nawaz Dhandala
049dd870d0 feat: add step to upload AAB as build artifact in Android deployment workflow 2026-02-19 19:45:57 +00:00
Nawaz Dhandala
02000e1a28 feat: add workflow for publishing Android app to Google Play Store 2026-02-19 19:43:31 +00:00
Nawaz Dhandala
32884930ae feat: add ITSAppUsesNonExemptEncryption key for iOS app compliance 2026-02-19 19:04:47 +00:00
Nawaz Dhandala
1a80b89fb8 feat: update app name and adaptive icon background color for improved branding 2026-02-19 18:50:09 +00:00
Nawaz Dhandala
ab83172d60 feat: preload Tailwind CSS and Google Fonts to improve loading performance and reduce layout shifts 2026-02-19 18:04:49 +00:00
Nawaz Dhandala
3a0d85174f feat: add native project generation for Android and iOS in MobileApp workflow 2026-02-19 17:44:24 +00:00
Nawaz Dhandala
6eee812701 feat: update font loading strategy and improve layout stability in documentation 2026-02-19 17:44:03 +00:00
Nawaz Dhandala
319093d3d8 feat: refactor axios instance creation with config merging support 2026-02-19 17:38:22 +00:00
Nawaz Dhandala
7b99129c2e feat: skip publishing if package version is already published on npm 2026-02-19 17:20:15 +00:00
Nawaz Dhandala
ee1996e961 feat: remove single-process argument from Chromium stability args 2026-02-19 17:03:27 +00:00
Nawaz Dhandala
d82d63b54a chore: bump version to 10.0.6 2026-02-19 15:48:43 +00:00
Nawaz Dhandala
5d2ab103ee Merge branch 'master' of https://github.com/OneUptime/oneuptime 2026-02-19 14:23:59 +00:00
Nawaz Dhandala
c57bda87ab feat: expose stability arguments and preferences in BrowserUtil for reuse 2026-02-19 14:21:56 +00:00
Nawaz Dhandala
54554d50c1 feat: add Firefox stability preferences for containerized environments 2026-02-19 14:19:42 +00:00
Nawaz Dhandala
3e6760dc7f feat: add Chromium stability arguments for containerized environments 2026-02-19 14:18:44 +00:00
Simon Larsen
c6d0200a98 Merge pull request #2309 from OneUptime/chore/npm-audit-fix
chore: npm audit fix
2026-02-19 14:08:10 +00:00
Nawaz Dhandala
fd9bd6ba6f feat: add stability arguments for headless browser in containerized environments
chore: bump version to 10.0.5
2026-02-19 08:45:12 +00:00
simlarsen
3348f44bf1 chore: npm audit fix 2026-02-19 02:31:47 +00:00
Nawaz Dhandala
b01ccf6318 chore: bump version to 10.0.4 2026-02-18 20:43:21 +00:00
Nawaz Dhandala
9a8b989e85 refactor: update comments for clarity and consistency in VMRunner 2026-02-18 20:43:14 +00:00
Nawaz Dhandala
f546126ee4 feat: add http/https Agent constructors for boundary serialization 2026-02-18 20:30:19 +00:00
Nawaz Dhandala
f903d0bea2 chore: bump version to 10.0.3 2026-02-18 20:15:12 +00:00
Nawaz Dhandala
32fcb9398c fix: improve waiting mechanism for @oneuptime/common availability on npm 2026-02-18 20:14:50 +00:00
Nawaz Dhandala
eee7b4cb67 chore: bump version to 10.0.2 2026-02-18 19:06:58 +00:00
Nawaz Dhandala
afb172c345 chore: bump version to 10.0.1 2026-02-18 18:41:04 +00:00
Nawaz Dhandala
1f9fad972c fix: remove expo-server-sdk dependency from package.json 2026-02-18 17:26:09 +00:00
Nawaz Dhandala
d9d9e70fd6 fix: remove NODE_AUTH_TOKEN environment variable from NPM publish step 2026-02-18 16:04:11 +00:00
Nawaz Dhandala
8b3912d369 feat: update NPM publish workflow to use NODE_AUTH_TOKEN and adjust CLI entry point 2026-02-18 16:03:32 +00:00
Nawaz Dhandala
7bf03074b2 fix: update shebang in Index.ts and correct paths in package.json for CLI execution 2026-02-18 15:55:03 +00:00
Nawaz Dhandala
b05db3486e feat: refactor tests to use environment variables for credentials and update jest command 2026-02-18 15:50:31 +00:00
Nawaz Dhandala
5e5fdb5402 feat: conditionally include title and body in push notification payload 2026-02-18 15:42:38 +00:00
Nawaz Dhandala
9ec5efbd1e feat: update base image to Node.js 24.9 in Dockerfile 2026-02-18 15:39:02 +00:00
Nawaz Dhandala
7d6e81cb8b feat: add mocks for isolated-vm and whois-json modules in Jest configuration 2026-02-18 15:37:53 +00:00
Nawaz Dhandala
a5e3e05f86 Merge branch 'master' into release 2026-02-18 15:24:00 +00:00
Nawaz Dhandala
266fcb274e chore: update version to 10.0.0 2026-02-18 15:23:48 +00:00
Nawaz Dhandala
70a8c9974c feat: update base image to Node.js 24.9-alpine3.21 in Dockerfile 2026-02-18 15:17:52 +00:00
Nawaz Dhandala
87501d8d1b feat: implement centralized logging utility and replace console statements in push notification handling 2026-02-18 15:08:30 +00:00
Nawaz Dhandala
a31f9d9d28 refactor: clean up code formatting and improve readability across multiple files 2026-02-18 15:02:24 +00:00
Nawaz Dhandala
d89be8ed57 feat: enhance error handling and process exit flow in SyntheticMonitorWorker 2026-02-18 15:00:25 +00:00
Nawaz Dhandala
509a010261 feat: enhance sandbox execution by wrapping user code in async IIFE and handling non-cloneable return values 2026-02-18 14:22:39 +00:00
Nawaz Dhandala
54754ff5ae feat: add registerProbeKey to values schema 2026-02-18 14:15:25 +00:00
Nawaz Dhandala
7f9ed4d439 feat: refactor SyntheticMonitor to use child processes for script execution
- Added isolated-vm dependency for secure script execution.
- Replaced direct Playwright usage in SyntheticMonitor with a worker process.
- Created SyntheticMonitorWorker to handle script execution in a sandboxed environment.
- Implemented proxy configuration handling for worker processes.
- Enhanced error handling and logging for script execution results.
- Removed unnecessary browser session management from SyntheticMonitor.
2026-02-18 14:14:24 +00:00
Nawaz Dhandala
d62816dd49 feat: update probe services to include environment variables for telemetry and logging 2026-02-18 13:50:28 +00:00
Nawaz Dhandala
7dd6129dad feat: add environment variables for log level and node environment in isolated-vm deployment 2026-02-18 13:42:14 +00:00
Nawaz Dhandala
7ccea02340 fix secret in probe 2026-02-18 13:37:22 +00:00
Nawaz Dhandala
0af41725b4 fix: add missing comma in dependencies section of package.json 2026-02-18 13:25:17 +00:00
Nawaz Dhandala
9f6bcddc1e feat: implement default notification rules for verified communication methods in User APIs 2026-02-18 13:16:54 +00:00
Nawaz Dhandala
97c461f7a3 fix: update key generation in MyOnCallPoliciesScreen to handle undefined policyId 2026-02-18 13:08:38 +00:00
Nawaz Dhandala
736f8bb83c chore: update adaptive and regular icons in MobileApp assets 2026-02-18 13:05:25 +00:00
Nawaz Dhandala
eb33daf64f refactor: add expo-splash-screen plugin configuration to app.json 2026-02-18 12:57:19 +00:00
Nawaz Dhandala
c3c90eef03 fix: ensure title and body are defaulted to empty strings in push notification 2026-02-18 12:25:14 +00:00
Nawaz Dhandala
e92e9f08d3 refactor: enhance push notification handling with PushNotificationService integration 2026-02-18 10:31:12 +00:00
Nawaz Dhandala
2b313a7702 Merge branch 'master' of https://github.com/OneUptime/oneuptime 2026-02-18 09:58:01 +00:00
Nawaz Dhandala
3cf7c7d1ae refactor: implement push notification relay and enhance Expo integration 2026-02-18 09:56:10 +00:00
Nawaz Dhandala
76cfa7186e refactor: add eas.json configuration for build and submission settings 2026-02-18 09:00:13 +00:00
Simon Larsen
afaff717c0 Merge pull request #2304 from OneUptime/snyk-upgrade-950bdb1d48a0c3f367ba1c51cd0a7dee
[Snyk] Upgrade playwright from 1.57.0 to 1.58.0
2026-02-18 08:57:48 +00:00
Simon Larsen
fde0d5f2c6 Merge pull request #2302 from OneUptime/snyk-fix-68e2c2852a4c507876028cf50ec2c87c
[Snyk] Security upgrade nginx from 1.29.3-alpine to 1.29.5-alpine
2026-02-18 08:57:18 +00:00
Simon Larsen
d5c5387621 Merge branch 'master' into snyk-upgrade-950bdb1d48a0c3f367ba1c51cd0a7dee 2026-02-18 08:56:47 +00:00
Simon Larsen
e0ef6e9a77 Merge pull request #2308 from OneUptime/chore/npm-audit-fix
chore: npm audit fix
2026-02-18 08:56:11 +00:00
Nawaz Dhandala
2dc0dc4c96 refactor: add google services configuration for Firebase integration 2026-02-18 08:54:10 +00:00
Nawaz Dhandala
c9eb72ba2c refactor: enhance push notification registration with improved error handling and retry logic 2026-02-18 08:39:29 +00:00
Nawaz Dhandala
92e247d168 refactor: update logo size and styling in LoginScreen and ServerUrlScreen for improved visual consistency 2026-02-18 07:57:47 +00:00
Nawaz Dhandala
14988c438a refactor: update app.json slug and add permissions for biometric authentication 2026-02-18 07:48:31 +00:00
Nawaz Dhandala
d81682d02f Refactor styles in various components for improved readability and consistency
- Simplified style definitions in AlertCard, EpisodeCard, FeedTimeline, IncidentCard, AlertDetailScreen, AlertEpisodeDetailScreen, and others by using multi-line formatting.
- Enhanced the layout of components in HomeScreen, MyOnCallPoliciesScreen, and SettingsScreen for better maintainability.
- Updated text styles for better clarity and consistency across screens.
2026-02-18 07:47:07 +00:00
simlarsen
9d5faca3ec chore: npm audit fix 2026-02-18 02:31:53 +00:00
Nawaz Dhandala
89ccde1bc4 refactor: standardize device type strings to lowercase in registerPushDevice function for consistency 2026-02-17 22:12:43 +00:00
Nawaz Dhandala
3aab280dcd refactor: standardize shadow properties and background colors across components for improved consistency 2026-02-17 22:04:56 +00:00
Nawaz Dhandala
b8e44a1bcf refactor: update layout and styling in AddNoteModal and NotesSection for improved consistency and visual clarity 2026-02-17 21:55:20 +00:00
Nawaz Dhandala
4c3b4d23ff refactor: update assignment badge colors and icon in MyOnCallPoliciesScreen for improved visual consistency 2026-02-17 21:42:26 +00:00
Nawaz Dhandala
a4ff718d61 refactor: remove unnecessary shadow properties and simplify layout in MyOnCallPoliciesScreen for improved performance and consistency 2026-02-17 21:40:03 +00:00
Nawaz Dhandala
3433a815f3 refactor: simplify Pressable style and adjust layout properties in HomeScreen for improved accessibility and visual consistency 2026-02-17 21:32:54 +00:00
Nawaz Dhandala
2a20807126 refactor: update padding values in MyOnCallPoliciesScreen for improved layout consistency 2026-02-17 21:31:10 +00:00
Nawaz Dhandala
991dc1c842 refactor: update icon in AlertCard and IncidentCard from desktop-outline to pulse-outline for improved visual representation 2026-02-17 21:29:59 +00:00
Nawaz Dhandala
2026e7fd77 refactor: move marginBottom to parent View in EpisodeCard for improved layout consistency 2026-02-17 21:28:48 +00:00
Nawaz Dhandala
1d0016412e refactor: add marginBottom to SwipeableCard for improved layout spacing 2026-02-17 21:27:38 +00:00
Nawaz Dhandala
917f27fe11 Refactor styles in multiple screens to use inline styles instead of class names for consistency and improved readability. Updated layout properties and adjusted padding, margin, and font sizes across IncidentsScreen, MyOnCallPoliciesScreen, SettingsScreen, LoginScreen, and ServerUrlScreen for better UI alignment and responsiveness. 2026-02-17 21:25:03 +00:00
Nawaz Dhandala
c07c89e3dd refactor: replace Pressable with TouchableOpacity in StatCard for improved touch handling and update layout for better spacing 2026-02-17 21:16:58 +00:00
Nawaz Dhandala
32c4c1666d refactor: update button layout and styles in AlertEpisodeDetailScreen and IncidentEpisodeDetailScreen for consistency 2026-02-17 21:12:52 +00:00
Nawaz Dhandala
636a419cbd refactor: replace Pressable with TouchableOpacity for improved touch handling in SegmentedControl, AlertDetailScreen, and IncidentDetailScreen 2026-02-17 21:10:54 +00:00
Nawaz Dhandala
61699b9f4a refactor: replace Pressable with TouchableOpacity for theme selection options 2026-02-17 21:07:19 +00:00
Nawaz Dhandala
b6ed3643c3 fix: update splash screen background color and replace asset images 2026-02-17 20:57:17 +00:00
Nawaz Dhandala
9e73ac45a1 Merge branch 'master' of https://github.com/OneUptime/oneuptime 2026-02-17 15:08:20 +00:00
Nawaz Dhandala
7a3dbd0e8e refactor: streamline style definitions in multiple components for consistency 2026-02-17 14:57:46 +00:00
Nawaz Dhandala
1ec25c27ee refactor: replace TouchableOpacity with Pressable for improved touch handling across components 2026-02-17 14:55:36 +00:00
Simon Larsen
5286527155 Merge pull request #2305 from OneUptime/chore/npm-audit-fix
chore: npm audit fix
2026-02-17 09:49:52 +00:00
simlarsen
895af10755 chore: npm audit fix 2026-02-17 02:28:09 +00:00
Nawaz Dhandala
77ccca7e2a refactor: simplify query client setup and improve deep link handling in navigation 2026-02-16 22:31:13 +00:00
Nawaz Dhandala
66f46e9b84 chore: remove unused whois-json type definitions and update package dependencies 2026-02-16 20:06:11 +00:00
Nawaz Dhandala
91edae50b2 feat: Implement domain monitoring criteria and secret handling for domain names 2026-02-16 19:17:00 +00:00
Nawaz Dhandala
7ab3dfe043 feat: Add Domain Monitor functionality with WHOIS integration
- Updated package.json to include whois-json dependency.
- Created DomainMonitorCriteria class for evaluating domain monitoring criteria.
- Added DomainMonitorResponse interface to define the structure of domain monitoring responses.
- Introduced MonitorStepDomainMonitor interface and utility for managing domain monitor steps.
- Developed DomainMonitorStepForm component for user input on domain monitoring settings.
- Implemented DomainMonitorView component to display monitoring results and details.
- Added DomainMonitorUtil class for querying domain information using WHOIS data.
- Included parsing methods for name servers and domain status in DomainMonitorUtil.
2026-02-16 19:10:44 +00:00
Nawaz Dhandala
fb661126d4 chore: bump version to 9.5.13 2026-02-16 17:10:17 +00:00
Nawaz Dhandala
94c57f3189 style: update DNSSEC comment to use block comment format for clarity 2026-02-16 17:10:06 +00:00
Nawaz Dhandala
4de6021905 fix: update DNSSEC check to use a default resolver if none specified 2026-02-16 17:09:41 +00:00
Nawaz Dhandala
c62a49d499 fix: combine iputils and dnsutils installation in Dockerfile 2026-02-16 16:56:11 +00:00
snyk-bot
01fd5263ca fix: upgrade playwright from 1.57.0 to 1.58.0
Snyk has created this PR to upgrade playwright from 1.57.0 to 1.58.0.

See this package in npm:
playwright

See this project in Snyk:
https://app.snyk.io/org/oneuptime-RsC2nshvQ2Vnr35jHvMnMP/project/49c81d9c-12c2-4e8e-b9e8-72f98b1b595c?utm_source=github&utm_medium=referral&page=upgrade-pr
2026-02-16 15:58:33 +00:00
Nawaz Dhandala
d87eee68e8 style: refactor arrow functions to use explicit return statements for clarity 2026-02-16 14:55:08 +00:00
Nawaz Dhandala
3f4db5b7e0 feat: implement project creation restriction for non-admin users 2026-02-16 14:54:14 +00:00
Nawaz Dhandala
462ad9d6ab Merge branch 'master' of https://github.com/OneUptime/oneuptime 2026-02-16 14:36:46 +00:00
Nawaz Dhandala
6444d3d5cc Merge branch 'master' of https://github.com/OneUptime/oneuptime 2026-02-16 14:36:31 +00:00
Nawaz Dhandala
415222561b feat: add health checks and common dependencies for services in Docker Compose files 2026-02-16 14:34:26 +00:00
Nawaz Dhandala
8cf2661c63 style: adjust spacing and styling for CardSelect option groups and EvaluationLogList component 2026-02-16 11:23:21 +00:00
Nawaz Dhandala
a820f817ff fix: update header color for option groups in CardSelect component 2026-02-16 11:20:14 +00:00
Nawaz Dhandala
576927c6c7 refactor: rename monitoring category from "Active Monitoring" to "Basic Monitoring" 2026-02-16 11:08:57 +00:00
Nawaz Dhandala
e866db9e18 feat: enhance monitor type handling with categorized card select options and improve DNS resolver configuration 2026-02-16 11:08:31 +00:00
Nawaz Dhandala
8e91a786f9 refactor: streamline route definition for CLI endpoint 2026-02-16 10:39:43 +00:00
Nawaz Dhandala
02d16446f1 feat: update documentation links in CLI and MCP server pages for improved navigation 2026-02-16 10:32:28 +00:00
Nawaz Dhandala
5d5517258b Refactor code structure for improved readability and maintainability 2026-02-16 08:59:39 +00:00
Nawaz Dhandala
5df632c46c feat: update CLI documentation to reflect changes in incident state handling and queries 2026-02-16 08:47:01 +00:00
Nawaz Dhandala
c1ee79b339 feat: enhance CLI documentation with additional context and options for commands 2026-02-16 08:41:48 +00:00
Nawaz Dhandala
67265c0fc8 feat: include createdAt and planName in project details for improved project information 2026-02-16 08:32:46 +00:00
Nawaz Dhandala
72e5384012 chore: remove ora dependency from package.json and package-lock.json 2026-02-16 08:30:39 +00:00
Nawaz Dhandala
dc8e9d44b1 chore: bump version to 9.5.12 2026-02-16 08:21:14 +00:00
Nawaz Dhandala
91102ee952 feat: add CLI section to navigation with links to documentation 2026-02-15 20:29:36 +00:00
Nawaz Dhandala
e46d1ae7da chore: update compile workflow to install and compile Common package 2026-02-15 20:19:08 +00:00
Nawaz Dhandala
008005415a fix: update MarkdownContent styles to use ReturnType for better type inference 2026-02-15 20:14:56 +00:00
Nawaz Dhandala
c7362f3ada feat: add comprehensive CLI documentation including authentication, command reference, resource operations, output formats, and scripting for automation 2026-02-15 20:10:21 +00:00
Nawaz Dhandala
1f634576fe chore: update CLI to use npm package for Common, adjust TypeScript config, and add CI workflow
- Changed dependency for Common in CLI package.json to use npm package @oneuptime/common@latest.
- Updated tsconfig.json to exclude Tests, build, node_modules, and jest.config.json.
- Modified PublishAllPackages.sh to replace Common dependency with the pinned version during publish.
- Added GitHub Actions workflow for testing CLI on pull requests and pushes.
2026-02-15 12:05:35 +00:00
Nawaz Dhandala
d25a97fe17 Refactor components for improved readability and consistency
- Added missing newlines at the end of files in MarkdownContent.tsx and RootCauseCard.tsx
- Reformatted shadowColor and color properties in NotesSection.tsx, SegmentedControl.tsx, MainTabNavigator.tsx, HomeScreen.tsx for better readability
- Enhanced code formatting in SectionHeader.tsx and OnCallStackNavigator.tsx for consistency
- Improved readability of getEntityId function in useAllProjectOnCallPolicies.ts
- Refactored conditional rendering in AlertDetailScreen.tsx, AlertEpisodeDetailScreen.tsx, IncidentDetailScreen.tsx, and IncidentEpisodeDetailScreen.tsx for better clarity
2026-02-15 11:47:32 +00:00
Nawaz Dhandala
b89ff11db8 Add comprehensive tests for CLI commands and error handling
- Implement tests for ResourceCommands, ConfigCommands, UtilityCommands, and ErrorHandler.
- Enhance test coverage for command registration and execution, including list, get, create, update, delete, and count operations.
- Introduce tests for credential management and context handling in commands.
- Add error handling tests to ensure graceful exits on API errors and invalid inputs.
- Update jest configuration to exclude test files from coverage and adjust TypeScript settings.
2026-02-15 10:54:50 +00:00
Nawaz Dhandala
5ac5ffede5 feat(cli): initialize CLI package with TypeScript configuration and dependencies
- Added package.json for OneUptime CLI with scripts for development and build processes.
- Included TypeScript configuration (tsconfig.json) with strict type checking and module settings.
2026-02-15 10:36:30 +00:00
Nawaz Dhandala
d9167b89ba feat: Add toPlainText utility function and update components to use it for improved text handling 2026-02-14 22:02:21 +00:00
Nawaz Dhandala
66b995c64a feat: Implement On-Call policies feature with navigation, API integration, and UI components 2026-02-14 21:51:09 +00:00
Nawaz Dhandala
f383bbba4d refactor: Update notification icons in MainTabNavigator for improved clarity 2026-02-14 21:42:19 +00:00
Nawaz Dhandala
43f1a59042 refactor: Update icon names in HomeScreen for improved clarity and consistency 2026-02-14 21:41:10 +00:00
Nawaz Dhandala
7d49872edc refactor: Enhance HomeScreen layout by adding section headers for Incidents and Alerts, improving organization and readability 2026-02-14 21:38:03 +00:00
Nawaz Dhandala
6d2d5892b9 refactor: Update label from 'Total active issues' to 'Total active items' and adjust total count calculation in HomeScreen for improved accuracy 2026-02-14 21:35:53 +00:00
Nawaz Dhandala
756217e19e refactor: Simplify layout in HomeScreen by removing unnecessary Live indicator for cleaner UI 2026-02-14 21:34:57 +00:00
Nawaz Dhandala
ca3cf01be7 refactor: Update icon for Root Cause section in AlertDetailScreen, IncidentDetailScreen, and IncidentEpisodeDetailScreen for improved clarity 2026-02-14 21:33:33 +00:00
Nawaz Dhandala
fd0a81a0b1 refactor: Remove unused Ionicons import and clean up RootCauseCard layout for improved readability 2026-02-14 21:31:49 +00:00
Nawaz Dhandala
14016d188b refactor: Replace MarkdownContent with RootCauseCard component in AlertDetailScreen, IncidentDetailScreen, and IncidentEpisodeDetailScreen for improved root cause display 2026-02-14 21:31:30 +00:00
Nawaz Dhandala
3a2aff7f34 refactor: Integrate MarkdownContent component for improved markdown rendering in FeedTimeline, AlertDetailScreen, IncidentDetailScreen, and IncidentEpisodeDetailScreen; update package.json and package-lock.json for new dependencies 2026-02-14 21:28:04 +00:00
Nawaz Dhandala
4a6edfee06 refactor: Update AlertCard, EpisodeCard, and IncidentCard components for improved styling and consistency; enhance rgbToHex function for better color handling 2026-02-14 21:23:20 +00:00
Nawaz Dhandala
2dc1b8aa8c refactor: Enhance footer in SettingsScreen with updated layout, styling, and open source acknowledgment 2026-02-14 21:18:45 +00:00
Nawaz Dhandala
eb0f0e742d refactor: Update color handling in GradientButton, NotesSection, SegmentedControl, and SettingsScreen for improved theme support 2026-02-14 21:16:37 +00:00
Nawaz Dhandala
23c82c5239 refactor: Update UI components for improved styling and consistency across AlertCard, EpisodeCard, IncidentCard, GradientButton, NotesSection, SegmentedControl, and detail screens 2026-02-14 21:15:32 +00:00
Nawaz Dhandala
2b61e4f4b7 refactor: Improve layout and accessibility in AlertCard, EpisodeCard, and IncidentCard components 2026-02-14 21:11:58 +00:00
Nawaz Dhandala
9b21abf78d refactor: Enhance footer in SettingsScreen with gradient background and open source acknowledgment 2026-02-14 20:56:02 +00:00
Nawaz Dhandala
bd54b38a69 refactor: Update footer text in SettingsScreen for clarity and add text alignment 2026-02-14 20:55:26 +00:00
Nawaz Dhandala
4dc799d238 refactor: Add useWindowDimensions for responsive tab bar label visibility 2026-02-14 20:52:24 +00:00
Nawaz Dhandala
b4d90e3bef refactor: Update color palette to neutral monochrome accents for improved consistency 2026-02-14 20:51:14 +00:00
Nawaz Dhandala
6c8d4203da refactor: Update Logo component sizes in HomeScreen and SettingsScreen for improved layout 2026-02-14 20:50:19 +00:00
Nawaz Dhandala
f7e9745624 refactor: Update Logo component to use SvgXml and adjust sizes in HomeScreen and SettingsScreen 2026-02-14 20:48:00 +00:00
Nawaz Dhandala
f7d133adba refactor: Add TypeScript types for alert and incident models and update tsconfig paths 2026-02-14 20:38:15 +00:00
Nawaz Dhandala
b06c2cb1c3 refactor: Add root cause field to alert and incident APIs and update detail screens for display 2026-02-14 20:31:06 +00:00
Nawaz Dhandala
b51c5d9677 refactor: Improve UI components with enhanced styles and layout adjustments across multiple screens 2026-02-14 20:29:12 +00:00
Nawaz Dhandala
9a1e265d1c refactor: Enhance UI with LinearGradient backgrounds and improve component styles across multiple screens 2026-02-14 20:26:05 +00:00
Nawaz Dhandala
e18d75fc8e refactor: Add TypeScript declarations for additional languages in react-syntax-highlighter across multiple files 2026-02-14 20:14:11 +00:00
Nawaz Dhandala
5a68d2f726 refactor: Add TypeScript declarations for additional languages in react-syntax-highlighter 2026-02-14 20:03:04 +00:00
Nawaz Dhandala
dfa7c4875a refactor: Simplify JSX structure in multiple components for improved readability 2026-02-14 20:00:20 +00:00
Nawaz Dhandala
8a568e0495 chore: Bump version to 9.5.11 2026-02-14 19:59:46 +00:00
Nawaz Dhandala
7152058ee2 Merge branch 'dash-chunk' 2026-02-14 19:54:45 +00:00
Nawaz Dhandala
b198dc0ec8 refactor: Enhance lazy loading of components in StatusPage for improved type safety and maintainability 2026-02-14 19:54:05 +00:00
Nawaz Dhandala
b0a3f8d60f refactor: Enable tree shaking in esbuild configuration for optimized bundle size 2026-02-14 19:42:53 +00:00
Nawaz Dhandala
83a13635cf Refactor route components to improve readability and consistency in prop spreading 2026-02-14 18:38:45 +00:00
Nawaz Dhandala
a2c1744e8c refactor: Enhance mermaid plugin to serve pre-bundled CJS file with ESM export for improved compatibility 2026-02-14 15:00:28 +00:00
snyk-bot
9dfbc05618 fix: Nginx/Dockerfile.tpl to reduce vulnerabilities
The following vulnerabilities are fixed with an upgrade:
- https://snyk.io/vuln/SNYK-ALPINE322-LIBPNG-15062355
- https://snyk.io/vuln/SNYK-ALPINE322-LIBPNG-15062356
- https://snyk.io/vuln/SNYK-ALPINE322-OPENSSL-15121112
- https://snyk.io/vuln/SNYK-ALPINE322-OPENSSL-15121113
- https://snyk.io/vuln/SNYK-ALPINE322-OPENSSL-15121196
2026-02-14 14:53:26 +00:00
Nawaz Dhandala
4271ddbdcb refactor: Consolidate lazy loading of components into AllPages for improved organization and performance 2026-02-14 14:52:55 +00:00
Nawaz Dhandala
6ffb081a02 refactor: Consolidate route imports into AllRoutes for improved organization and maintainability 2026-02-14 14:51:01 +00:00
Nawaz Dhandala
16a9edbfcd Refactor route imports in StatusPagesRoutes, TracesRoutes, UserSettingsRoutes, and WorkflowRoutes for improved readability and performance by replacing lazy loading with direct imports. Remove unnecessary Suspense components around routes. 2026-02-14 11:00:15 +00:00
Nawaz Dhandala
e32d4395a3 refactor: update UI components for consistency and improved theming
- Refactored IncidentsScreen to use theme colors for backgrounds and text.
- Adjusted font sizes and styles across various components for better readability.
- Updated SettingsScreen to enhance layout and visual hierarchy, including removing GlassCard and using View components with theme colors.
- Modified LoginScreen and ServerUrlScreen to improve input field styling and overall layout.
- Revised color tokens in theme/colors.ts for better contrast and accessibility.
- Improved button labels for clarity and consistency.
2026-02-13 21:24:31 +00:00
Nawaz Dhandala
b8cd3ce1c1 Merge branch 'release' of https://github.com/OneUptime/oneuptime into release 2026-02-13 20:57:59 +00:00
Nawaz Dhandala
b86aee7f2a refactor: Update comments for channel name normalization and sanitization in Slack and Teams utilities 2026-02-13 20:57:25 +00:00
Nawaz Dhandala
2cde167445 refactor: Enhance channel name normalization to remove invalid characters for Microsoft Teams 2026-02-13 20:56:22 +00:00
Nawaz Dhandala
9bd6b011fe refactor: Sanitize Slack channel names to remove invalid characters 2026-02-13 20:52:02 +00:00
Nawaz Dhandala
538eef5660 chore: Bump version to 9.5.10 2026-02-13 19:27:29 +00:00
Simon Larsen
e1a343ae38 Merge pull request #2300 from OneUptime/master
Release
2026-02-13 19:27:00 +00:00
Nawaz Dhandala
8b42af35c1 refactor: Remove unused noteModalVisible prop from NotesSection component 2026-02-13 17:12:49 +00:00
Nawaz Dhandala
fc9026a8d8 refactor: Clean up component code and improve formatting across multiple files 2026-02-13 17:11:00 +00:00
Nawaz Dhandala
86edee35c1 style: Update color scheme and improve UI consistency across components 2026-02-13 17:09:12 +00:00
Nawaz Dhandala
109c276bc5 feat: Update GlassCard component to support opaque prop and apply it to multiple cards 2026-02-13 16:55:15 +00:00
Nawaz Dhandala
8040dd0f56 Refactor UI components and enhance styling
- Removed unused theme variable from IncidentsScreen.
- Updated SettingsScreen to use GlassCard and LinearGradient for improved UI.
- Refactored LoginScreen and ServerUrlScreen to utilize GradientHeader and GlassCard for consistent styling.
- Introduced GradientButton component for reusable gradient-styled buttons.
- Added Logo component for consistent branding across screens.
- Created NotesSection component to manage and display internal notes.
- Implemented SectionHeader component for better organization of section titles.
- Enhanced color theme with new gradient and accent colors.
2026-02-13 16:45:48 +00:00
Nawaz Dhandala
00d4148b6b Enhance UI and UX across multiple screens
- Updated BiometricLockScreen with improved icon layout and shadow effects.
- Refined HomeScreen layout for better spacing and added header gradient.
- Enhanced IncidentDetailScreen with gradient backgrounds and improved badge styling.
- Replaced error handling in IncidentsScreen with a reusable EmptyState component.
- Improved SettingsScreen with a profile header and added icons to settings rows.
- Revamped LoginScreen and ServerUrlScreen with gradient headers and refined input fields.
- Updated color theme to include new gradient properties for better visual consistency.
2026-02-13 16:14:15 +00:00
Nawaz Dhandala
dec03bc3a8 feat: Add expo-font dependency to package.json and package-lock.json 2026-02-13 15:45:30 +00:00
Nawaz Dhandala
46a9f95fc0 fix: Update error handling in getUserMiddleware to use NotAuthenticatedException 2026-02-13 15:42:10 +00:00
Simon Larsen
8b2f9bc778 Merge pull request #2299 from OneUptime/dna-monitor
DNS monitor
2026-02-13 14:04:51 +00:00
Nawaz Dhandala
fcc6223850 refactor: Remove unused variables and improve type annotations in hooks and screens 2026-02-13 14:04:23 +00:00
Nawaz Dhandala
c9bc214e86 fix: Correct CAA record resolution method and typo in value formatting 2026-02-13 13:57:31 +00:00
Simon Larsen
2897a937ba Merge pull request #2298 from OneUptime/refactor-mob-app
Refactor mob app
2026-02-13 13:49:05 +00:00
Nawaz Dhandala
f3cd7be143 refactor: Clean up code formatting and improve readability across multiple components 2026-02-13 13:48:46 +00:00
Nawaz Dhandala
f324a4e864 feat: Implement DNS monitoring configuration and secret handling 2026-02-13 13:42:45 +00:00
Nawaz Dhandala
f6a8cef649 feat: Add DNS monitoring capabilities
- Introduced new DNS monitor types and criteria checks in CriteriaFilter.ts.
- Implemented DNS monitor instance creation in MonitorCriteriaInstance.ts.
- Enhanced MonitorStep to support DNS configurations.
- Added DNS response handling in ProbeMonitorResponse.ts.
- Created DNS monitor forms and summary views in the Dashboard.
- Developed DNS query utilities and response handling in MonitorTypes/DnsMonitor.ts.
- Added DNS record types and response structures for better data handling.
- Implemented criteria evaluation for DNS monitoring in DnsMonitorCriteria.ts.
2026-02-13 13:29:01 +00:00
Nawaz Dhandala
770ef007a4 refactor: Enhance response normalization in API client to handle serialized types 2026-02-13 12:10:42 +00:00
Nawaz Dhandala
dafa0cc5d9 refactor: Update styling in AppContent and RootNavigator for consistent theme integration 2026-02-13 11:48:31 +00:00
Nawaz Dhandala
196e9cae10 refactor: Enhance error handling in getBlogPost method to return null on failure 2026-02-13 11:33:02 +00:00
Nawaz Dhandala
d0d26d20b2 refactor: Integrate SplashScreen to hide native splash screen after loading completes 2026-02-13 11:25:13 +00:00
Nawaz Dhandala
6a90ee97bf Merge branch 'master' of https://github.com/OneUptime/oneuptime 2026-02-13 11:12:22 +00:00
Nawaz Dhandala
f2a2644b0e Merge branch 'release' 2026-02-13 11:12:12 +00:00
Nawaz Dhandala
5cb2ac8c8b fix: Use absolute path for npm in ts-node installation to ensure correct execution 2026-02-13 11:10:34 +00:00
Nawaz Dhandala
6751d59b2f refactor: Remove totalActive calculation and active issues summary from HomeScreen 2026-02-13 11:05:09 +00:00
Nawaz Dhandala
aefc649743 refactor: Integrate useAuth in ProjectProvider for authentication handling 2026-02-13 10:25:57 +00:00
Nawaz Dhandala
cfba73665c refactor: Update loading state to use 'isPending' instead of 'isLoading' in multiple hooks 2026-02-13 10:21:06 +00:00
Nawaz Dhandala
049c5d003c refactor: Add flex styling to SectionList in AlertsScreen and IncidentsScreen for better layout 2026-02-13 10:15:17 +00:00
Simon Larsen
fd8998952d Merge pull request #2297 from OneUptime/chore/npm-audit-fix
chore: npm audit fix
2026-02-13 10:06:30 +00:00
Nawaz Dhandala
5ca85e4915 refactor: Update header comment in fetchProjects function for clarity on multi-tenant request 2026-02-13 09:58:09 +00:00
Nawaz Dhandala
aa401291b6 refactor: Update SettingsRow component to consolidate version display in SettingsScreen 2026-02-13 09:01:30 +00:00
Nawaz Dhandala
bf3d90871d refactor: Add feed fetching functions and integrate FeedTimeline component in detail screens 2026-02-13 09:00:20 +00:00
Nawaz Dhandala
4a4dff9264 refactor: Add 'muted' prop to AlertCard, EpisodeCard, and IncidentCard; implement opacity adjustment based on prop value 2026-02-13 08:51:36 +00:00
Nawaz Dhandala
fd3f75e4e2 refactor: Consolidate API fetching logic for alerts, incidents, and episodes; streamline hooks to use unified fetching methods 2026-02-13 08:46:59 +00:00
Nawaz Dhandala
43fc5acdda Refactor HomeScreen to consolidate project counts using useAllProjectCounts hook; remove unused hooks and simplify loading logic
Remove ProjectSelectionScreen as it is no longer needed; integrate project selection into other screens

Update IncidentDetailScreen and IncidentEpisodeDetailScreen to directly receive projectId from route params

Refactor IncidentsScreen to utilize useAllProjectIncidents, useAllProjectIncidentEpisodes, and useAllProjectIncidentStates hooks for better data management

Add new hooks: useAllProjectCounts, useAllProjectAlerts, useAllProjectAlertEpisodes, useAllProjectAlertStates, useAllProjectIncidentEpisodes, and useAllProjectIncidentStates for improved data fetching and state management

Remove useProject hook usage from SettingsScreen and adjust project-related logic accordingly
2026-02-13 08:39:24 +00:00
simlarsen
c7ca6138f3 chore: npm audit fix 2026-02-13 02:34:23 +00:00
Nawaz Dhandala
87475b00c4 Enhance UI components and improve accessibility across multiple screens
- Added SectionHeader component for consistent section titles in IncidentEpisodeDetailScreen.
- Updated styles for various text elements to improve readability and accessibility.
- Refined button styles in LoginScreen and ServerUrlScreen for better visual feedback.
- Introduced focus states for input fields in LoginScreen and ServerUrlScreen.
- Enhanced ProjectSelectionScreen with improved project item display.
- Implemented SectionCard component in SettingsScreen for better layout organization.
- Updated color tokens in theme for better visual consistency and added new accent colors.
- Adjusted Tailwind CSS configuration for improved shadow effects and new color variables.
2026-02-12 22:36:25 +00:00
Nawaz Dhandala
d5613cc4bd refactor: Integrate Ionicons for improved iconography in EmptyState, BiometricLockScreen, HomeScreen, and SettingsScreen 2026-02-12 22:19:24 +00:00
Nawaz Dhandala
b1c9d9a645 refactor: Add Ionicons for tab bar icons in MainTabNavigator 2026-02-12 22:17:33 +00:00
Nawaz Dhandala
01c6101ae9 Refactor navigation and screens for incidents and alerts
- Removed AlertEpisodesStackNavigator and IncidentEpisodesStackNavigator.
- Integrated AlertEpisodeDetailScreen and IncidentEpisodeDetailScreen into AlertsStackNavigator and IncidentsStackNavigator respectively.
- Updated MainTabNavigator to remove references to the deleted stack navigators.
- Adjusted RootNavigator linking to reflect the new navigation structure.
- Modified types to remove unused stack parameter lists.
- Updated handlers for notifications to navigate to the correct screens.
- Refactored AlertsScreen and IncidentsScreen to support segmented control for toggling between alerts and episodes.
- Added SegmentedControl component for better UI navigation.
- Cleaned up unused screen components and hooks related to episodes.
2026-02-12 22:14:55 +00:00
Nawaz Dhandala
ec56609bf4 refactor: Update biometric labels for clarity in SettingsScreen 2026-02-12 22:07:44 +00:00
Nawaz Dhandala
e5f652a950 refactor: Remove Notification Preferences screen and related navigation; update settings stack and types accordingly 2026-02-12 22:06:36 +00:00
Nawaz Dhandala
749bd2e41d refactor: Replace react-native-keychain with AsyncStorage for token management 2026-02-12 22:03:50 +00:00
Nawaz Dhandala
cc23416ad8 refactor: Move SafeAreaProvider to wrap the PersistQueryClientProvider for improved layout handling 2026-02-12 21:56:58 +00:00
Nawaz Dhandala
86fda9ba16 refactor: Update Tailwind CSS configuration and clean up imports; remove unused tailwind.config.ts file 2026-02-12 21:54:17 +00:00
Nawaz Dhandala
969983043b feat: Add babel-preset-expo to dependencies for improved Babel configuration 2026-02-12 21:39:30 +00:00
Nawaz Dhandala
2b64dd0b1d Refactor Settings, Login, and Server URL screens to use Tailwind CSS for styling; remove unused styles and theme properties; integrate NativeWind for utility-first styling approach; update theme context to support dark mode; enhance accessibility and maintainability. 2026-02-12 21:31:38 +00:00
Nawaz Dhandala
3a514969dc Refactor UI components to enhance styling and shadows
- Updated background colors for various components to use `backgroundElevated` instead of `backgroundSecondary`.
- Increased padding and border radius for cards and buttons across multiple screens for a more modern look.
- Introduced shadow styles for components to improve depth perception.
- Adjusted header styles in navigators to use `backgroundPrimary` and added subtle borders.
- Added a new `shadows` utility to manage shadow styles consistently across the app.
- Modified text styles for better readability and visual hierarchy.
- Updated spacing constants to include larger values for improved layout.
2026-02-12 21:02:46 +00:00
Nawaz Dhandala
10d006890c Refactor code structure for improved readability and maintainability 2026-02-12 20:46:28 +00:00
Nawaz Dhandala
2cb719d53a feat: Add check to skip localhost instances in OpenSourceDeploymentAPI 2026-02-12 20:34:30 +00:00
Nawaz Dhandala
eafb543371 Merge branch 'master' of https://github.com/OneUptime/oneuptime 2026-02-12 19:35:57 +00:00
Nawaz Dhandala
5800fe4f7a feat: Implement aggressive disk cleanup in release workflow to optimize build space 2026-02-12 18:55:47 +00:00
Nawaz Dhandala
8d3712c36a feat: Enhance probe handling in secrets.yaml with improved logic for existing secrets 2026-02-12 17:05:33 +00:00
Nawaz Dhandala
f8e26246dd refactor: Improve related types handling and add peer dependencies in package-lock.json 2026-02-12 10:33:04 +00:00
Nawaz Dhandala
8560ecab41 chore: Bump version to 9.5.9 2026-02-12 09:14:53 +00:00
Nawaz Dhandala
5b2a6924d9 feat: Enhance DataTypeDetail and data-type.ejs with enriched related types and summary box 2026-02-11 22:51:39 +00:00
Nawaz Dhandala
e047143974 refactor: Improve type annotations and code readability in Dropdown component and migration file 2026-02-11 22:43:09 +00:00
Nawaz Dhandala
d23dc791e2 feat: Enhance data type documentation with category grouping and related types links 2026-02-11 22:38:41 +00:00
Nawaz Dhandala
a4b3b340c8 Add new data types for monitoring criteria and configurations
- Introduced CriteriaFilter, CriteriaIncident, and CriteriaAlert for defining filter conditions and incident configurations.
- Added enums for CheckOn, FilterType, and FilterCondition to specify evaluation aspects and comparison operators.
- Included configurations for various monitor step types: Log, Trace, Metric, and SNMP monitors.
2026-02-11 22:25:59 +00:00
Nawaz Dhandala
2173e4e611 Merge branch 'master' of https://github.com/OneUptime/oneuptime 2026-02-11 22:10:58 +00:00
Nawaz Dhandala
dc19f87404 refactor: Clean up code formatting and improve readability across multiple files 2026-02-11 22:08:17 +00:00
Nawaz Dhandala
a3045c5f26 feat: Implement PermissionPicker component for enhanced permission selection 2026-02-11 22:04:25 +00:00
Nawaz Dhandala
5d6907be97 feat: Enhance dropdown options handling by introducing DropdownOptionGroup support 2026-02-11 21:42:57 +00:00
Nawaz Dhandala
e2ace9fc11 feat: Enhance permissions documentation with quick navigation and category links 2026-02-11 21:18:43 +00:00
Nawaz Dhandala
5a11bf228a feat: Enhance permissions handling by grouping permissions into categories 2026-02-11 21:14:55 +00:00
Nawaz Dhandala
cdd8d5523f Implement feature X to enhance user experience and optimize performance 2026-02-11 21:06:04 +00:00
Nawaz Dhandala
f5029fada7 feat: Refactor TypeToDocPath mapping to use dynamic generation from DataTypes 2026-02-11 20:54:47 +00:00
Nawaz Dhandala
8131c9d42f feat: Swap Data Types and Resources sections in navigation 2026-02-11 20:47:15 +00:00
Nawaz Dhandala
946c7d4c48 feat: Add detailed data type documentation and navigation
- Implemented a new DataTypeDetail service to handle detailed views for data types.
- Created a DataTypeUtil to manage data type definitions and retrieval.
- Added a new EJS template for displaying data type details, including properties, values, and JSON examples.
- Updated navigation to include a section for data types in both desktop and mobile views.
- Introduced a new DataTypes module to encapsulate data type information and improve maintainability.
2026-02-11 20:46:02 +00:00
Simon Larsen
9cbc7d9646 Merge pull request #2293 from OneUptime/chore/npm-audit-fix
chore: npm audit fix
2026-02-11 20:20:52 +00:00
Nawaz Dhandala
72d95871f7 feat: enhance model documentation links in API reference view 2026-02-11 19:24:51 +00:00
Nawaz Dhandala
3545a221bc feat: Add Alert Creation Page and Update Related Components
- Implemented a new page for creating alerts with a form that includes fields for title, description, severity, state, monitor, on-call policy, labels, root cause, and remediation notes.
- Updated the AlertsTable component to conditionally render a "Create Alert" button based on the disableCreate prop.
- Modified the AlertsLayout to optionally hide the side menu when navigating to the create alert page.
- Enhanced routing to include a new route for the alert creation page and adjusted the side menu visibility accordingly.
- Updated PageMap and RouteMap to include the new alert creation route.
- Added migration to update database schema related to incident and alert policies.
2026-02-11 19:17:25 +00:00
Nawaz Dhandala
9a5bcb9f31 feat: add migration for updating constraints and indexes in Incident and Alert OnCallDutyPolicy tables 2026-02-11 18:25:10 +00:00
Nawaz Dhandala
91a4d3601c fix: rename columns and update constraints in AlertOnCallDutyPolicy and IncidentOnCallDutyPolicy migrations 2026-02-11 18:23:43 +00:00
Nawaz Dhandala
ea99dd4873 refactor: update model relationships and descriptions for On-Call Duty Policies in Alert, Incident, and IncidentTemplate models; add migration for database schema changes 2026-02-11 18:16:38 +00:00
Nawaz Dhandala
34863dbcb6 fix: rename "Deleted by User ID" to "Acknowledged by User ID" in OnCallDutyPolicyExecutionLog and UserOnCallLog models 2026-02-11 18:09:28 +00:00
Nawaz Dhandala
effeb3a0b6 fix: update title and description for Monitor ID in Alert model 2026-02-11 18:06:59 +00:00
Nawaz Dhandala
86bdcb416a fix: await token refresh before retrying original request in response interceptor 2026-02-11 17:38:36 +00:00
Nawaz Dhandala
665f194f6d feat: add peer dependency flag to multiple packages in package-lock.json 2026-02-11 15:01:14 +00:00
Nawaz Dhandala
1378445dc5 feat: update dependencies for React and React Native 2026-02-11 14:59:54 +00:00
Nawaz Dhandala
236be5b60e feat: conditionally include prefix properties in alert and incident services 2026-02-11 14:53:57 +00:00
Nawaz Dhandala
256f4334eb Merge branch 'release' 2026-02-11 14:32:22 +00:00
Nawaz Dhandala
28d5ad4292 chore: bump version to 9.5.8 2026-02-11 14:32:03 +00:00
Nawaz Dhandala
5c169ccd5b feat: simplify props handling in Slack alert and incident episode actions 2026-02-11 14:27:26 +00:00
Nawaz Dhandala
e05f15d3f6 feat: refactor episode state update methods for Slack and Microsoft Teams actions 2026-02-11 14:24:50 +00:00
Nawaz Dhandala
de0cbe1f42 feat: update on-call policy notifications in Slack and Teams actions for clarity 2026-02-11 13:36:58 +00:00
Nawaz Dhandala
fc48a0efdb feat: add notification for missing on-call policies in Slack actions 2026-02-11 13:31:02 +00:00
Nawaz Dhandala
e623c973ee feat: enhance incident episode note handling with public/private options 2026-02-11 13:26:00 +00:00
Nawaz Dhandala
8d56287892 feat: update workspace channel retrieval to support incident and alert episodes 2026-02-11 13:09:21 +00:00
Nawaz Dhandala
0950d4288f feat: add episode and incident number prefixes to relevant services and messages 2026-02-11 12:58:23 +00:00
Nawaz Dhandala
56ea1c4690 fix: streamline formatting in various services for consistency 2026-02-11 12:52:15 +00:00
Nawaz Dhandala
08d2b6f5a2 feat: add emoji to button titles in Slack incident episode messages 2026-02-11 12:49:21 +00:00
Nawaz Dhandala
2cabdde5bd feat: add workspace notification handling for alert and incident episode services 2026-02-11 12:45:16 +00:00
Nawaz Dhandala
3e48a706bd feat: enhance workspace notification handling in various services 2026-02-11 12:39:48 +00:00
Nawaz Dhandala
7c672e14a1 feat: add workspace notification handling in Alert and Incident episode services 2026-02-11 12:16:42 +00:00
Nawaz Dhandala
80a3bbac3d Merge branch 'master' into release 2026-02-11 11:46:59 +00:00
simlarsen
25f9b826cf chore: npm audit fix 2026-02-11 02:38:02 +00:00
Nawaz Dhandala
c478e6af30 feat: add openSourceDeployment schema with webhookUrl property 2026-02-10 23:26:06 +00:00
Nawaz Dhandala
555a722732 feat: update expo-device to version 8.0.10 and react-native-screens to version 4.16.0 2026-02-10 23:24:47 +00:00
Nawaz Dhandala
6b5f981424 feat: refactor push token handling and move related functions to pushTokenUtils 2026-02-10 23:23:17 +00:00
Nawaz Dhandala
e0e614cf21 feat: enable new architecture in app.json 2026-02-10 23:17:53 +00:00
Nawaz Dhandala
44cc072d98 feat: add expo-system-ui dependency to package.json and package-lock.json 2026-02-10 23:15:38 +00:00
Nawaz Dhandala
c2c97dae0a feat: add mobile app Android and iOS deployment jobs to release workflow 2026-02-10 23:09:02 +00:00
Nawaz Dhandala
3978374ccb feat: add release signing setup documentation for Android and iOS 2026-02-10 23:08:24 +00:00
Nawaz Dhandala
6950daf10a feat: add workflows for mobile app Android and iOS deployment 2026-02-10 23:06:32 +00:00
Nawaz Dhandala
7a07e669c9 feat: add mobile app compilation and testing workflows 2026-02-10 22:55:57 +00:00
Nawaz Dhandala
67ece0fcca refactor: enhance type safety and improve code readability across multiple files 2026-02-10 22:45:12 +00:00
Nawaz Dhandala
5413e24bd4 refactor: enhance type safety and improve code readability across multiple files
- Updated hooks to return specific types using UseQueryResult for better type safety.
- Refactored various components to explicitly define return types for functions and callbacks.
- Improved type annotations for variables and function parameters in screens and hooks.
- Enhanced readability by restructuring code and ensuring consistent formatting.
- Added missing type imports and ensured proper usage of types from the API.
- Cleaned up unnecessary type assertions and improved overall code clarity.
2026-02-10 22:38:45 +00:00
Nawaz Dhandala
59b3fc0334 Refactor screens and components for improved readability and consistency
- Simplified state management and data fetching in IncidentEpisodeDetailScreen.
- Enhanced code clarity by using arrow functions consistently and removing unnecessary destructuring.
- Improved type annotations across various screens for better TypeScript support.
- Streamlined the rendering of components in IncidentEpisodesScreen and IncidentsScreen.
- Updated NotificationPreferencesScreen to use consistent function signatures and improved readability.
- Refactored ProjectSelectionScreen and SettingsScreen for better structure and clarity.
- Enhanced LoginScreen and ServerUrlScreen with clearer type definitions and improved error handling.
- Updated storage utilities to ensure consistent type usage and improved code clarity.
- Refactored theme context and spacing utilities for better type safety and readability.
- Improved color and date utility functions for better maintainability.
2026-02-10 22:29:37 +00:00
Nawaz Dhandala
2bc72dbdb6 feat: update project view route to use current navigation context 2026-02-10 22:21:19 +00:00
Nawaz Dhandala
15ccf00503 chore: update expo-server-sdk to version 3.15.0; reorganize push notifications documentation
- Updated expo-server-sdk in package.json from 3.10.0 to 3.15.0.
- Deleted outdated firebase-push-notifications.md and created new push-notifications.md for clarity.
- Updated navigation links in Nav.ts and README.md to point to the new push-notifications documentation.
2026-02-10 22:07:39 +00:00
Nawaz Dhandala
b3d73a5523 feat: add notification preferences screen and settings stack navigator
feat: implement notification preferences management with local storage
feat: enhance accessibility for alert and incident actions
feat: integrate haptic feedback for user interactions in various screens
refactor: update navigation structure to include settings and notification preferences
2026-02-10 22:04:37 +00:00
Nawaz Dhandala
43e6291608 feat: enhance incident detail screens with haptic feedback and loading skeletons
- Added haptic feedback on state change actions in IncidentDetailScreen and IncidentEpisodeDetailScreen.
- Replaced ActivityIndicator with SkeletonCard for better loading experience in IncidentDetailScreen and IncidentEpisodeDetailScreen.
- Updated empty state messages in IncidentEpisodesScreen and IncidentsScreen for clarity.
- Refactored SettingsScreen to improve layout and added biometric authentication options.
- Introduced OfflineBanner component to notify users of network issues.
- Created SwipeableCard component for better interaction with list items.
- Implemented useBiometric and useNetworkStatus hooks for managing biometric settings and network status.
- Added BiometricLockScreen for unlocking the app using biometric authentication.
- Introduced preferences storage for theme mode and biometric settings.
2026-02-10 21:54:18 +00:00
Nawaz Dhandala
09d82f64de refactor: remove Firebase Cloud Messaging configuration and related code for push notifications 2026-02-10 21:38:42 +00:00
Nawaz Dhandala
51ed9fc2bb feat: implement push notification registration and unregistration functionality 2026-02-10 21:27:35 +00:00
Nawaz Dhandala
b23ccdcc57 feat: add Incident and Alert Episodes screens and navigators
- Created IncidentEpisodesStackNavigator for managing navigation between incident episodes list and detail screens.
- Implemented AlertEpisodesScreen to display a list of alert episodes with pagination and refresh functionality.
- Developed AlertEpisodeDetailScreen to show detailed information about a specific alert episode, including state changes and internal notes.
- Added IncidentEpisodesScreen to display a list of incident episodes with similar functionality to the alert episodes screen.
- Created IncidentEpisodeDetailScreen for detailed view of incident episodes, including state management and note-taking features.
- Integrated hooks for fetching data related to alert and incident episodes.
- Added UI components for displaying episode details, actions, and notes.
2026-02-10 20:34:53 +00:00
Nawaz Dhandala
147e687bac fix: refactor alert and incident state handling to use state IDs for acknowledgment and resolution 2026-02-10 20:15:46 +00:00
Nawaz Dhandala
b84cebcb10 feat: add hooks and screens for alerts and incidents management
- Implemented `useAlertDetail`, `useAlertStates`, and `useAlertStateTimeline` hooks for fetching alert details, states, and timelines.
- Created `useAlerts` and `useUnresolvedAlertCount` hooks for managing alerts list and unresolved alert counts.
- Developed `useIncidentDetail`, `useIncidentStates`, and `useIncidentStateTimeline` hooks for incident management.
- Added `useIncidents` and `useUnresolvedIncidentCount` hooks for fetching incidents and unresolved incident counts.
- Introduced `ProjectProvider` and `useProject` hook for managing project selection and state.
- Created `AlertsStackNavigator` and `IncidentsStackNavigator` for navigation between alerts and incidents screens.
- Developed `AlertDetailScreen` and `IncidentDetailScreen` for displaying detailed information about alerts and incidents.
- Added `ProjectSelectionScreen` for selecting projects with loading and error handling.
- Implemented utility functions for color conversion and date formatting.
2026-02-10 20:10:37 +00:00
Nawaz Dhandala
7374e3bf9a fix: update UI/UX design philosophy to emphasize native app experience 2026-02-10 18:00:54 +00:00
Nawaz Dhandala
413ba90b02 fix: correct property access for miscData in login function 2026-02-10 17:59:55 +00:00
Nawaz Dhandala
2fd61385bd fix: update login function to structure email and password as objects 2026-02-10 17:58:04 +00:00
Nawaz Dhandala
822bc9f8d5 fix: update newArchEnabled to false and modify start scripts in package.json 2026-02-10 17:54:43 +00:00
Nawaz Dhandala
e53a490606 Merge branch 'master' into release 2026-02-10 16:39:21 +00:00
Nawaz Dhandala
cc53460e7a feat: add README.md for OneUptime Mobile App setup and configuration 2026-02-10 16:01:51 +00:00
Nawaz Dhandala
7d6e0488ba feat: add Firebase Cloud Messaging configuration for native push notifications 2026-02-10 15:58:12 +00:00
Nawaz Dhandala
385a0fb9e5 fix: change deviceType property type to PushDeviceType in UserPush model 2026-02-10 15:34:21 +00:00
Nawaz Dhandala
584b79f48c Merge branch 'master' into mob-phase-1 2026-02-10 15:26:41 +00:00
Nawaz Dhandala
92901b1647 feat: add openSourceDeployment schema and update values.yaml documentation 2026-02-10 15:23:15 +00:00
Nawaz Dhandala
bcbc4f6d99 chore: bump version to 9.5.7 2026-02-10 15:00:30 +00:00
Nawaz Dhandala
04dd1260ac style: improve code formatting for better readability in Register and BasicForm components 2026-02-10 15:00:08 +00:00
Nawaz Dhandala
882f9f6ae4 feat: add support for full row spanning in form fields and conditionally display notification checkbox based on billing status 2026-02-10 14:58:51 +00:00
Nawaz Dhandala
549dc3546b refactor: clean up code formatting and improve readability in Authentication and OpenSourceDeploymentAPI 2026-02-10 14:14:33 +00:00
Nawaz Dhandala
25edcf7d9b feat: add migration for OpenSourceDeployment table and update OnCallDutyPolicyScheduleLayer defaults 2026-02-10 14:13:07 +00:00
Nawaz Dhandala
46378fc3db Merge branch 'master' of https://github.com/OneUptime/oneuptime 2026-02-10 14:11:31 +00:00
Nawaz Dhandala
f9f5bff4ce feat: add Open Source Deployment webhook support and related configuration 2026-02-10 14:11:03 +00:00
Nawaz Dhandala
12b78249c5 feat: rename version field to oneuptimeVersion in OpenSourceDeployment model and API 2026-02-10 13:57:55 +00:00
Nawaz Dhandala
f8cbc3a551 feat: implement Open Source Deployment registration and related database schema 2026-02-10 13:50:56 +00:00
Simon Larsen
670b984cee Merge pull request #2288 from OneUptime/episode-resolve
Episode resolve
2026-02-10 13:29:17 +00:00
Nawaz Dhandala
e677e54ea9 feat: update migration and services to handle allIncidentsResolvedAt and allAlertsResolvedAt fields 2026-02-10 13:28:18 +00:00
Nawaz Dhandala
928a2589c2 feat: remove workspaceProjectAuthTokenId from WorkspaceNotificationRule in migration 2026-02-10 13:20:40 +00:00
Nawaz Dhandala
45f7a86888 Merge branch 'master' of https://github.com/OneUptime/oneuptime 2026-02-10 13:12:42 +00:00
Nawaz Dhandala
0fa7848ab9 feat: add migration for allIncidentsResolvedAt and allAlertsResolvedAt fields with index creation 2026-02-10 13:12:10 +00:00
Nawaz Dhandala
ae6e49da8f feat: add allAlertsResolvedAt and allIncidentsResolvedAt fields for resolve delay calculations 2026-02-10 13:08:42 +00:00
Simon Larsen
e80e22b1fa Merge pull request #2285 from OneUptime/inc-episode
feat(AutoResolve): implement resolve delay logic based on incident gr…
2026-02-10 12:42:17 +00:00
Simon Larsen
2adefd1cee Merge pull request #2287 from OneUptime/chore/npm-audit-fix
chore: npm audit fix
2026-02-10 11:17:17 +00:00
simlarsen
36cbb3159a chore: npm audit fix 2026-02-10 02:40:36 +00:00
Nawaz Dhandala
54909116b9 mobile phase 1 2026-02-09 23:45:17 +00:00
Nawaz Dhandala
4582f6100a feat(MobileApp): enhance UI/UX design philosophy with modern aesthetics and user experience considerations 2026-02-09 22:49:00 +00:00
Nawaz Dhandala
49a01eca8c feat(MobileApp): add UI/UX design philosophy and core design principles to the design document 2026-02-09 22:47:15 +00:00
Nawaz Dhandala
349df0e181 feat(MobileApp): enhance multi-project support with project badges and filters in UI 2026-02-09 22:43:03 +00:00
Nawaz Dhandala
c52116bec1 feat(MobileApp): add design document for OneUptime On-Call mobile app 2026-02-09 22:32:55 +00:00
Nawaz Dhandala
098a18005f feat(ResolveInactiveEpisodes): implement inactivity timeout logic based on incident grouping rules 2026-02-09 22:20:42 +00:00
Nawaz Dhandala
6dbcd69ecd feat(AutoResolve): implement resolve delay logic based on incident grouping rules 2026-02-09 21:49:49 +00:00
Nawaz Dhandala
09a6827709 refactor(Service): streamline incident count template replacement 2026-02-09 21:16:52 +00:00
Nawaz Dhandala
dbb1fa6c18 feat(IncidentEpisodeService): enhance incident count update with dynamic title and description templates 2026-02-09 21:15:56 +00:00
Nawaz Dhandala
cd450bc3b6 feat(migrations): set default value of groupByMonitor to false for Alert and Incident grouping rules 2026-02-09 20:15:15 +00:00
Nawaz Dhandala
047195116d feat(migrations): optimize backfill queries to use MAX instead of COUNT for performance and accuracy 2026-02-09 19:27:12 +00:00
Nawaz Dhandala
564f21388b chore(VERSION): bump version to 9.5.6 2026-02-09 18:21:05 +00:00
Nawaz Dhandala
c69d7c949e feat(templates): reorder service and autoscaler definitions in Helm chart templates 2026-02-09 18:04:54 +00:00
Nawaz Dhandala
dd47b9c3a9 feat(e2e-tests): add CRUD and idempotency tests for oneuptime_file resource 2026-02-09 14:46:40 +00:00
Nawaz Dhandala
ce731cb489 Merge branch 'master' of https://github.com/OneUptime/oneuptime 2026-02-09 14:41:34 +00:00
Nawaz Dhandala
f725fdd2d9 feat(ResourceGenerator): implement no-op methods for read, update, and delete operations 2026-02-09 14:41:32 +00:00
Simon Larsen
1aec570c83 Merge pull request #2280 from OneUptime/feat/readme-ai-copilot
docs: add AI Copilot section to README
2026-02-09 14:32:57 +00:00
Jamie Mallers
97b7e15ece docs: add AI Copilot section to README
Highlight the AI agent capabilities - auto-detection, root cause analysis,
and automated code fix PRs. This is our key differentiator and was missing
from the README.
2026-02-09 14:31:03 +00:00
Nawaz Dhandala
7cdac5fe66 Merge branch 'master' of https://github.com/OneUptime/oneuptime 2026-02-09 14:28:11 +00:00
Nawaz Dhandala
4add175070 refactor(Monitor): improve code readability by consolidating API ping parameters
refactor(Migration): update comments for clarity and maintainability
fix(MarkdownEditor): adjust code block formatting for consistency in documentation
2026-02-09 14:28:09 +00:00
Nawaz Dhandala
711cfd2f6b feat(MonitorUtil): add tests for URL placeholder resolution and update method visibility 2026-02-09 14:21:05 +00:00
Nawaz Dhandala
6869ee670a fix(VMUtil): skip replacement if variable is not found in storageMap 2026-02-09 14:17:20 +00:00
Nawaz Dhandala
c4d978cc3b feat: add API and Website monitor documentation with dynamic URL placeholders 2026-02-09 14:11:02 +00:00
Simon Larsen
1dffc2fbbe Merge pull request #2279 from OneUptime/feat/ga4-signup-tracking
feat(analytics): add GA4 sign_up and page view tracking
2026-02-09 13:56:18 +00:00
Jamie Mallers
6b0756cd3a feat(analytics): add GA4 sign_up and page view tracking
- Fire sign_up event on successful registration (Accounts/Register.tsx)
- Fire page_view_pricing event on pricing page load
- Fire page_view_demo event on demo page load
- All events use dataLayer.push for GTM/GA4 compatibility
2026-02-09 13:54:44 +00:00
Simon Larsen
fbfa7747e0 Merge pull request #2278 from OneUptime/fluentbit
Fluentbit
2026-02-09 13:14:59 +00:00
Nawaz Dhandala
a7c38dcbf2 refactor(FluentLogsIngestService): improve code readability by formatting function arguments and object properties 2026-02-09 13:14:39 +00:00
Nawaz Dhandala
6b8dd9e8b5 feat(FluentLogsIngestService): enhance log ingestion with structured fields and attributes extraction
test(FluentLogsIngestService): add comprehensive tests for log normalization and attribute extraction
2026-02-09 13:05:52 +00:00
Nawaz Dhandala
c5e7429b3d chore(VERSION): bump version to 9.5.5 2026-02-09 11:54:17 +00:00
Nawaz Dhandala
13ccee4e69 Merge branch 'master' of https://github.com/OneUptime/oneuptime 2026-02-09 11:53:31 +00:00
Nawaz Dhandala
f9c9ac5ef0 fix(Migration): optimize backfill queries for project counters using JOINs 2026-02-09 11:53:29 +00:00
Nawaz Dhandala
10654a0a04 Merge branch 'master' of https://github.com/OneUptime/oneuptime 2026-02-09 11:07:56 +00:00
Nawaz Dhandala
0d900dca78 fix(StatusPageDomain): change subdomain requirement from required to optional 2026-02-09 11:07:24 +00:00
Nawaz Dhandala
bf5846e7f4 fix(ProductCompare): improve question formatting for clarity in FAQ section 2026-02-08 22:25:06 +00:00
Nawaz Dhandala
9fa48c7a25 chore(VERSION): bump version to 9.5.4 2026-02-08 22:02:16 +00:00
Simon Larsen
9efb070334 Merge pull request #2276 from OneUptime/fix/seo-better-uptime-enhanced
SEO: Add FAQ schema to compare pages + enhance Better Uptime content
2026-02-08 18:46:52 +00:00
Nawaz Dhandala
85e7dd1150 fix(sitemap): update comment format for clarity on blog tag sitemaps removal 2026-02-08 18:16:51 +00:00
Nawaz Dhandala
072f162b6e fix(Project): update create permissions to allow User role 2026-02-08 18:12:26 +00:00
Nawaz Dhandala
9e01b0b75a fix(playwright): install browsers to a fixed path for runtime accessibility 2026-02-08 17:56:59 +00:00
Nawaz Dhandala
3d7b98d1ee fix(deps): update net-snmp to version 3.26.1 2026-02-08 15:37:41 +00:00
Jamie Mallers
62f6900dd2 Merge origin/master - resolve conflict in product-compare.ejs
Keep both:
- Updated meta description from master
- FAQ schema for rich snippets from this branch
2026-02-08 15:20:49 +00:00
Jamie Mallers
51cf4a88bf SEO: Add FAQ schema + enhance Better Uptime comparison content
Changes:
1. Add JSON-LD FAQ schema to all compare pages for rich snippets
2. Expand Better Uptime FAQs from 4 to 8 questions
3. Add more detailed answers covering:
   - Better Uptime → Better Stack rebrand context
   - Self-hosting advantages
   - Detailed pricing comparison
   - Feature differences

Target: /compare/better-uptime at position 13.5 for 'better uptime' query
Expected: Rich snippet eligibility + better content relevance
2026-02-08 09:21:54 +00:00
Simon Larsen
0dfd38d263 Merge pull request #2275 from OneUptime/fix/seo-compare-page-titles
fix(seo): improve compare page title tags for better CTR
2026-02-07 20:44:53 +00:00
Simon Larsen
66424eee24 Merge pull request #2274 from OneUptime/fix/seo-meta-descriptions
fix(seo): unique meta descriptions for product pages
2026-02-07 20:44:14 +00:00
Simon Larsen
93adee4b16 Merge pull request #2273 from OneUptime/fix/seo-noindex-tag-pages
fix(seo): noindex tag pages and remove from sitemap
2026-02-07 20:42:57 +00:00
Jamie Mallers
d7efe2445c fix(seo): improve compare page title tags for better CTR
Changed title pattern from:
'OneUptime vs [Product]: Open-Source Alternative | 2026 Comparison'

To:
'[Product] Alternative - OneUptime | Open Source | 2026 Comparison'

Rationale:
- Puts competitor name first (matches search intent)
- 'Alternative' keyword prominent (what users search for)
- Shorter, more scannable

Also improved meta description to be more action-oriented with clear value props.

Targets: /compare/better-uptime (position 13.5, 803 impressions)
2026-02-07 17:01:18 +00:00
Jamie Mallers
1bf4c52518 fix(seo): unique meta descriptions for product pages
Multiple product pages had identical generic meta descriptions:
'OneUptime monitors websites, APIs, and servers...'

This hurt CTR because Google showed the same text for different pages.

Updated with unique, keyword-rich descriptions:
- /product/status-page: Focus on free, unlimited subscribers, Statuspage alternative
- /product/monitoring: Focus on global locations, alerts, Datadog alternative
- /product/incident-management: Focus on features, integrations
- /product/on-call: Focus on rotations, escalations, PagerDuty alternative
- /about: Focus on open source, GitHub stars, mission

Expected impact: Improved CTR from search results
2026-02-07 16:53:21 +00:00
Jamie Mallers
8348bf6897 fix(seo): noindex tag pages and remove from sitemap
Google Search Console shows 0/10,519 pages indexed. Root cause: 5,000+
thin tag pages are diluting site quality signals and consuming crawl budget.

Changes:
- Add noindex,follow meta tag to blog tag pages (ListByTag.ejs)
- Remove tag sitemaps from sitemap index (Sitemap.ts)

This tells Google to:
1. Stop trying to index tag pages (they're thin content)
2. Still follow links on those pages to discover real content
3. Focus crawl budget on valuable pages (blog posts, product pages)

Expected impact:
- Improved crawl budget efficiency
- Better quality signals for the domain
- Gradual improvement in indexing of valuable pages
2026-02-07 15:49:51 +00:00
Simon Larsen
7f2192206f Merge pull request #2272 from OneUptime/prefix-alert-number
Prefix alert number
2026-02-06 20:50:57 +00:00
Nawaz Dhandala
ddf7636965 Set default number prefixes for incident, alert, and maintenance numbers in ProjectService 2026-02-06 20:48:02 +00:00
Nawaz Dhandala
52514fbb7e Rename parameter onCreate to _onCreate in onCreateSuccess method for clarity 2026-02-06 20:40:21 +00:00
Nawaz Dhandala
2c3521561d Add type annotations to fix ESLint typedef errors for numberResult variables
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 20:31:31 +00:00
Nawaz Dhandala
8d6ef5a277 Refactor alert and incident number display formatting
- Updated multiple components and worker jobs to improve the formatting of alert and incident numbers.
- Ensured consistent use of conditional rendering for alert and incident numbers with prefixes.
- Enhanced readability by using multiline return statements for JSX elements.
2026-02-06 20:27:35 +00:00
Nawaz Dhandala
726ae7ef98 Fix prefix display in UI tables and detail views by adding WithPrefix to selectMoreFields
ModelTable/CardModelDetail only fetch the first key from column field objects.
The WithPrefix fields must be in selectMoreFields to be included in the API query.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 20:19:52 +00:00
Nawaz Dhandala
fde974d968 Add alertNumberWithPrefix support to AlertEpisodeMemberService and UserNotificationRuleService
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 20:08:08 +00:00
Nawaz Dhandala
eae5e026fa Add prefix support to monitor evaluation log incident/alert number display
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 20:00:44 +00:00
Nawaz Dhandala
5b01743e74 Add alertNumberWithPrefix support to AlertOwners Worker notification files
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 19:53:01 +00:00
Nawaz Dhandala
2cfc6a5e68 feat: Add migration for new prefix fields in Project, Incident, Alert, and ScheduledMaintenance tables 2026-02-06 19:46:40 +00:00
Nawaz Dhandala
0976df1bee feat: Add custom prefixes for alert, incident, and scheduled maintenance numbers
- Implemented functionality to allow users to set custom prefixes for alert numbers and alert episode numbers in the AlertMoreSettings component.
- Added similar functionality for incident numbers and incident episode numbers in the IncidentMoreSettings component.
- Introduced custom prefixes for scheduled maintenance numbers in the ScheduledMaintenanceMoreSettings component.
- Updated various notification jobs to utilize the new prefix settings for alert, incident, and scheduled maintenance numbers.
- Ensured backward compatibility by maintaining the default '#' prefix when no custom prefix is provided.
2026-02-06 19:43:11 +00:00
Nawaz Dhandala
caa59aea7e Refactor Analytics event tracking for consistent string quoting 2026-02-06 14:46:44 +00:00
Nawaz Dhandala
ab5e0ec3c4 Merge branch 'master' of https://github.com/OneUptime/oneuptime 2026-02-06 14:43:21 +00:00
Nawaz Dhandala
f4eda526c5 Remove semaphore mutex implementation from AlertEpisodeService and IncidentEpisodeService for simplified episode creation 2026-02-06 14:43:19 +00:00
Nawaz Dhandala
19f347a826 Remove semaphore mutex implementation from AlertService, IncidentService, and ScheduledMaintenanceService for simplified alert and incident creation 2026-02-06 14:39:49 +00:00
Nawaz Dhandala
7eb84c2fb0 Refactor increment methods in ProjectService to use atomicIncrementColumnValueByOne for better code reuse 2026-02-06 14:32:30 +00:00
Nawaz Dhandala
a27f3953ab Add counters for incidents, alerts, and scheduled maintenance to Project model and implement corresponding increment methods 2026-02-06 14:30:16 +00:00
Simon Larsen
4ec162208b Merge pull request #2269 from OneUptime/fix/add-ga4-conversion-tracking
Add GA4 conversion tracking for demo bookings and CTA clicks
2026-02-06 00:00:01 +00:00
Jamie Mallers
13482b13d7 Add GA4 tracking to Analytics module for signup and all events
- All events captured via Analytics.capture() now also push to GA4 dataLayer
- This enables tracking signups (accounts/register) and other events in GA4
- Works alongside existing PostHog tracking
2026-02-05 23:52:33 +00:00
Jamie Mallers
69c0253862 Add GA4 conversion tracking for demo bookings and CTA clicks
- Add GA4 gtag event tracking alongside PostHog for demo bookings
- Add dataLayer push for GTM compatibility
- Add CTA click tracking for 'Get started' and 'Request demo' buttons
- Enable conversion funnel analysis in Google Analytics

This fixes the issue where GA4 Key Events showed 0 conversions
despite demos being booked (only tracked in PostHog previously).
2026-02-05 23:42:10 +00:00
Nawaz Dhandala
92d8b7b425 Add computed property to various database models 2026-02-05 15:23:06 +00:00
Nawaz Dhandala
0ef053dc3d Add computed property to schedule next event column in ScheduledMaintenanceTemplate 2026-02-05 13:52:34 +00:00
Nawaz Dhandala
fa0bd99bc8 Bump version to 9.5.3 2026-02-05 12:58:30 +00:00
Simon Larsen
68b6ca9fd3 Merge pull request #2267 from OneUptime/episode-status-page
Episode status page
2026-02-05 12:58:03 +00:00
Simon Larsen
cd9b711ee4 Merge pull request #2268 from OneUptime/chore/npm-audit-fix
chore: npm audit fix
2026-02-05 12:56:59 +00:00
Nawaz Dhandala
b05d1652e1 Implement logic to mark episode public notes and state timelines as Skipped for subscriber notifications 2026-02-05 12:52:55 +00:00
Nawaz Dhandala
d971573db0 Refactor code for improved readability and consistency
- Adjusted formatting in PublicNote.tsx for better alignment of imports and function parameters.
- Reformatted episode monitor extraction in Detail.tsx for clarity.
- Enhanced readability of episodes data parsing in List.tsx.
2026-02-05 12:48:13 +00:00
Nawaz Dhandala
5a11518c31 Add episode settings section to Alert Grouping Rules page 2026-02-05 12:47:09 +00:00
Nawaz Dhandala
ce4f41367b Add episode settings section to Incident Grouping Rules page 2026-02-05 12:44:37 +00:00
Nawaz Dhandala
f553726186 Refactor code structure for improved readability and maintainability 2026-02-05 12:32:56 +00:00
Nawaz Dhandala
1bd746b285 Update icon SVG path for improved rendering and clarity 2026-02-05 12:14:41 +00:00
Nawaz Dhandala
82558fda59 Add episode management features to Status Page
- Implement fetching and displaying active incident episodes on the Status Page.
- Introduce episode public notes and state timelines for enhanced episode details.
- Create EpisodeGroup type to structure episode-related data.
- Update Overview component to integrate episodes alongside incidents.
2026-02-05 12:11:41 +00:00
Nawaz Dhandala
3f1fe2bf1c Preserve monitor data in episodes from raw JSON and update serialization logic 2026-02-05 12:00:57 +00:00
Nawaz Dhandala
d00fa80e47 Add monitor mapping and resource filtering for episode events in Detail component 2026-02-05 11:40:37 +00:00
Nawaz Dhandala
dc4805c3b2 Add settings page for incident episodes with routing and UI integration 2026-02-05 11:27:50 +00:00
Nawaz Dhandala
642fb95209 Replace Toggle components with Checkbox in MonitorCriteriaAlertForm and MonitorCriteriaIncidentForm 2026-02-05 11:19:06 +00:00
Nawaz Dhandala
01baf60b2e Add endpoint and logic for retrieving incident episode public note attachments 2026-02-05 10:52:03 +00:00
simlarsen
225679f5d3 chore: npm audit fix 2026-02-05 02:25:19 +00:00
Nawaz Dhandala
51f16e2213 Add public notes feature for incident episodes with routing and UI integration 2026-02-04 21:55:32 +00:00
Nawaz Dhandala
0af23bbacb Add migration for new StatusPage fields and update index 2026-02-04 20:36:14 +00:00
Nawaz Dhandala
7b446a853c Add migration for StatusPage enhancements and OnCallDutyPolicyScheduleLayer defaults 2026-02-04 20:35:43 +00:00
Nawaz Dhandala
df480577ab Clarify comments regarding incident visibility and episode display logic in StatusPageAPI 2026-02-04 20:33:42 +00:00
Nawaz Dhandala
c031cc2af3 Refactor incident query logic to clarify episode visibility criteria on status page 2026-02-04 20:31:26 +00:00
Nawaz Dhandala
ae17820d0d Add subscriber episode notification templates for incident updates 2026-02-04 20:28:37 +00:00
Nawaz Dhandala
f2f3900506 Refactor notification templates and job scripts to replace "Episode" with "Incident" for consistency across the application. 2026-02-04 20:26:49 +00:00
Nawaz Dhandala
4c8b92144c Refactor code for improved readability and consistency: adjust formatting, enhance type annotations, and streamline notification logging in various files. 2026-02-04 20:20:08 +00:00
Nawaz Dhandala
4c45e16f56 Refactor episode handling: remove EpisodeDetail component and update routing; enhance incident and episode event item retrieval 2026-02-04 20:13:25 +00:00
Nawaz Dhandala
53e39724e7 Add episode management features to status page: enable episode display, history, and labels 2026-02-04 20:09:01 +00:00
Nawaz Dhandala
849882d868 Add notification jobs for incident episode public notes and state timelines
- Implemented SendNotificationToSubscribers job for IncidentEpisodePublicNote to notify subscribers about new public notes added to episodes.
- Implemented SendNotificationToSubscribers job for IncidentEpisodeStateTimeline to notify subscribers about state changes of episodes.
- Both jobs include logic for fetching relevant episodes, monitors, and subscribers, and sending notifications via email, SMS, Slack, and Microsoft Teams.
- Added error handling and logging for better traceability of notification processes.
2026-02-04 19:11:44 +00:00
Nawaz Dhandala
e3f8af83e5 Add showIncidentOnStatusPage feature to CriteriaIncident and update forms 2026-02-04 18:30:06 +00:00
Simon Larsen
18a5559116 Merge pull request #2265 from OneUptime/compare-pages-seo-improvement
Improve compare page SEO: add 'open-source alternative' positioning
2026-02-03 15:20:18 +00:00
Jamie Mallers
3c3ecfc698 Enhance compare pages: stronger positioning and CTAs
- Badge: 'Open Source Alternative' with GitHub link (reinforces positioning)
- H1: 'The Open-Source [Competitor] Alternative' (SEO + clear value prop)
- CTA: 'Start free — no credit card' (removes friction)
- Added trust signal: 'Self-host for free or use our cloud. No vendor lock-in.'

These changes make the compare pages more conversion-focused.
2026-02-03 15:16:51 +00:00
Jamie Mallers
c22f7fec46 Improve compare page SEO: add 'open-source alternative' positioning
- Title: 'OneUptime vs [Competitor]: Open-Source Alternative | 2026 Comparison'
- Meta: Highlights open-source, alternative positioning, and all-in-one value prop
- Targets high-intent search terms: '[competitor] alternative', 'open source [competitor]'

Affects all /compare/* pages (Datadog, PagerDuty, New Relic, Statuspage, etc.)
2026-02-03 15:12:45 +00:00
Simon Larsen
75d473f6d7 Merge pull request #2264 from OneUptime/homepage-messaging-update
Update homepage messaging: lead with open-source positioning
2026-02-03 15:04:40 +00:00
Jamie Mallers
5d8f8e248e Update homepage messaging: lead with open-source positioning
- Title: 'OneUptime | The Open-Source Observability Platform'
- Meta: Focus on unified platform, self-hostable
- H1: 'The Open-Source Observability Platform'
- Sub: Emphasize complete reliability stack (monitoring, incidents, status pages, APM)

Part of GTM refresh to improve SEO and conversion.
2026-02-03 14:54:29 +00:00
Simon Larsen
a7c3ea274f Merge pull request #2263 from OneUptime/chore/npm-audit-fix
chore: npm audit fix
2026-02-03 08:13:39 +00:00
simlarsen
da324f49d9 chore: npm audit fix 2026-02-03 02:29:12 +00:00
Nawaz Dhandala
6fd9223ee9 refactor: Improve code formatting for better readability in UserSettings components 2026-02-02 19:36:32 +00:00
Nawaz Dhandala
c062d651e8 chore: Bump version number to 9.5.2 2026-02-02 19:35:12 +00:00
Nawaz Dhandala
1c264ac5a1 feat: Enhance User Settings with Incident On-Call Rules and related components 2026-02-02 19:31:58 +00:00
Nawaz Dhandala
8923a4bff6 feat: Format MicrosoftTeamsIntegration component for improved readability 2026-02-02 18:29:57 +00:00
Nawaz Dhandala
ab4e0cf85f chore: Bump version number to 9.5.1 2026-02-02 18:29:48 +00:00
Nawaz Dhandala
8f63e93eba feat: Replace ComingSoon component with MicrosoftTeamsIntegration for immediate functionality 2026-02-02 18:26:52 +00:00
Nawaz Dhandala
c06697f299 chore: Clean up whitespace in migration file for better readability 2026-02-02 18:21:51 +00:00
Nawaz Dhandala
2590a8d671 feat: Refactor DashboardSideMenu to use sections for better organization and readability 2026-02-02 18:20:55 +00:00
Nawaz Dhandala
e4eadc297b feat: Update Dashboard SideMenu to rename "Overview" to "Monitors" and conditionally add "Monitor Groups" section 2026-02-02 18:05:45 +00:00
Nawaz Dhandala
d0bf351dc1 feat: Add monitorId column and constraints to log tables in migration 2026-02-02 17:56:22 +00:00
Nawaz Dhandala
3234cc7d09 feat: Add migration for monitorId integration and schema updates across log tables 2026-02-02 17:46:50 +00:00
Nawaz Dhandala
0158271e6a feat: Add monitorId support across notification services and logs, and implement Notification Logs page 2026-02-02 17:44:25 +00:00
Nawaz Dhandala
9d50bf2535 chore: Bump version to 9.5.0 2026-02-01 16:19:28 +00:00
Nawaz Dhandala
c8ca2eacc5 fix: Improve descriptions for note reminder intervals and templates in IncidentSlaRule 2026-02-01 16:18:42 +00:00
Nawaz Dhandala
66699901a7 refactor: Simplify function signatures and improve code readability in Sla.tsx 2026-02-01 16:13:20 +00:00
Nawaz Dhandala
68a33eee2f feat: Enhance SLA management by integrating incident state timeline for accurate response and resolution tracking 2026-02-01 16:04:50 +00:00
Nawaz Dhandala
016793d77d feat: Implement SLA rule management with add and remove functionality in IncidentViewSla 2026-02-01 16:00:52 +00:00
Nawaz Dhandala
7b040b659d fix: Update SlaCard to consider incident resolution status for completion state 2026-02-01 15:56:54 +00:00
Nawaz Dhandala
c6db71f383 fix: Convert modelId and projectId to strings for stable dependency references in IncidentViewSla 2026-02-01 15:50:07 +00:00
Nawaz Dhandala
16923c750b feat: Add canReadOnRelationQuery property to IncidentSlaRule model and enhance SLA timers with real-time updates 2026-02-01 15:49:07 +00:00
Nawaz Dhandala
f57173f43c fix: Update sorting field in SLA tracking to use 'slaStartedAt' instead of 'createdAt' 2026-02-01 15:42:45 +00:00
Nawaz Dhandala
9716d138ea refactor: Update fetchStates function type annotation and simplify error handling in EpisodeAlerts and EpisodeIncidents components 2026-02-01 15:34:18 +00:00
Nawaz Dhandala
8e1b6859f5 feat: Add support for incident episode notification rule type and update default title generation to use 'Untitled Episode' 2026-02-01 15:28:38 +00:00
Nawaz Dhandala
9751dd0d5f refactor: Update episode title generation to remove prefix and use 'Untitled Episode' as default 2026-02-01 15:21:47 +00:00
Nawaz Dhandala
e84bd95f49 feat: Add canReadOnRelationQuery property to Alert and Incident models 2026-02-01 14:10:13 +00:00
Nawaz Dhandala
02b76539ab feat: Fetch and display alert and incident states in episode views 2026-02-01 14:07:46 +00:00
Nawaz Dhandala
c40c33773b feat: Add action buttons and current state display for alerts and incidents in episode views 2026-02-01 13:55:49 +00:00
Nawaz Dhandala
b0cebf9338 refactor: Improve code structure and readability in role assignment components 2026-02-01 13:47:39 +00:00
Nawaz Dhandala
674e35dc70 refactor: Improve code readability by formatting and simplifying filter/map functions in various components 2026-02-01 13:44:07 +00:00
Nawaz Dhandala
d5f42141a0 feat: Enhance Episode Member Role Assignments with user selection and role management 2026-02-01 13:41:54 +00:00
Nawaz Dhandala
8fb6da7d41 feat: Add Episode Member Role Assignments form field for incident grouping rules 2026-02-01 13:39:41 +00:00
Nawaz Dhandala
d14e77ee7f feat: Implement role assignment functionality for incident episodes with UI components 2026-02-01 13:29:31 +00:00
Nawaz Dhandala
71e8a70717 feat: Add IncidentEpisodeRoleMember service and model to BaseAPI feature set 2026-02-01 13:20:21 +00:00
Nawaz Dhandala
370bdc6e21 feat: Refactor MonitorCriteria components to use dedicated UI elements for teams, users, and labels 2026-02-01 13:09:07 +00:00
Nawaz Dhandala
d2a5d037c1 feat: Add support for labels, teams, users, and incident roles in monitor criteria components 2026-02-01 13:02:53 +00:00
Nawaz Dhandala
1ffa87d322 feat: Update title of My On-Call Policies card to reflect active assignments 2026-02-01 12:48:45 +00:00
Nawaz Dhandala
28ffde7983 feat: Enhance My On-Call Policies page with project details and improved UI components 2026-02-01 12:48:17 +00:00
Nawaz Dhandala
4655e207a5 feat: Add My On-Call Policies page and integrate routing 2026-02-01 12:44:00 +00:00
Nawaz Dhandala
62a5b216a0 feat: Update collapsible section titles for clarity in Monitor Criteria forms 2026-02-01 12:33:15 +00:00
Nawaz Dhandala
79fb9d18ca feat: Conditionally render Test Monitor Card based on monitor type 2026-02-01 12:28:23 +00:00
Nawaz Dhandala
ba5e8fdaeb feat: Simplify Monitor Criteria section by removing conditional rendering 2026-02-01 12:26:50 +00:00
Nawaz Dhandala
aa31bbab45 feat: Refactor Ownership & Labels section in MonitorCriteriaIncidentForm with improved dropdown handling 2026-02-01 12:22:27 +00:00
Nawaz Dhandala
3c84365d61 feat: Add reassign functionality for member roles with modal confirmation 2026-02-01 12:13:57 +00:00
Nawaz Dhandala
ae59bd8300 feat: Enhance incident member assignment logic to include primary role assignments for creators 2026-02-01 12:08:34 +00:00
Nawaz Dhandala
b868206e82 feat: Improve user assignment flow with enhanced dropdown handling and feedback 2026-02-01 12:05:23 +00:00
Nawaz Dhandala
ddbf971f1f feat: Reorder Owners item in SideMenu for improved accessibility 2026-02-01 12:01:35 +00:00
Nawaz Dhandala
483578ba4d refactor: Remove unused owner teams and users fields from incident creation 2026-02-01 12:00:41 +00:00
Nawaz Dhandala
614cb4413e feat: Enhance user assignment button with disabled state and tooltip for clarity 2026-02-01 11:57:47 +00:00
Nawaz Dhandala
ad43fc2df2 fix: Adjust icon size in MemberRoleAssignment component for better visibility 2026-02-01 11:54:44 +00:00
Nawaz Dhandala
aa2a6deb9e feat: Refactor role assignments to use useRef for better performance and update role assignment handling in IncidentCreate 2026-02-01 11:51:28 +00:00
Nawaz Dhandala
dcb13bb401 feat: Add showEvenIfPermissionDoesNotExist option to incident roles assignment 2026-02-01 11:40:00 +00:00
Nawaz Dhandala
5260364e91 fix lint 2026-01-30 20:38:36 +00:00
Nawaz Dhandala
1938e620bb feat: Update role assignment label to clarify multiple user assignment for incident roles 2026-01-30 20:32:27 +00:00
Nawaz Dhandala
c3fd71dcd4 feat: Enhance incident role assignment functionality with new IncidentRoleFormField component and support for multiple user assignments 2026-01-30 20:24:34 +00:00
Nawaz Dhandala
aba191c533 feat: Implement validation for primary roles to restrict multiple user assignments and update IncidentRoles component to include toggle for multiple user assignment 2026-01-30 20:09:31 +00:00
Nawaz Dhandala
847c019aea feat: Add canAssignMultipleUsers field to IncidentRole and update related components for multi-user assignment 2026-01-30 19:55:13 +00:00
Nawaz Dhandala
edf05944c1 feat: Add roleIcon to MemberRole and IncidentMemberRoleAssignment for enhanced role representation 2026-01-30 19:48:13 +00:00
Nawaz Dhandala
5293876943 feat: Update MemberRoleAssignment to allow single member assignment per role and improve user dropdown options 2026-01-30 19:42:51 +00:00
Nawaz Dhandala
ca7a702c13 feat: Add createdAt field to user selection in IncidentMemberRoleAssignment 2026-01-30 19:37:42 +00:00
Nawaz Dhandala
4020b4b647 feat: Update SideMenu section titles for clarity and consistency 2026-01-30 19:34:44 +00:00
Nawaz Dhandala
650849f4ad feat: Rename Members page to Roles and implement role assignment component 2026-01-30 19:09:06 +00:00
Nawaz Dhandala
9098261ac0 feat: Enhance user role assignment logic by validating role assignments and filtering available users by role 2026-01-30 19:07:28 +00:00
Nawaz Dhandala
70c6abbb86 feat: Add alert and incident episode counts with real-time updates in DashboardHeader 2026-01-30 14:15:52 +00:00
Nawaz Dhandala
23bc5531f0 feat: Add Incident SLA and Incident SLA Rule services and models to BaseAPI 2026-01-30 14:07:55 +00:00
Nawaz Dhandala
20404458e2 refactor: Remove dynamic imports of IncidentService and IncidentSlaService for improved performance 2026-01-30 14:00:44 +00:00
Nawaz Dhandala
31d3ce949d Refactor and clean up code across multiple files
- Added missing commas in migration index.
- Improved type annotations for dynamic imports in IncidentService and IncidentSlaRuleService.
- Simplified logger debug messages in IncidentSlaService and IncidentSlaRuleService.
- Cleaned up JSX formatting in IconPicker, NotificationBellDropdown, RoleLabel, and Header components.
- Enhanced readability by restructuring long lines and removing unnecessary line breaks in various components.
- Updated error handling in fetch functions within DashboardHeader to use concise catch blocks.
- Refactored UserSettings and Incident routes for better readability.
- Improved code consistency and formatting in CheckSlaBreaches job.
2026-01-30 13:50:50 +00:00
Nawaz Dhandala
61ed224ad0 feat: Add Incident SLA and related tables with migration 2026-01-30 13:39:46 +00:00
Nawaz Dhandala
71ea76ee62 feat: Implement Incident SLA Management System
- Added IncidentSlaStatus enum to define SLA status values.
- Created IncidentSlaRulesPage component for managing SLA rules, including documentation and configuration options.
- Developed IncidentViewSla component to display SLA status and deadlines for incidents.
- Implemented CheckSlaBreaches job to monitor SLA breaches and send notifications.
- Created SendNoteReminders job to automate internal and public note reminders based on SLA rules.
2026-01-30 13:03:48 +00:00
Nawaz Dhandala
7a4a0553ca fix: Update fetchData dependencies to use string representations of incidentId and projectId 2026-01-30 12:26:22 +00:00
Nawaz Dhandala
cb57fa4a07 feat: Add new icon types (Compass, Disc, Grid) to Icon component 2026-01-30 12:20:47 +00:00
Nawaz Dhandala
22bc222689 feat: Refactor IconPicker and RoleLabel components for improved icon handling and styling 2026-01-30 12:12:21 +00:00
Nawaz Dhandala
9d96170c42 feat: Add migration for roleIcon column in IncidentRole and update OnCallDutyPolicyScheduleLayer defaults 2026-01-30 12:03:01 +00:00
Nawaz Dhandala
d0f4d21177 feat: Change roleIcon type from string to IconProp in IncidentRole model 2026-01-30 11:59:18 +00:00
Nawaz Dhandala
e9a2167484 feat: Rename icon to roleIcon in IncidentRole model and update references in IncidentRoles and CustomFields components 2026-01-30 11:59:03 +00:00
Nawaz Dhandala
a8af991a80 feat: Add IconPicker component and integrate icon selection in IncidentRoles 2026-01-30 11:52:21 +00:00
Nawaz Dhandala
8bb3a5b7ac feat: Replace Pill with RoleLabel component in IncidentRoles for enhanced role display 2026-01-30 11:45:22 +00:00
Nawaz Dhandala
d0b1efb660 feat: Add isEditable prop to CustomFieldsDetail and set it in UserView 2026-01-30 11:34:24 +00:00
Nawaz Dhandala
4f3259c3b1 feat: Enhance user profile creation with HTTP response handling and validation 2026-01-30 11:31:42 +00:00
Nawaz Dhandala
30b53a90a4 feat: Reorder profile section in SideMenu for improved organization 2026-01-30 11:31:00 +00:00
Nawaz Dhandala
cf8377ceec feat: Add custom fields management for team members and user profiles
- Introduced new models: ProjectUserProfile and TeamMemberCustomField to manage custom fields for users in projects.
- Implemented services for ProjectUserProfile and TeamMemberCustomField for database interactions.
- Created UI components for managing team member custom fields and user-specific custom fields in settings.
- Updated routing to include new pages for custom fields management.
- Added necessary database migrations for new tables and relationships.
- Enhanced side menu in user settings to navigate to custom fields.
2026-01-30 11:28:00 +00:00
Nawaz Dhandala
ffc7dbc35f feat: Update user notification event type for incident episode creation 2026-01-30 11:01:23 +00:00
Nawaz Dhandala
97fd817db4 feat: Enhance change plan API with payment method validation and error handling 2026-01-30 10:50:19 +00:00
Nawaz Dhandala
aa7caaa193 feat: Add validation for payment provider customer and methods before changing plan 2026-01-30 10:48:08 +00:00
Nawaz Dhandala
a05853ea09 feat: Move NotificationBell component to the right section for improved visibility 2026-01-30 10:45:26 +00:00
Nawaz Dhandala
2ba96c093d feat: Update NotificationBell to improve badge count logic and display based on alert types 2026-01-30 10:43:56 +00:00
Nawaz Dhandala
ed3df77ca4 feat: Refactor NotificationBell and related components for improved alert handling and UI consistency 2026-01-30 10:39:35 +00:00
Nawaz Dhandala
9ec363d222 feat: Implement notification system with NotificationBell and related components 2026-01-30 10:29:33 +00:00
Nawaz Dhandala
a090ec2747 feat: Enhance migration and service files for incident roles and alert grouping
- Updated migration file to include new tables and constraints for incident roles and grouping rules.
- Refactored alert grouping engine service for improved readability.
- Adjusted incident episode role member service for better error logging and code clarity.
- Modified data migration to streamline imports and enhance maintainability.
2026-01-29 22:09:38 +00:00
Nawaz Dhandala
9fde4fece9 feat: Add migration for IncidentEpisodeRoleMember and related grouping rules 2026-01-29 22:03:40 +00:00
Nawaz Dhandala
596798801a feat: Add episode configuration fields and role assignments to incident and alert grouping rules 2026-01-29 21:59:13 +00:00
Nawaz Dhandala
dcc87c46b2 feat: Implement Incident Episode Role Member functionality and UI components 2026-01-29 21:51:49 +00:00
Nawaz Dhandala
33fdabaea3 feat: Add migration to assign default incident roles to existing projects 2026-01-29 21:41:30 +00:00
Nawaz Dhandala
35deea863b feat: Refactor incident member role assignment and improve code readability across components 2026-01-29 21:39:29 +00:00
Nawaz Dhandala
2b2bbbdd55 feat: Add incident member role assignment functionality to MonitorIncident and related components 2026-01-29 21:38:41 +00:00
Nawaz Dhandala
d8cd92c504 feat: Update title and description in ListByTag.ejs for improved SEO 2026-01-29 21:38:17 +00:00
Nawaz Dhandala
f6ef2fa97d feat: Add IncidentMemberRoleAssignment component for managing team member roles in incidents 2026-01-29 21:25:03 +00:00
Nawaz Dhandala
8a86f6a94f feat: Implement auto-assignment of Incident Commander during state changes 2026-01-29 21:14:33 +00:00
Nawaz Dhandala
4b30274915 feat: Add incident member notification system with email and WhatsApp templates 2026-01-29 21:09:44 +00:00
Nawaz Dhandala
01f7d7cc78 feat: Refactor routing and add ActiveIncidents and ActiveAlerts components 2026-01-29 20:59:58 +00:00
Nawaz Dhandala
616e64110a feat: Add MigrationName1769719826928 to implement isPrimaryRole and isDeleteable fields in IncidentRole 2026-01-29 20:51:00 +00:00
Nawaz Dhandala
903a72a4e1 feat: Add isPrimaryRole and isDeleteable fields to IncidentRole model and update related service logic 2026-01-29 20:50:17 +00:00
Nawaz Dhandala
699c1d4341 feat: Update MigrationName1769719135546 to include down method and fix formatting 2026-01-29 20:42:13 +00:00
Nawaz Dhandala
078a4e8180 Merge branch 'incident-roles' 2026-01-29 20:39:52 +00:00
Nawaz Dhandala
29232e7052 feat: Add new migration MigrationName1769719135546 to schema migrations 2026-01-29 20:39:29 +00:00
Nawaz Dhandala
a221f7247c feat: Remove startsAt and endsAt fields from IncidentMember model and update related migration 2026-01-29 20:39:12 +00:00
Simon Larsen
c4b5aca463 Merge pull request #2259 from OneUptime/incident-roles
feat: Add Incident Member and Role Management
2026-01-29 20:32:09 +00:00
Nawaz Dhandala
9de4be6661 chore: Remove unnecessary blank lines in SettingsRoutes component 2026-01-29 20:31:57 +00:00
Simon Larsen
a532dcdd5f Merge pull request #2260 from digitalsparky/patch-1
Fix installation command to ensure it can follow the redirect
2026-01-29 20:20:35 +00:00
Simon Larsen
bde09d2326 Merge pull request #2261 from digitalsparky/patch-2
Modify clone instructions for release branch
2026-01-29 20:19:40 +00:00
Matt Spurrier
b36ac68026 Modify clone instructions for release branch
Updated the instructions to clone the repository with only the release branch.

This will save significant bandwidth and disk space by cloning only the release branch as it exists now, rather than the entire repository with its full history.
2026-01-30 04:02:16 +08:00
Matt Spurrier
200a94692e Fix installation command to ensure it can follow the redirect
The install.The URL is a redirect, for curl to follow it, you must use the -L option, otherwise you get an error.

EG:

root@inf1:~# curl https://oneuptime.com/install.sh | bash
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   107  100   107    0     0    242      0 --:--:-- --:--:-- --:--:--   243
bash: line 1: Found.: command not found
2026-01-30 03:42:58 +08:00
Nawaz Dhandala
3afc3a3302 feat: Add IncidentRole and IncidentMember APIs to BaseAPIFeatureSet 2026-01-29 19:16:23 +00:00
Nawaz Dhandala
fa52c30462 feat: Add Incident Roles page and update routing in incidents and settings 2026-01-29 19:09:19 +00:00
Nawaz Dhandala
c229936d5c Refactor permission arrays in database models to improve formatting consistency
- Updated permission arrays in various database model files to ensure consistent formatting by aligning closing brackets.
- Adjusted the `onBeforeCreate` function in the IncidentMembers component for improved readability.
- Cleaned up descriptions in the Permission helper class for better clarity.
- Added a new migration for IncidentRole and IncidentMember tables with appropriate constraints and indexes.
2026-01-29 18:58:05 +00:00
Nawaz Dhandala
70b2fb8c16 feat: Add IncidentRole and IncidentMember migration with constraints and indexes 2026-01-29 18:54:51 +00:00
Nawaz Dhandala
264613c676 feat: Rename On-Call Users to Members and update related routes and components 2026-01-29 16:52:50 +00:00
Nawaz Dhandala
61209f967f refactor: Remove order field from IncidentRole model and related services 2026-01-29 16:50:59 +00:00
Nawaz Dhandala
f82de89f3f feat: Add Incident Member and Role Management
- Introduced IncidentMember model to manage users assigned to incidents with specific roles.
- Created IncidentRole model to define roles that can be assigned during incident response.
- Implemented IncidentMemberService for CRUD operations on incident members.
- Implemented IncidentRoleService for CRUD operations on incident roles with order management.
- Added OnCallUsers page to display and manage users assigned to incidents.
- Added IncidentRoles settings page to define and manage incident roles.
- Updated RouteMap to include new routes for on-call users and incident roles.
2026-01-29 16:33:52 +00:00
Nawaz Dhandala
b94a095bef feat: add ReadAllProjectResources permission to various models
- Updated TelemetryIngestionKey, TelemetryUsageBilling, User, WhatsAppLog, Workflow, WorkflowLog, WorkflowVariable, WorkspaceNotificationLog, WorkspaceNotificationRule, WorkspaceProjectAuthToken models to include the new ReadAllProjectResources permission in access control settings.
- Introduced ReadAllProjectResources permission in Permission.ts with a description outlining its purpose and scope.
2026-01-29 15:03:22 +00:00
Simon Larsen
c08de3da35 Merge pull request #2258 from OneUptime/monitor-ui
feat: Add support for labels and ownership in Monitor Alert and Incid…
2026-01-29 14:09:25 +00:00
Nawaz Dhandala
748e18fd1b fix: Correct type annotation for updateField function in MonitorCriteriaAlertForm and MonitorCriteriaIncidentForm 2026-01-29 14:09:08 +00:00
Nawaz Dhandala
c0d7c34018 refactor: Improve code readability by formatting multiline expressions and consistent function signatures in Monitor components 2026-01-29 14:04:42 +00:00
Nawaz Dhandala
e249ee6e59 feat: Add support for labels and ownership in Monitor Alert and Incident forms
- Updated MonitorAlert and MonitorIncident classes to handle labels and owner teams/users.
- Enhanced CriteriaAlert and CriteriaIncident types to include labelIds, ownerTeamIds, and ownerUserIds.
- Modified MonitorCriteriaAlertForm and MonitorCriteriaIncidentForm components to support new fields for labels and ownership.
- Integrated dropdown options for labels, teams, and users in MonitorSteps and related components.
- Implemented fetching of labels, teams, and users in MonitorSteps for dynamic dropdowns.
2026-01-29 14:02:55 +00:00
Simon Larsen
9eca0153ce Merge pull request #2257 from OneUptime/monitor-ui
feat: Refactor MonitorStep component to use CollapsibleSection for ad…
2026-01-29 13:34:13 +00:00
Nawaz Dhandala
c4deb0d0b4 feat: Refactor MonitorStep component to use CollapsibleSection for advanced options and improve UI structure
- Introduced CollapsibleSection component for better organization of advanced options in MonitorStep.
- Replaced existing div structures with Card components for improved visual hierarchy.
- Enhanced user experience by adding collapsible sections for API and Website monitor advanced settings.
- Cleaned up code by removing unnecessary comments and consolidating error handling logic.
2026-01-29 13:28:33 +00:00
Simon Larsen
7ecd86eca7 Merge pull request #2256 from elmy-team/fix-missing-redis-existing-secret
Fix missing Redis & ClickHouse existing secrets
2026-01-29 12:09:37 +00:00
Matías Plaza
f3312a2417 fix(helm-chart): add missing existing secret condition for clickhouse 2026-01-29 10:52:33 +01:00
Nawaz Dhandala
e0558a4a0a chore: Bump version to 9.4.13 2026-01-29 09:07:31 +00:00
Nawaz Dhandala
e2a238e3e3 refactor: Simplify imports and improve type annotations in IncidentEpisode services 2026-01-29 09:06:32 +00:00
Nawaz Dhandala
543c62df5a feat: Implement AI-generated postmortem functionality for IncidentEpisode 2026-01-29 09:05:42 +00:00
Matías Plaza
382c838d40 fix(helm-chart): add missing existing secret condition for redis 2026-01-29 10:04:31 +01:00
Nawaz Dhandala
1a88832efc feat: Enhance EpisodePostmortem component with template functionality 2026-01-29 09:01:07 +00:00
Nawaz Dhandala
d1f97a3193 feat: Add canReadOnRelationQuery property to Incident model 2026-01-29 08:58:18 +00:00
Nawaz Dhandala
70a269b662 fix: Update icons in DashboardSideMenu for better clarity 2026-01-29 08:56:23 +00:00
Nawaz Dhandala
a4ae42fd08 feat: Add create episode button to IncidentEpisodesTable component 2026-01-29 08:54:05 +00:00
Simon Larsen
6c0161543a Merge pull request #2255 from OneUptime/snmp-monitor
Snmp monitor
2026-01-29 08:45:54 +00:00
Nawaz Dhandala
4519292cc8 feat: Add migration to rename 'eveluateOverTime' to 'evaluateOverTime' in Monitor table 2026-01-29 08:42:34 +00:00
Nawaz Dhandala
83993fc2a4 fix: Correct spelling of 'evaluateOverTime' in multiple criteria files 2026-01-29 08:38:35 +00:00
Simon Larsen
5340b04b26 Merge pull request #2253 from OneUptime/chore/npm-audit-fix
chore: npm audit fix
2026-01-29 08:28:58 +00:00
Nawaz Dhandala
2a6003e78f refactor: Add type annotations for functions in SnmpOidEditor and SnmpMonitor 2026-01-29 08:28:23 +00:00
Nawaz Dhandala
bfed03a10e refactor: Improve code formatting and readability in SNMP monitor components 2026-01-29 08:24:51 +00:00
Nawaz Dhandala
d99b20327f feat: Update SNMP monitor implementation and add net-snmp dependency 2026-01-29 08:22:04 +00:00
Nawaz Dhandala
3a317a8b55 feat: Enhance E2E test execution with retry mechanism 2026-01-29 08:09:32 +00:00
Nawaz Dhandala
0c64ba30b0 fix: Correct URL construction in generateBlogSitemapXml function 2026-01-29 08:08:40 +00:00
simlarsen
4400a7e5dd chore: npm audit fix 2026-01-29 02:15:00 +00:00
Nawaz Dhandala
45aab853c4 feat: Add SNMP monitor documentation with configuration and usage details 2026-01-28 23:26:10 +00:00
Nawaz Dhandala
b37c13d347 feat: Enhance monitor destination handling for SNMP monitors 2026-01-28 23:21:32 +00:00
Nawaz Dhandala
d13407494b feat: Implement SNMP monitor support with criteria and view components 2026-01-28 23:06:13 +00:00
Nawaz Dhandala
724ab97874 feat: Add SNMP Monitor functionality with v3 authentication support
- Introduced MonitorStepSnmpMonitor interface for SNMP configuration.
- Implemented SNMP version handling with enums for V1, V2c, and V3.
- Created utility classes for handling SNMP OIDs and v3 authentication parameters.
- Developed SNMP Monitor response structure to capture OID responses and status.
- Added UI components for SNMP Monitor configuration including OID editor and v3 auth form.
- Implemented SNMP query logic with retry mechanism and response time tracking.
- Added type definitions for net-snmp library to support SNMP operations.
2026-01-28 22:56:28 +00:00
Nawaz Dhandala
15e2fdcf48 feat: add IncidentGroupingRule service and router integration 2026-01-28 21:10:37 +00:00
Simon Larsen
3a546f9b5a Merge pull request #2254 from OneUptime/incident-episodes
feat: Add Incident Episode Management Pages and Components
2026-01-28 20:32:17 +00:00
Nawaz Dhandala
291a0f12f1 feat: Enhance Incident Grouping Rules Migration and UI Components
- Updated the migration script for IncidentGroupingRule to improve structure and readability.
- Added new foreign key constraints and indexes to enhance database integrity.
- Refactored DeleteAccount component to improve layout and readability.
- Improved placeholder text formatting in IncidentGroupingRules page for better clarity.
- Added ProjectService import in BillingService to resolve circular dependency.
- Refactored condition checks in IncidentGroupingEngineService for better readability.
2026-01-28 20:27:23 +00:00
Nawaz Dhandala
3d62b67bca feat: add IncidentGroupingRule migration with related tables and constraints 2026-01-28 19:55:03 +00:00
Nawaz Dhandala
4c31c2b651 feat: add incidentEpisodeId to multiple service classes 2026-01-28 19:42:44 +00:00
Nawaz Dhandala
acdcb2d5da feat: add IncidentGroupingRuleService and IncidentGroupingRules page
- Implemented IncidentGroupingRuleService to manage incident grouping rules with automatic deletion of old records if billing is enabled.
- Created IncidentGroupingRules page for managing incident grouping rules, including detailed documentation on grouping logic, match criteria, and configuration options.
- Added UI components for creating, editing, and displaying incident grouping rules with various filtering and grouping options.
2026-01-28 19:31:13 +00:00
Nawaz Dhandala
43084263ab feat: Add tabs for Incident and Incident Episodes in Microsoft Teams and Slack pages 2026-01-28 19:10:10 +00:00
Nawaz Dhandala
1bbc953462 Refactor and clean up code across multiple services and components
- Added missing commas in migration index.
- Removed unused imports in IncidentEpisodeService and UserNotificationRuleService.
- Simplified conditional statements and improved code readability in various services.
- Adjusted formatting for better consistency in code style.
- Enhanced error handling and logging in MicrosoftTeamsIncidentEpisodeActions.
- Updated documentation text in IncidentEpisodeDocs for clarity.
- Improved API call structure in IncidentEpisodesTable and IncidentEpisodeFeed.
- Refactored SlackIncidentEpisodeActions for better readability.
- Cleaned up unnecessary code and improved formatting in various components.
2026-01-28 18:49:22 +00:00
Nawaz Dhandala
7cef3956e8 feat: Add IncidentEpisode migration with related tables and constraints 2026-01-28 18:48:36 +00:00
Nawaz Dhandala
4904a535d1 feat: Add Active Incident Episodes page and integrate with SideMenu 2026-01-28 18:47:26 +00:00
Nawaz Dhandala
5db511036e feat: Add notification handling for Incident Episodes and enhance Push Notification utility 2026-01-28 18:40:39 +00:00
Nawaz Dhandala
8b9023d93d feat: Add incident episode auto-resolution and notification jobs
- Implemented AutoResolve job to automatically resolve inactive incident episodes.
- Created ResolveInactiveEpisodes job to resolve episodes inactive for over 24 hours.
- Added SendCreatedResourceNotification job to notify owners of newly created incident episodes.
- Developed SendNotePostedNotification job to inform owners when a note is posted on an incident episode.
- Introduced SendOwnerAddedNotification job to notify users when they are added as owners of an incident episode.
- Implemented SendStateChangeNotification job to alert owners of state changes in incident episodes.
2026-01-28 18:30:45 +00:00
Nawaz Dhandala
0546d1fb12 feat: Enhance User Notification and OnCall Duty Policy services to support Incident Episode handling 2026-01-28 18:13:09 +00:00
Nawaz Dhandala
d523ae822d feat: Implement Incident Episode services, routes, and notification templates 2026-01-28 18:07:36 +00:00
Nawaz Dhandala
9fd781c083 feat: Add Slack and Workspace message handling for Incident Episodes
- Implemented SlackIncidentEpisodeMessages class to create message blocks for incident episodes in Slack.
- Added IncidentEpisodeWorkspaceMessages class to handle workspace notifications and message blocks for incident episodes.
- Created IncidentEpisodeDocs component to provide a comprehensive guide on incident grouping, including lifecycle, setup steps, and best practices.
2026-01-28 18:00:10 +00:00
Nawaz Dhandala
8d743dbb59 feat: Add Incident Episode creation functionality and related routes 2026-01-28 17:19:15 +00:00
Nawaz Dhandala
5b3e97c10d feat: Add Incident Episode Management Pages and Components
- Implemented Episode Internal Note component for managing private notes related to incident episodes.
- Created Layout component for the Incident Episode view, integrating side menu and breadcrumb navigation.
- Developed Owners component to manage teams and users associated with incident episodes.
- Added Postmortem component for documenting postmortem analyses of incidents.
- Introduced Remediation component for capturing remediation notes for incidents.
- Created Root Cause component to document the root causes of incidents.
- Developed SideMenu for navigating through various sections of the Incident Episode view.
- Implemented State Timeline component to track the status changes of incident episodes.
- Added Episodes and Unresolved Episodes pages for listing all incident episodes and unresolved ones respectively.
2026-01-28 17:10:08 +00:00
Nawaz Dhandala
252a81c9ae chore: bump version to 9.4.12 2026-01-28 15:50:27 +00:00
Nawaz Dhandala
e6b414a94b feat: enhance project fetching in NewAlerts and NewIncidents components with ListResult type 2026-01-28 15:50:15 +00:00
Nawaz Dhandala
f521091f8e feat: update project filter to use projectId and change filter type to EntityArray in NewAlerts and NewIncidents components 2026-01-28 15:46:08 +00:00
Nawaz Dhandala
19e112a8a8 feat: add project filter functionality to NewAlerts and NewIncidents components 2026-01-28 15:33:35 +00:00
Nawaz Dhandala
84dd084dae feat: add saveFilterProps to NewAlerts and NewIncidents components for improved table filtering 2026-01-28 13:08:56 +00:00
Nawaz Dhandala
439c1f8716 style: format code for improved readability in EpisodeCreate component 2026-01-28 12:59:00 +00:00
Nawaz Dhandala
647b713375 feat: add episode creation functionality with form and routing 2026-01-28 12:58:16 +00:00
Nawaz Dhandala
a2e6b7a4fc feat: add Stripe type definitions and improve type safety in Billing and Project services 2026-01-28 12:09:41 +00:00
Nawaz Dhandala
b06bc71a2c refactor: improve logging and code formatting in billing and project services 2026-01-28 12:08:01 +00:00
Nawaz Dhandala
367a80c413 fix: remove unused status field from invoice template and billing service 2026-01-28 12:05:03 +00:00
Nawaz Dhandala
f49e4bd5d0 feat: implement invoice email notification system with template and email handling 2026-01-28 12:01:28 +00:00
Nawaz Dhandala
a74a7e0a9a feat: enhance billing functionality with detailed logging for invoice email processes and Stripe synchronization 2026-01-28 11:52:34 +00:00
Nawaz Dhandala
f4946449f3 feat: add migration to introduce sendInvoicesByEmail column and update defaults for OnCallDutyPolicyScheduleLayer 2026-01-28 11:32:44 +00:00
Nawaz Dhandala
9640732e29 feat: implement Stripe webhook for automatic invoice email sending and add configuration for webhook secret 2026-01-28 11:26:22 +00:00
Nawaz Dhandala
6ef5e409da fix: replace Navigation with window.location for redirect after account deletion 2026-01-28 09:53:57 +00:00
Nawaz Dhandala
bcdfa034f6 feat: add user account deletion functionality with confirmation modal 2026-01-28 09:50:25 +00:00
Nawaz Dhandala
959267a174 feat: update NewAlerts and NewIncidents to use alertNumber and incidentNumber with appropriate types 2026-01-28 09:40:42 +00:00
Nawaz Dhandala
767db415d2 fix: conditionally include incidentNumber in notification payloads 2026-01-27 21:11:50 +00:00
Nawaz Dhandala
7ca81aa9f8 chore: bump version to 9.4.11 2026-01-27 20:42:20 +00:00
Nawaz Dhandala
26bd4c7a90 fix: improve readability of conditional check in Delete method 2026-01-27 20:42:00 +00:00
Nawaz Dhandala
9d29a1d00b Refactor Terraform tests to remove project_id references
- Removed project_id variable and its usage from multiple Terraform test files (23-probe-crud, 24-status-page-crud, 25-status-page-with-domain, 26-monitor-steps-basic, 27-monitor-types, 28-incident-crud, 29-alert-crud, 30-scheduled-maintenance-crud, 31-on-call-duty-policy-crud, 32-monitor-group-crud, 33-team-crud, 35-monitor-with-steps, 36-monitor-types-basic, 37-label-order-idempotency).
- Updated ResourceGenerator to treat project_id as computed-only, inferred from API key authentication.
- Adjusted related logic in resource update and delete operations to exclude project_id from requests.
2026-01-27 20:00:41 +00:00
Nawaz Dhandala
935608d23d fix: format condition for attribute import check in DataSourceGenerator 2026-01-27 18:30:58 +00:00
Nawaz Dhandala
4a3000d3c3 feat: add initial Netscape HTTP cookie file for E2E tests 2026-01-27 18:30:17 +00:00
Nawaz Dhandala
b5df7042f7 fix: correct variable name in label validation checks for idempotency test 2026-01-27 18:30:01 +00:00
Nawaz Dhandala
a345390b9b feat: add URL normalization helpers to avoid drift in resource handling 2026-01-27 18:16:28 +00:00
Nawaz Dhandala
d0a8c049ba feat: add support for 'set' type in data source and resource generation, enhancing collection handling 2026-01-27 18:04:51 +00:00
Nawaz Dhandala
56037adcf0 fix: update alert field to use alertId for episode alerts 2026-01-27 16:18:28 +00:00
Nawaz Dhandala
73e2fcf3c6 feat: implement Episode Alerts table with CRUD functionality and enhanced alert display 2026-01-27 16:13:55 +00:00
Nawaz Dhandala
710bdea813 refactor: remove unused alert feed entry creation logic and related parameters 2026-01-27 15:44:11 +00:00
Nawaz Dhandala
f07ba35310 feat: add RemovedFromEpisode event type and update alert feed creation logic 2026-01-27 15:39:43 +00:00
Nawaz Dhandala
9ef2163bc0 feat: add New Alerts page and integrate with routing 2026-01-27 14:32:38 +00:00
Nawaz Dhandala
1f53a56b8f chore: bump version to 9.4.10 2026-01-27 13:55:36 +00:00
Nawaz Dhandala
6998b63f59 feat: enhance alert data retrieval in notification service for improved accuracy 2026-01-27 13:55:21 +00:00
Nawaz Dhandala
dccddf3ebc refactor: define ResourceInfo type for improved type safety in resource info retrieval 2026-01-27 13:36:43 +00:00
Nawaz Dhandala
2a7d076407 chore: bump version to 9.4.9 2026-01-27 13:33:00 +00:00
Nawaz Dhandala
0e82b17f6b refactor: format code for better readability in notification handling 2026-01-27 13:30:37 +00:00
Nawaz Dhandala
0aa7838fc5 feat: include alert numbers in push notification titles and bodies for note and state change updates 2026-01-27 13:29:52 +00:00
Nawaz Dhandala
b1d243896f feat: update alert episode notification templates to include episode numbers 2026-01-27 13:26:42 +00:00
Nawaz Dhandala
ab70c2c041 Enhance notification templates and services to include alert and incident numbers
- Updated email templates for alerts and incidents to include alertNumber and incidentNumber in the subject and body.
- Modified PushNotificationUtil to incorporate alert and incident numbers in notification titles and messages.
- Adjusted UserNotificationRuleService to pass alertNumber and incidentNumber for notifications.
- Enhanced SendCreatedResourceNotification, SendNotePostedNotification, SendOwnerAddedNotification, and SendStateChangeNotification jobs to include alert and incident numbers in subjects and messages.
- Updated ScheduledMaintenance notification jobs to reflect scheduledMaintenanceNumber in relevant notifications.
2026-01-27 13:19:12 +00:00
Nawaz Dhandala
22b6c5ace0 feat: update email subject and template to include alert episode number 2026-01-27 13:04:04 +00:00
Nawaz Dhandala
ad32579214 feat: enhance alert episode email template with styled alerts list 2026-01-27 13:01:51 +00:00
Nawaz Dhandala
e525cc3708 feat: enhance UserNotificationRuleService to include alert episode members and build alerts list for notifications 2026-01-27 12:56:02 +00:00
Nawaz Dhandala
db7eaacd14 feat: enhance UserNotificationLogTimelineAPI to support Alert Episodes and improve notification handling 2026-01-27 12:52:03 +00:00
Nawaz Dhandala
a549daf9ab refactor: improve code readability by updating comments and formatting in migration and services 2026-01-27 12:45:28 +00:00
Nawaz Dhandala
d9e65ce633 feat: add migration to rename notification rule types in UserNotificationRule 2026-01-27 12:43:00 +00:00
Nawaz Dhandala
be2d33591d refactor: update notification rule types for incidents and alerts 2026-01-27 12:36:00 +00:00
Nawaz Dhandala
88897004a2 refactor: rename fetchSitemap to fetchSitemapXml and update tests for sitemap structure 2026-01-27 12:20:41 +00:00
Simon Larsen
dceccf00fa Merge pull request #2251 from elmy-team/support-existing-secret-clickhouse-redis
Support existing secrets for Redis & ClickHouse
2026-01-27 10:39:44 +00:00
Matías Plaza
f079a2b9e6 feat: support existing secrets for redis & clickhouse 2026-01-27 10:53:29 +01:00
Nawaz Dhandala
4044d705d6 Merge branch 'master' of https://github.com/OneUptime/oneuptime 2026-01-27 08:44:35 +00:00
Nawaz Dhandala
a2cbe4e241 fix: update containerRef type to remove null from HTMLDivElement reference 2026-01-27 08:43:00 +00:00
Simon Larsen
b509b57bb8 Merge pull request #2250 from OneUptime/chore/npm-audit-fix
chore: npm audit fix
2026-01-27 08:38:44 +00:00
simlarsen
4a7f27a372 chore: npm audit fix 2026-01-27 02:04:49 +00:00
Nawaz Dhandala
214ac678d3 feat: add triggeredByAlertEpisodeId to on-call related tables and update UserOnCallLogService comments 2026-01-26 23:35:05 +00:00
Nawaz Dhandala
9dabce1b7a feat: add support for alert episode handling in user on-call logs and notifications 2026-01-26 23:34:30 +00:00
Nawaz Dhandala
9bc260847d feat: add migration for triggeredByAlertEpisodeId in on-call related tables 2026-01-26 23:24:27 +00:00
Nawaz Dhandala
229775935e style: improve code formatting and readability in various services 2026-01-26 23:22:27 +00:00
Nawaz Dhandala
668c35ba2e feat: add AcknowledgeAlertEpisode template and update related services for alert episode notifications 2026-01-26 23:21:50 +00:00
Nawaz Dhandala
a808913049 feat: add support for alert episode handling in user on-call logs and notifications 2026-01-26 23:06:20 +00:00
Nawaz Dhandala
54da185280 feat: enhance on-call duty policy execution to support alert episode handling 2026-01-26 22:45:49 +00:00
Nawaz Dhandala
b1bc7bfde4 fix: update canonical link generation in blog post view 2026-01-26 22:43:27 +00:00
Nawaz Dhandala
09d5ce0e1a feat: add support for triggeredByAlertEpisodeId in on-call duty policy execution and related services 2026-01-26 22:36:15 +00:00
Nawaz Dhandala
3935446071 feat: enhance on-call policy execution by adding policy names to feed entry 2026-01-26 22:28:52 +00:00
Nawaz Dhandala
2463dcb2db fix: update alert episode handling in ExecutionLogsTable; add support for triggeredByAlertEpisodeId 2026-01-26 22:26:17 +00:00
Nawaz Dhandala
0eb239a469 feat: implement on-call duty policy execution in episode creation process 2026-01-26 22:21:31 +00:00
Nawaz Dhandala
b865caae7a fix: update comment formatting in AlertEpisodeService for clarity 2026-01-26 22:16:32 +00:00
Nawaz Dhandala
a3e25723af fix: update sorting and resolve logic in AlertEpisodeService; improve clarity in auto-resolve comments 2026-01-26 22:15:14 +00:00
Nawaz Dhandala
bae0338c36 fix: encode tag slugs in AllTagsFilter and Tags partials for proper URL formatting 2026-01-26 22:07:39 +00:00
Nawaz Dhandala
f656f23836 fix: encode tag slugs in generateTagsSitemapXml for proper URL formatting 2026-01-26 22:06:24 +00:00
Nawaz Dhandala
a8a79162e4 fix: ensure consistent response handling in sitemap generation routes 2026-01-26 22:02:57 +00:00
Nawaz Dhandala
04556835b0 feat: add bulk delete action to Team and Users tables 2026-01-26 21:36:51 +00:00
Nawaz Dhandala
84c5e50199 chore: bump version to 9.4.8 2026-01-26 21:13:31 +00:00
Nawaz Dhandala
06fd44ecaf refactor: improve code formatting and consistency across multiple components 2026-01-26 21:12:47 +00:00
Nawaz Dhandala
c535b68056 feat(tests): update icon visibility checks in Input and TextArea tests 2026-01-26 21:03:03 +00:00
Nawaz Dhandala
34c8e4fdec feat(alert): update feed message icons for episode creation and addition 2026-01-26 20:50:39 +00:00
Nawaz Dhandala
bf04796637 feat(subscriber-notification-template): add connected status pages table for linking templates 2026-01-26 20:46:05 +00:00
Nawaz Dhandala
16e6c0c601 feat(alert): add event type for alert added to episode and create corresponding feed entries 2026-01-26 20:39:56 +00:00
Nawaz Dhandala
75a733e9b8 feat(scheduled-maintenance): implement bulk state change functionality for scheduled maintenance events 2026-01-26 20:33:38 +00:00
Nawaz Dhandala
30217a64ec feat(alert): enhance mutex handling in groupAlertWithRule to prevent race conditions 2026-01-26 20:23:28 +00:00
Nawaz Dhandala
488295e303 feat(alert): trigger sidebar badge count refresh on bulk actions 2026-01-26 20:12:55 +00:00
Nawaz Dhandala
973131b70a feat(alert): implement mutex for episode creation to prevent race conditions 2026-01-26 20:07:33 +00:00
Nawaz Dhandala
52cb00a1c4 feat(alert-episode): fetch current state order and name for alert episodes in bulk state change 2026-01-26 20:03:27 +00:00
Nawaz Dhandala
ab0027a042 feat(alert-episode): implement bulk state change functionality for alert episodes 2026-01-26 19:58:46 +00:00
Nawaz Dhandala
26a6d12809 refactor(alert): simplify episode retrieval by removing resolved state checks 2026-01-26 19:52:20 +00:00
Nawaz Dhandala
0bdf74cab2 feat(modal): exclude close button from focus trap in modal 2026-01-26 19:43:07 +00:00
Nawaz Dhandala
dd996539bc feat(alert): implement mutex for episode number generation to prevent race conditions 2026-01-26 19:40:57 +00:00
Nawaz Dhandala
ad2ee2b0d6 feat(alert): make cascadeStateToMemberAlerts public and integrate it into onCreateSuccess 2026-01-26 19:38:59 +00:00
Nawaz Dhandala
ed6630c2d6 feat(alert): update current alert state and handle resolvedAt transitions 2026-01-26 19:34:23 +00:00
Simon Larsen
3af9121d6a Merge pull request #2238 from OneUptime/alert-episode
Alert episode
2026-01-26 19:24:18 +00:00
Nawaz Dhandala
e9d5a560ff Refactor migration files and improve code formatting
- Updated migration files to enhance readability by adjusting indentation and line breaks.
- Added missing commas in the index file for migration imports.
- Improved accessibility attributes in various UI components by ensuring proper aria-labels and roles.
- Refactored key event handlers in UI components for better clarity and consistency.
- Enhanced error message handling in form components to ensure proper display and accessibility.
- Updated legacy function comments for clarity and maintainability.
2026-01-26 19:23:58 +00:00
Nawaz Dhandala
d87b6da7c5 fix(dependencies): update random provider version and adjust lock file handling 2026-01-26 19:19:23 +00:00
Nawaz Dhandala
34d6c8edbe feat(remediation): add remediation notes feature and alert episode feed component 2026-01-26 17:26:27 +00:00
Nawaz Dhandala
e9f63fb1e2 feat(alert): add lastAlertAddedAt and resolvedAt fields to alert selection 2026-01-26 17:18:20 +00:00
Nawaz Dhandala
7a515f7ad8 feat(alert): add priority field to alert selection criteria 2026-01-26 17:13:04 +00:00
Nawaz Dhandala
4564baae70 fix(accessibility): remove skip to main content link for improved keyboard navigation 2026-01-26 17:07:26 +00:00
Nawaz Dhandala
4316fdbf81 fix(vpat): update conformance status for Non-text Contrast to reflect full support 2026-01-26 16:58:08 +00:00
Nawaz Dhandala
be98736f4e fix(modal): correct type definition for modalRef to avoid null type 2026-01-26 16:55:45 +00:00
Nawaz Dhandala
ce7e10e3d9 feat(accessibility): enhance ARIA attributes and keyboard navigation across multiple components for improved accessibility 2026-01-26 16:53:41 +00:00
Nawaz Dhandala
21683de677 feat(accessibility): enhance ARIA roles and attributes across multiple components for improved screen reader support 2026-01-26 16:37:03 +00:00
Nawaz Dhandala
4dddec9966 feat(accessibility): enhance ARIA roles and attributes across components for improved screen reader support 2026-01-26 16:23:47 +00:00
Nawaz Dhandala
b79a287791 feat(accessibility): enhance ARIA attributes and alt text for improved screen reader support 2026-01-26 15:58:59 +00:00
Nawaz Dhandala
6bd4b7257d feat(legal): add VPAT page and update legal navigation 2026-01-26 14:41:48 +00:00
Nawaz Dhandala
438f8f4b6f refactor(AlertEpisodeService): import AlertService to avoid circular dependency 2026-01-26 13:46:25 +00:00
Nawaz Dhandala
75c1fedfba feat(package-lock): add peer dependency flag to multiple packages 2026-01-26 13:41:40 +00:00
Nawaz Dhandala
731a8e8b8f refactor(MarkdownViewer): simplify containerRef declaration in MermaidDiagram component 2026-01-26 13:40:29 +00:00
Simon Larsen
74768efea1 Merge pull request #2249 from OneUptime/chore/npm-audit-fix
chore: npm audit fix
2026-01-26 13:32:56 +00:00
Nawaz Dhandala
716d05b105 feat(logging): enhance alert processing with detailed debug logs 2026-01-26 13:18:11 +00:00
Nawaz Dhandala
8ea7b26299 feat(migration): add migration for episodeTitleTemplate and titleTemplate adjustments 2026-01-26 12:01:03 +00:00
Nawaz Dhandala
1538852bc4 feat: update title template fields to use LongText type for better flexibility 2026-01-26 11:58:41 +00:00
Nawaz Dhandala
a85607b996 feat(migration): add migration for AlertEpisode and OnCallDutyPolicyScheduleLayer schema updates 2026-01-26 11:58:05 +00:00
Nawaz Dhandala
a53db3b673 feat: rename menu items for clarity in DashboardSideMenu 2026-01-26 11:56:29 +00:00
Nawaz Dhandala
f6ec592cb6 feat: add Active Episodes page and integrate into dashboard routing 2026-01-26 11:54:48 +00:00
Nawaz Dhandala
d2e82fe50e feat(sitemap): implement paginated tags sitemap generation with caching 2026-01-25 18:51:23 +00:00
Nawaz Dhandala
30cb030470 feat(sitemap): enhance sitemap generation with multiple endpoints and caching mechanisms 2026-01-25 18:45:20 +00:00
Nawaz Dhandala
3b84f5cece feat: add comprehensive Dashboard Grafana Parity Plan document outlining improvements and implementation phases 2026-01-24 12:22:39 +00:00
Nawaz Dhandala
7f84f5c34d feat(migrations): add episodeDescriptionTemplate to AlertGroupingRule and update OnCallDutyPolicyScheduleLayer defaults
refactor(AlertEpisodeService): change variable declaration from let to const for users

refactor(AlertGroupingEngineService): improve code readability with comments and formatting

fix(WhatsAppTemplateUtil): format long lines for better readability

refactor(MicrosoftTeamsAlertEpisodeActions): format code for consistency and readability

refactor(MicrosoftTeams): improve readability by formatting conditional statements

refactor(SlackAlertEpisodeActions): format code for better readability

refactor(AlertEpisodeMemberService.test): improve imports and formatting for clarity

refactor(AlertEpisodeService.test): enhance readability with consistent formatting

refactor(AlertGroupingEngineService.test): improve code readability and formatting

refactor(MarkdownViewer): format imports and improve readability

refactor(BaseModelTable): format helpContent type definition for clarity

refactor(OrderedStatesList): format JSX for better readability

fix(AlertEpisodeDocs): format descriptions for consistency

fix(AlertGroupingRules): format helpContent description for clarity

fix(UserSettingsRoutes): format route path for better readability

fix(ResolveInactiveEpisodes): format conditional statements for clarity

fix(SendCreatedResourceNotification): format code for better readability

fix(SendNotePostedNotification): format code for consistency

fix(SendStateChangeNotification): format code for better readability
2026-01-24 11:50:16 +00:00
Nawaz Dhandala
47b42d92c1 Add unit tests for AlertEpisodeService, AlertGroupingEngineService, and AlertGroupingRuleService
- Implement comprehensive tests for AlertEpisodeService covering model instantiation, property accessors, and template rendering.
- Create tests for AlertGroupingEngineService focusing on alert grouping logic, matching criteria, and grouping key generation.
- Develop tests for AlertGroupingRuleService to validate rule properties, match criteria, group settings, and priority ordering.
2026-01-24 11:38:56 +00:00
simlarsen
1928244a8e chore: npm audit fix 2026-01-24 01:54:38 +00:00
Nawaz Dhandala
c3af14c3fe feat: Add title and description template support in AlertEpisode and update related services for dynamic variable handling 2026-01-23 21:26:07 +00:00
Nawaz Dhandala
324666bafe feat: Refactor template variable display in AlertGroupingRules for improved clarity and structure 2026-01-23 21:22:18 +00:00
Nawaz Dhandala
8d4862f39f feat: Add migration for episodeDescriptionTemplate in AlertGroupingRule and update OnCallDutyPolicyScheduleLayer defaults 2026-01-23 21:15:36 +00:00
Nawaz Dhandala
8c42627d46 feat: Add episode description template support and update placeholders in AlertGroupingRules 2026-01-23 21:14:44 +00:00
Nawaz Dhandala
590caa0563 feat: Remove unused ShowAs prop and related ordered states list props from AlertGroupingRulesPage 2026-01-23 21:06:58 +00:00
Nawaz Dhandala
15ac2bf749 feat: Update help content button style in BaseModelTable for improved UI 2026-01-23 20:55:32 +00:00
Nawaz Dhandala
d98103ca94 feat: Add help content support to BaseModelTable and integrate into AlertGroupingRules 2026-01-23 20:49:33 +00:00
Nawaz Dhandala
f8a6354fb4 feat: Update documentation headings in AlertGroupingRules for improved clarity 2026-01-23 20:38:01 +00:00
Nawaz Dhandala
c7cb3b3b20 feat: Enhance MarkdownViewer to handle Mermaid diagrams without additional wrappers 2026-01-23 20:22:53 +00:00
Nawaz Dhandala
978998b3f8 feat: Enhance Mermaid diagram styling in MarkdownViewer component 2026-01-23 20:21:10 +00:00
Nawaz Dhandala
738e464c1e feat: Add 'Group By Service' option to Alert Grouping Rules and implement related database migration 2026-01-23 20:15:58 +00:00
Nawaz Dhandala
8882d62eac chore: update mermaid dependency from 11.4.0 to 11.12.2 in package.json 2026-01-23 19:24:22 +00:00
Nawaz Dhandala
90cd819b0a feat: Add Mermaid diagram support to MarkdownViewer and integrate documentation in AlertGroupingRules 2026-01-23 19:23:47 +00:00
Nawaz Dhandala
a6873c687a feat: Update grouping logic to require explicit enabling of group by options 2026-01-23 19:16:40 +00:00
Nawaz Dhandala
4b54bd6b91 feat: Conditionally display no items message based on create button presence 2026-01-23 18:51:03 +00:00
Nawaz Dhandala
eaf2cbcb71 feat: Remove no items message from Alert Grouping Rules page 2026-01-23 18:46:49 +00:00
Nawaz Dhandala
d9a1876ad4 feat: Add cascade state change functionality for member alerts in AlertEpisodeService 2026-01-23 18:44:18 +00:00
Nawaz Dhandala
c09f75faf0 feat: Add Episode On-Call Rules functionality and related routes 2026-01-23 18:32:19 +00:00
Nawaz Dhandala
674e32b95b feat: add computed properties for SSL ordered and provisioned in StatusPageDomain 2026-01-23 18:20:48 +00:00
Nawaz Dhandala
84cab49386 feat: Add migration for remediation notes and workspace channel updates in AlertEpisode 2026-01-23 17:49:31 +00:00
Nawaz Dhandala
479b83f6bf feat: Implement Alert Episode functionality with Slack and Microsoft Teams integration 2026-01-23 17:33:15 +00:00
Nawaz Dhandala
81ed9e0fc1 fix: correct typo in TerraformPrompt.md regarding provider code generation 2026-01-23 17:28:39 +00:00
Nawaz Dhandala
1f162461ad feat: Add alert episode owner notifications and related email templates
- Implemented findOwners method in AlertEpisodeService to retrieve user and team owners for alert episodes.
- Added new email templates for alert episode owner notifications: AlertEpisodeOwnerAdded, AlertEpisodeOwnerStateChanged, AlertEpisodeOwnerNotePosted, and AlertEpisodeOwnerResourceCreated.
- Created jobs for sending notifications when an owner is added, a note is posted, and when the state of an alert episode changes.
- Updated routes to include new jobs for alert episode owner notifications.
2026-01-23 14:17:32 +00:00
Nawaz Dhandala
ad4f901e2f feat: add alert episode support across services and notification settings 2026-01-23 14:05:00 +00:00
Nawaz Dhandala
a885e2e8a8 feat: add migration for alert episode related fields and constraints 2026-01-23 13:56:25 +00:00
Nawaz Dhandala
8f11156011 feat: Add Alert Episode functionality and Slack integration
- Implemented addNote and hasNoteFromSlackMessage methods in AlertEpisodeInternalNoteService.
- Added isEpisodeAcknowledged method in AlertEpisodeService to check acknowledgment status.
- Enhanced OnCallDutyPolicyService to include triggeredByAlertEpisodeId in logs.
- Updated WorkspaceNotificationRuleService to support Alert Episode conditions.
- Introduced new Slack action types for Alert Episodes.
- Extended NotificationRuleEventType and NotificationRuleConditionCheckOn enums for Alert Episodes.
- Modified WorkspaceConnectionMicrosoftTeams and WorkspaceConnectionSlack components to handle Alert Episodes.
- Created SlackAlertEpisodeActions class for managing Alert Episode actions in Slack.
- Implemented emoji reaction handling for private notes in Alert Episodes.
2026-01-23 13:53:54 +00:00
Nawaz Dhandala
1ef1101134 Merge branch 'master' into release 2026-01-23 13:05:05 +00:00
Nawaz Dhandala
5664ad48dd fix: move git user configuration after repository initialization in publish script 2026-01-23 13:04:50 +00:00
Nawaz Dhandala
e2608f56db feat: add migration for AlertGroupingRuleMonitor and related tables 2026-01-23 12:47:09 +00:00
Nawaz Dhandala
78df267145 feat: add monitor name and description pattern fields to alert grouping rules 2026-01-23 12:45:45 +00:00
Nawaz Dhandala
20e7e68e71 feat: enhance alert grouping rules with match criteria and grouping options 2026-01-23 12:35:30 +00:00
Nawaz Dhandala
877d69f22e feat: add migration for enableTimeWindow column in AlertGroupingRule 2026-01-23 12:16:48 +00:00
Nawaz Dhandala
0791029f4a feat: add enable time window option for alert grouping rules 2026-01-23 12:16:10 +00:00
Nawaz Dhandala
69c91a2c41 Merge branch 'master' into alert-episode 2026-01-23 12:04:34 +00:00
Nawaz Dhandala
499d28c34c Merge branch 'master' into release 2026-01-23 12:01:58 +00:00
Nawaz Dhandala
ec57a9ddbc chore: bump version to 9.4.7 2026-01-23 12:01:46 +00:00
Nawaz Dhandala
e59db99b22 feat: update migration for monitor log retention and format RouteMap entries 2026-01-23 11:58:30 +00:00
Nawaz Dhandala
aaa3c4f602 fix: remove unnecessary constraints and columns from Alert table in migration 2026-01-23 11:57:21 +00:00
Nawaz Dhandala
29842c06e3 feat: add migration for monitor log retention and update alert table structure 2026-01-23 11:56:46 +00:00
Nawaz Dhandala
402ccf01d0 feat: add Data Retention settings page and integrate with global configuration 2026-01-23 11:55:37 +00:00
Nawaz Dhandala
b7114304ee fix: streamline git repository handling in Terraform provider publishing script 2026-01-23 11:51:40 +00:00
Nawaz Dhandala
dca72856a2 fix: configure git user before committing generated Terraform provider files 2026-01-23 11:48:30 +00:00
Nawaz Dhandala
1d7c758096 fix: update import path for ReactElement in CategoryCheckboxTypes 2026-01-23 11:31:50 +00:00
Simon Larsen
1e748365a5 Merge pull request #2245 from OneUptime/snyk-fix-a7af8e60213a832c66f6a2fdddd05e1e
[Snyk] Fix for 1 vulnerabilities
2026-01-23 11:28:56 +00:00
Nawaz Dhandala
a8022762a2 Merge branch 'release' into alert-episode 2026-01-23 11:02:19 +00:00
Nawaz Dhandala
9c1ed659e9 fix(URL): streamline route handling in toString method 2026-01-23 09:24:03 +00:00
Nawaz Dhandala
42accb4204 feat(Migration): add migration for new alert grouping rule features and on-call duty policy updates 2026-01-22 23:46:59 +00:00
Nawaz Dhandala
529e5954d4 feat(AlertEpisodes): add resolve delay, reopen window, and inactivity timeout features 2026-01-22 23:45:48 +00:00
Nawaz Dhandala
75ea34ef9e feat(AlertEpisodeDocs): enhance descriptions and update icons for clarity 2026-01-22 23:32:10 +00:00
Nawaz Dhandala
c4e1b8d97d fix: increase timeout for Terraform E2E tests from 60 to 120 minutes 2026-01-22 23:31:21 +00:00
Nawaz Dhandala
b5626ef352 feat(AlertEpisodeDocs): add on-call policy flow steps and notification scenarios 2026-01-22 23:14:56 +00:00
Nawaz Dhandala
692d15159c feat(Terraform E2E): enhance disk cleanup process and optimize Docker storage management 2026-01-22 23:13:51 +00:00
Nawaz Dhandala
91c163af9e feat(AlertEpisodes): add documentation page for alert episodes and update side menu 2026-01-22 23:05:49 +00:00
Nawaz Dhandala
fe71be64dd refactor: replace dynamic imports with direct imports for service dependencies 2026-01-22 22:58:49 +00:00
Nawaz Dhandala
9e6587cb62 feat(OrderedStatesList): enhance no items message with add new item option 2026-01-22 22:48:05 +00:00
snyk-bot
42b2f58d6a 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-DIFF-14917201
2026-01-22 22:34:30 +00:00
Simon Larsen
98afb63880 Merge pull request #2243 from OneUptime/chore/npm-audit-fix
chore: npm audit fix
2026-01-22 22:33:12 +00:00
Nawaz Dhandala
6d352c8579 Merge branch 'master' into alert-episode 2026-01-22 22:28:57 +00:00
Nawaz Dhandala
21b761ddbf chore(VERSION): bump version to 9.4.6 2026-01-22 22:23:34 +00:00
Nawaz Dhandala
e51009e94d Merge branch 'master' into release 2026-01-22 22:23:21 +00:00
Nawaz Dhandala
619136c9da feat(MailService): enhance email sending response types for SendGrid and SMTP 2026-01-22 21:58:45 +00:00
Nawaz Dhandala
d622334bd3 feat(StatusPageAuthentication): integrate SMTP configuration into status page login and creation processes 2026-01-22 21:51:34 +00:00
Nawaz Dhandala
5508bf6302 feat(StatusPagePrivateUserService): integrate SMTP configuration into status page creation process 2026-01-22 21:44:11 +00:00
Nawaz Dhandala
4ba9151400 feat(dockerignore): add large directories to ignore for Docker builds 2026-01-22 21:42:52 +00:00
Nawaz Dhandala
97fe212d15 feat(ExceptionProductImprovements): add comprehensive plan for enhancing exception tracking features 2026-01-22 21:21:09 +00:00
Nawaz Dhandala
af3738924e feat(MailService): add debug logging for SMTP and SendGrid email responses 2026-01-22 20:57:18 +00:00
Nawaz Dhandala
92f77c7ce2 feat(SCIM): update user name in SCIM request if missing or different 2026-01-22 20:44:11 +00:00
Nawaz Dhandala
ecb54381d8 style(Login.tsx, MasterPassword.tsx, StatusPage.ts): improve code formatting for better readability 2026-01-22 20:29:08 +00:00
Nawaz Dhandala
0a9435ef1a feat(auth): implement safe redirect URL handling to prevent navigation to auth pages 2026-01-22 20:23:18 +00:00
Nawaz Dhandala
2bc52c7b5d Merge branch 'master' into release 2026-01-22 18:11:50 +00:00
Nawaz Dhandala
392b6dda9a chore: bump version to 9.4.5 2026-01-22 18:11:39 +00:00
Nawaz Dhandala
0a6035ed65 style(ProductCompare.ts, GenerateProvider.ts): improve code formatting for better readability 2026-01-22 18:11:21 +00:00
Nawaz Dhandala
70f9444aab feat(svg): add new SVG icons for better integration and visual consistency 2026-01-22 18:07:19 +00:00
Nawaz Dhandala
088333c91c style(pricing.ejs, product-compare.ejs): improve text wrapping and update image styling for better layout 2026-01-22 15:47:30 +00:00
Nawaz Dhandala
7fc7276207 feat(pricing.ejs): enhance Bitcoin payment interaction with clickable text and improved styling 2026-01-22 15:43:20 +00:00
Nawaz Dhandala
631bf12c23 style(pricing.ejs): adjust genesis text styling and update content for clarity 2026-01-22 15:19:28 +00:00
Nawaz Dhandala
5ce158ebf3 Refactor code structure for improved readability and maintainability 2026-01-22 15:18:17 +00:00
Nawaz Dhandala
4684f25f22 easter egg 2026-01-22 15:17:19 +00:00
Nawaz Dhandala
3d36d86bd6 chore(VERSION): bump version to 9.4.4 2026-01-22 13:36:19 +00:00
Nawaz Dhandala
93c017dbab chore(GenerateProvider): add VERSION file generation to ensure proper detection of changes 2026-01-22 13:25:03 +00:00
Nawaz Dhandala
c74204ed1f chore(sitemap): remove outdated sitemap.xml file 2026-01-22 13:12:09 +00:00
Nawaz Dhandala
80125f500c chore(views): update card components to ensure full height for better layout consistency 2026-01-22 12:58:43 +00:00
Nawaz Dhandala
2771efcd87 chore(GoModuleGenerator): update comments for clarity in generateGoMod method
chore(GenerateProvider): format log message for consistency in main function
2026-01-22 12:43:45 +00:00
Nawaz Dhandala
0e27802f1a chore(verify.sh): update monitor verification logic to check for manual with description and remove empty steps check 2026-01-22 12:39:14 +00:00
Nawaz Dhandala
a96c270a94 chore(URL.ts): update toString method to conditionally add route and improve URL construction logic
chore(MonitorStep.ts): remove redundant default monitor assignments in getDefaultMonitorStep method
chore(Port.ts): change toJSON method to return port value as a number instead of string
2026-01-22 12:38:50 +00:00
Nawaz Dhandala
66d76676f5 chore(ResourceGenerator): add sorting for list items to ensure consistent ordering and fix idempotency issues 2026-01-22 12:35:50 +00:00
simlarsen
cc0eb6a4b9 chore: npm audit fix 2026-01-22 02:02:10 +00:00
Nawaz Dhandala
2d687a3275 chore(E2E Tests): refactor verification scripts to use shared library functions and improve validation 2026-01-21 22:23:20 +00:00
Nawaz Dhandala
47bca3fb9b chore(ResourceGenerator): refactor valid ObjectType handling to use dynamic generation from enum 2026-01-21 21:29:29 +00:00
Nawaz Dhandala
79d3548492 chore(ResourceGenerator): add validation for OneUptime ObjectType in resource handling 2026-01-21 21:26:07 +00:00
Nawaz Dhandala
3156302dbc chore(JSON.ts): remove unnecessary blank line in ObjectType enum 2026-01-21 21:15:35 +00:00
Nawaz Dhandala
84307250b7 chore(GoModuleGenerator): update go.mod generation to specify minimum versions and clarify dependency fetching process 2026-01-21 21:13:07 +00:00
Nawaz Dhandala
c3986bd66a chore(GoModuleGenerator): update go.mod to use Go 1.23 and pin dependencies for compatibility with Terraform 1.5+ 2026-01-21 21:08:42 +00:00
Nawaz Dhandala
a4a56bf2c7 chore(GoModuleGenerator): update go.mod to pin additional indirect dependencies for compatibility with Terraform 1.5+ 2026-01-21 21:04:36 +00:00
Nawaz Dhandala
6644523c54 chore(robots.txt): update AI agent access rules and refine API disallow directive 2026-01-21 18:20:50 +00:00
Nawaz Dhandala
44aa046fec chore: update Go version to 'stable' in workflow files 2026-01-21 17:27:08 +00:00
Nawaz Dhandala
da5b9b4955 chore(VERSION): bump version to 9.4.3 2026-01-21 15:59:54 +00:00
Nawaz Dhandala
81dd803b62 Merge branch 'release' 2026-01-21 15:59:39 +00:00
Nawaz Dhandala
efffa82cbf refactor(StatusPageService): simplify conditional check for downtimeMonitorStatuses 2026-01-21 15:56:49 +00:00
Nawaz Dhandala
64b0c9f137 feat(Terraform): update monitor names to use random suffixes for uniqueness 2026-01-21 15:48:15 +00:00
Nawaz Dhandala
a3661e1626 Merge branch 'master' of https://github.com/OneUptime/oneuptime 2026-01-21 15:46:18 +00:00
Nawaz Dhandala
4115deadc4 feat(Monitor): add monitorId prop to MonitorStep and MonitorSteps components for secret population during testing 2026-01-21 14:58:09 +00:00
Nawaz Dhandala
6fbd112964 fix(ResourceGenerator): enhance handling of complex typed objects in Delete method 2026-01-21 14:55:25 +00:00
Nawaz Dhandala
a171c52c8d feat(tests): refactor E2E tests to use random suffixes for resource names
- Updated multiple test files to replace timestamp-based naming with random suffixes for better uniqueness and idempotency.
- Added random provider to each test file to generate unique identifiers.
- Removed unnecessary lifecycle blocks that ignored changes to resource names.
- Ensured consistent naming conventions across all test cases for clarity and maintainability.
2026-01-21 14:45:46 +00:00
Nawaz Dhandala
4f7d3ed2be feat(E2E Tests): add comprehensive tests for various monitor types with steps and basic configurations 2026-01-21 13:38:49 +00:00
Nawaz Dhandala
9db97b3919 Add E2E tests for OneUptime resources
- Implemented monitor types tests including variable definitions and verification scripts.
- Created incident CRUD tests with various configurations and verification logic.
- Added alert CRUD tests to validate alert creation and configurations.
- Developed scheduled maintenance CRUD tests to ensure proper handling of maintenance events.
- Established on-call duty policy CRUD tests to validate policy creation and configurations.
- Introduced monitor group CRUD tests to verify group creation and uniqueness.
- Created team CRUD tests to validate team creation and configurations.
2026-01-21 13:25:55 +00:00
Nawaz Dhandala
8bc545c90f Add end-to-end tests for OneUptime resources
- Implemented idempotency tests for status pages, monitors, and probes.
- Created Terraform configurations and verification scripts for:
  - StatusPageDomain idempotency (Issue #2236)
  - Monitor CRUD operations with various configurations
  - Probe CRUD operations, ensuring probe_version is a string (Issue #2228)
  - StatusPage CRUD operations, handling server defaults (Issue #2232)
  - StatusPage with domain integration, validating computed fields and server-injected defaults
- Added necessary variable files for configuration.
2026-01-21 13:09:36 +00:00
Nawaz Dhandala
19d1629e37 fix(StatusPageDomain): update access control for create permissions and adjust required fields 2026-01-21 13:01:51 +00:00
Nawaz Dhandala
e7fd472c14 fix(ResourceGenerator): enhance handling of complex object responses in Delete method 2026-01-21 12:33:27 +00:00
Nawaz Dhandala
dd4a1416fc fix(StatusPageService): simplify downtime monitor statuses check 2026-01-21 12:17:25 +00:00
Nawaz Dhandala
09b65b9a5b fix(StatusPageDomain): change CNAME Verification Token requirement to optional 2026-01-21 12:09:11 +00:00
Simon Larsen
8fb1a1daf9 Merge pull request #2239 from OneUptime/chore/npm-audit-fix
chore: npm audit fix
2026-01-21 09:27:59 +00:00
simlarsen
d3d0dedfee chore: npm audit fix 2026-01-21 02:01:12 +00:00
Nawaz Dhandala
46635f4251 refactor: improve type safety with type aliases for dynamically imported services 2026-01-20 21:09:37 +00:00
Nawaz Dhandala
1068a5d96e refactor: add type annotations for improved type safety and clarity in service imports 2026-01-20 21:00:36 +00:00
Nawaz Dhandala
d1e200a54f Refactor code for improved readability and consistency
- Added missing comma in schema migrations index.
- Reformatted long lines in AlertEpisodeFeedService and AlertEpisodeMemberService for better readability.
- Improved formatting in AlertEpisodeService for destructured variables.
- Enhanced readability in AlertGroupingEngineService by restructuring filter and map functions.
- Cleaned up permission descriptions in Permission.ts for consistency.
- Standardized description formatting in ChangeState and AlertGroupingRules components.
- Improved formatting in Episodes and AlertView components for better readability.
- Added comments for clarity in AutoResolve and BreakInactive jobs.
2026-01-20 20:50:09 +00:00
Nawaz Dhandala
8220b0356c Merge branch 'master' into alert-episode 2026-01-20 20:46:58 +00:00
Nawaz Dhandala
bc9b09aed5 chore: Bump version to 9.4.2 2026-01-20 20:46:03 +00:00
Nawaz Dhandala
6822718c46 feat(SCIM): Normalize operation strings to lowercase in PATCH requests 2026-01-20 20:42:57 +00:00
Nawaz Dhandala
9a935c4e90 feat(AlertEpisode): add services and models for AlertEpisode management 2026-01-20 19:56:41 +00:00
Nawaz Dhandala
6137199e63 feat: add AlertGroupingRule, AlertEpisode, and related tables with foreign key constraints and indexes 2026-01-20 19:42:01 +00:00
Nawaz Dhandala
6e6d989be4 feat: Add Alert Episode Management Features
- Implemented AlertEpisodeViewLayout for displaying episode details.
- Created Owners component to manage team and user ownership of episodes.
- Added RootCause component for documenting the root cause of episodes.
- Developed SideMenu for navigation within the episode view.
- Introduced StateTimeline component to track the status timeline of episodes.
- Created EpisodesPage for listing all alert episodes.
- Added AlertGroupingRulesPage for managing alert grouping rules.
- Implemented UnresolvedEpisodesPage to display active (unresolved) episodes.
- Developed AutoResolve job to automatically resolve episodes based on alert status.
- Created BreakInactive job to resolve inactive episodes after a specified timeout.
2026-01-20 19:35:31 +00:00
Simon Larsen
5a7af27543 Merge pull request #2237 from OneUptime/alert-episode
Alert episode
2026-01-20 19:08:04 +00:00
Nawaz Dhandala
7c422b4384 feat(AlertGrouping): Enhance episode management documentation with ownership, severity, root cause, flapping prevention, manual creation, deletion, UI navigation, and alert relationships 2026-01-20 18:53:00 +00:00
Nawaz Dhandala
b06de38f69 feat(AlertGrouping): Update manual management details and enhance episode title generation guidelines 2026-01-20 18:46:13 +00:00
Nawaz Dhandala
71723675d6 feat(AlertGrouping): Update Alert Model enhancements and add Implementation Q&A for episode state management and grouping logic 2026-01-20 18:41:53 +00:00
Nawaz Dhandala
e699e323cb feat(AlertGrouping): Remove outdated migration and implementation documents; add summary for Alert Grouping feature
- Deleted the detailed migration plan (5-Migration.md) and implementation plan (README.md) for Alert Grouping.
- Introduced a new summary document (Summary.md) outlining key capabilities, data models, grouping types, and on-call policy resolution for the Alert Grouping feature.
2026-01-20 18:32:31 +00:00
Nawaz Dhandala
8e8bc54aed feat: Add on-call policy override fields and behavior to AlertEpisode and AlertGroupingRule models 2026-01-20 18:18:09 +00:00
Nawaz Dhandala
d3cf309aef feat: Add flapping prevention fields and behavior to AlertEpisode model 2026-01-20 18:02:32 +00:00
Nawaz Dhandala
a24f9d37a9 Refactor verify scripts to handle API response wrapper formats
- Added a helper function `unwrap_value` to extract values from API responses that may be wrapped in an object format.
- Updated all verification scripts to use `unwrap_value` for fields such as name, description, color, title, and IDs.
- Enhanced checks for boolean fields to handle cases where the API may not return values due to permission issues.
- Ensured consistency across multiple test scripts for better maintainability and readability.
2026-01-20 18:02:18 +00:00
Nawaz Dhandala
d5332ed494 test: Add idempotency tests for probe_version READ operation 2026-01-20 16:57:30 +00:00
Nawaz Dhandala
23fdd3bfd7 feat: Implement Alert Suppression UI and Migration Plan
- Added UI components and pages for Alert Suppression including:
  - Suppression Rules List Page
  - Create/Edit Suppression Rule Page
  - Suppressed Alerts Log Page
  - Maintenance Windows Calendar View
  - Active Maintenance Banner
  - Quick Maintenance Modal
- Created migration scripts for new database tables:
  - AlertSuppressionGroup
  - AlertSuppressionRule
  - SuppressedAlertLog
  - AlertThrottleState
- Defined rollout strategy and data retention policies for suppressed alerts
- Updated README with implementation plan and architecture diagram
2026-01-20 13:46:10 +00:00
Nawaz Dhandala
714f8b4edf chore: bump version to 9.4.1 2026-01-20 11:05:38 +00:00
Nawaz Dhandala
02f920a152 refactor: improve code formatting and structure in EvaluationLogList and Logs components 2026-01-20 10:50:49 +00:00
Nawaz Dhandala
378663b03c fix: enhance status indicator styles for better visibility in EvaluationLogList 2026-01-20 10:46:29 +00:00
Nawaz Dhandala
78257ebda8 fix: update status indicators in EvaluationLogList for improved clarity 2026-01-20 10:16:23 +00:00
Nawaz Dhandala
d29e876b96 feat: add probeName prop to monitor summary components for enhanced monitoring details 2026-01-20 10:07:52 +00:00
Nawaz Dhandala
88c1e23da9 fix: clean node_modules to prevent permission issues with npm cache in CI 2026-01-20 09:35:58 +00:00
Nawaz Dhandala
cb50f89a12 Refactor code structure for improved readability and maintainability 2026-01-19 21:36:54 +00:00
Simon Larsen
869cc6d2b8 Merge pull request #2234 from OneUptime/terraform-tests
Terraform tests
2026-01-19 21:09:46 +00:00
Nawaz Dhandala
1566bd6a21 Add verification scripts for various resources in E2E tests
- Implemented verify.sh scripts for the following tests:
  - 08-probe: Validate probe resource creation and API response consistency.
  - 09-label-crud: Validate label CRUD operations via API.
  - 10-monitor-status-crud: Validate monitor status CRUD operations via API.
  - 11-incident-severity-crud: Validate incident severity CRUD operations via API.
  - 12-status-page-domain: Validate domain and status page resources via API.
  - 13-status-page-domain-computed-fields: Validate computed fields for status page domains.
  - 14-status-page-server-defaults: Validate server-provided defaults for status pages.
  - 15-monitor-server-defaults: Validate server-provided defaults for monitors.
  - 16-incident-server-defaults: Validate server-provided defaults for incidents.
  - 17-alert-server-defaults: Validate server-provided defaults for alerts.
  - 18-scheduled-maintenance-server-defaults: Validate server-provided defaults for scheduled maintenance events.
  - 19-on-call-duty-policy-server-defaults: Validate server-provided defaults for on-call duty policies.

Each script checks the API responses against expected values derived from Terraform outputs, ensuring resource integrity and consistency.
2026-01-19 21:08:05 +00:00
Nawaz Dhandala
d175841b2a feat: enhance Terraform E2E tests with comprehensive API validation and remove CRUD tests 2026-01-19 19:56:39 +00:00
Nawaz Dhandala
811fb49c2d refactor: update comments for clarity and consistency in DomainService, Domain, and ResourceGenerator 2026-01-19 19:47:37 +00:00
Nawaz Dhandala
290b59dfc9 feat: add Terraform tests for server-provided defaults in incident, alert, scheduled maintenance, and on-call policy configurations 2026-01-19 19:40:56 +00:00
Nawaz Dhandala
acb57b6b32 feat: enable tool-cache in disk space cleanup and add additional disk cleanup step 2026-01-19 19:40:37 +00:00
Nawaz Dhandala
ff1feb1a9f feat: add Terraform test for server-provided defaults in monitor configuration 2026-01-19 19:30:06 +00:00
Nawaz Dhandala
a99c09c05a feat: remove isDefaultValueColumn from TerraformAttribute interface 2026-01-19 19:25:58 +00:00
Nawaz Dhandala
b400965384 feat: enhance resource generation with dynamic plan modifier imports and add tests for server-provided defaults 2026-01-19 19:25:37 +00:00
Nawaz Dhandala
48a9523bb6 Merge branch 'terraform-tests' of https://github.com/OneUptime/oneuptime into terraform-tests 2026-01-19 18:46:22 +00:00
Nawaz Dhandala
aa5ff55a9c feat: refactor domain verification logic to use isTestDomain method for clarity 2026-01-19 18:43:47 +00:00
Nawaz Dhandala
08f8200d5b feat: allow auto-verification for test domains and update Terraform test configurations 2026-01-19 18:29:02 +00:00
Simon Larsen
7030f27076 Potential fix for code scanning alert no. 1311: Workflow does not contain permissions
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
2026-01-19 18:08:21 +00:00
Nawaz Dhandala
d7611b895b feat: add script to run Terraform tests in package.json 2026-01-19 18:05:41 +00:00
Nawaz Dhandala
ec8afb2d0b refactor: dynamically discover test directories in run-tests.sh 2026-01-19 17:58:57 +00:00
Nawaz Dhandala
81aeb373c4 feat: add Terraform E2E tests for status page domain and computed fields 2026-01-19 17:57:46 +00:00
Nawaz Dhandala
cbc779ae0b feat: add computed property to Full Domain and CNAME Verification Token in StatusPageDomain model 2026-01-19 17:50:16 +00:00
Nawaz Dhandala
9906115faf feat: add transaction execution method to DatabaseService and refactor MonitorProbeService to use it 2026-01-19 17:29:43 +00:00
Nawaz Dhandala
86d60f4688 refactor: improve code comments for clarity in MonitorProbeService and ResourceGenerator 2026-01-19 17:25:11 +00:00
Nawaz Dhandala
d7f329fcff feat: add comprehensive CRUD tests for probe, label, monitor status, and incident severity resources 2026-01-19 17:21:10 +00:00
Nawaz Dhandala
120d36f3dd fix: always import math/big and add helper for converting *big.Float to float64 2026-01-19 14:43:18 +00:00
Nawaz Dhandala
13f22b1611 fix: extract API key value correctly from response in setup-test-account.sh 2026-01-19 14:29:53 +00:00
Nawaz Dhandala
f9a89548e2 fix: remove terraform init step for tests using dev_overrides 2026-01-19 14:27:05 +00:00
Nawaz Dhandala
88c55f9e14 fix: correct provider directory path in run-tests.sh script 2026-01-19 14:24:01 +00:00
Nawaz Dhandala
91965f3cc9 feat: add peer dependency flags in package-lock.json and update .gitignore for test environment script 2026-01-19 14:21:50 +00:00
Nawaz Dhandala
1383b1f3b0 feat: add conditions to disable active monitoring for manual incidents and scheduled maintenance 2026-01-19 14:03:47 +00:00
Nawaz Dhandala
cf45f089af feat: add subscription status checks for claiming monitor probes 2026-01-19 14:01:44 +00:00
Nawaz Dhandala
ecfcbae86b feat: implement atomic claiming of monitor probes using FOR UPDATE SKIP LOCKED 2026-01-19 13:59:57 +00:00
Nawaz Dhandala
b714ad168c chore: add step to free disk space in Terraform E2E tests workflow 2026-01-19 13:49:39 +00:00
Nawaz Dhandala
59d6aeb2b4 refactor: remove cleanup function and associated trap from index.sh 2026-01-19 13:36:45 +00:00
Nawaz Dhandala
be6d122879 fix: update cleanup function to use npm run down instead of docker compose 2026-01-19 13:36:33 +00:00
Nawaz Dhandala
b9fcfd5c61 chore: remove unused cookies.txt file from e2e-tests 2026-01-19 13:29:26 +00:00
Nawaz Dhandala
9e902e5b76 fix: update index.sh file permissions to make it executable 2026-01-19 13:29:22 +00:00
Nawaz Dhandala
4fa44b40c9 refactor: remove wait-for-services script and update index.sh to use npm status-check 2026-01-19 13:27:55 +00:00
Nawaz Dhandala
3baa081850 feat: Implement end-to-end tests for Terraform provider
- Added a new directory structure for E2E tests under E2E/Terraform/e2e-tests.
- Created scripts for managing the test lifecycle: index.sh, wait-for-services.sh, setup-test-account.sh, run-tests.sh, and cleanup.sh.
- Developed README.md to document the E2E testing process and directory structure.
- Created individual test cases for various resources including labels, monitor statuses, incident severities, incident states, status pages, alert severities, and alert states.
- Configured GitHub Actions workflow for automated testing on pull requests and pushes to main branches.
- Added necessary variable files for each test case to define required inputs.
- Included cookies.txt for session management during API interactions.
2026-01-19 13:27:11 +00:00
Nawaz Dhandala
bcc7218091 feat: add implementation plan for Terraform provider end-to-end tests 2026-01-19 12:45:31 +00:00
Nawaz Dhandala
91937ea9bc fix: update .gitignore to include specific Terraform files and exclude general directory 2026-01-19 12:42:04 +00:00
Nawaz Dhandala
79835d411e feat(migrations): add IncomingCallPolicy and related tables with constraints
- Implemented MigrationName1768825402472 to create tables for IncomingCallPolicy, IncomingCallPolicyEscalationRule, IncomingCallLog, IncomingCallLogItem, UserIncomingCallNumber, and IncomingCallPolicyLabel.
- Added necessary indexes and foreign key constraints for data integrity.
- Updated the User table to drop the alertPhoneNumber column and adjusted OnCallDutyPolicyScheduleLayer defaults.
- Updated index file to include the new migration.
2026-01-19 12:25:06 +00:00
Nawaz Dhandala
8a965dcf1a feat: Add IncomingCallPolicy migration and related database schema changes 2026-01-19 12:24:14 +00:00
Nawaz Dhandala
549cbe7102 Revert "fix: Enhance handling of complex object responses in ResourceGenerator"
This reverts commit 5c84699bae.
2026-01-19 11:45:15 +00:00
Nawaz Dhandala
2f727b7707 Remove obsolete migration files and update index
- Deleted migration file for removing alert phone number from User.
- Deleted extensive migration file for IncomingCallPolicy and related tables.
- Deleted migration file for altering OnCallDutyPolicyScheduleLayer and IncomingCallPolicyEscalationRule.
- Updated index file to remove references to deleted migrations.
2026-01-19 11:34:58 +00:00
Nawaz Dhandala
5dd1f1a7f1 chore: Bump version to 9.4.0 2026-01-17 22:47:59 +00:00
Simon Larsen
80defab7b2 Merge pull request #2224 from OneUptime/on-call-route
On call route
2026-01-17 22:46:38 +00:00
Nawaz Dhandala
5dea6fcbad feat: Implement Twilio configuration helper and refactor related code for consistency 2026-01-17 22:46:14 +00:00
Nawaz Dhandala
5e6d5aebff refactor: Add type annotations for function return types in various components 2026-01-17 22:24:20 +00:00
Nawaz Dhandala
d7e8dc3d92 Refactor code for improved readability and maintainability
- Simplified import statements in PhoneNumber.ts and Config.ts.
- Consolidated async function calls in TwilioCallProvider.ts for clarity.
- Enhanced comment formatting and structure in TwilioCallProvider.ts.
- Reformatted SQL migration code for better readability in MigrationName.ts.
- Updated array syntax in Index.ts for consistency.
- Improved formatting and spacing in IncomingCallPolicyEscalationRuleService.ts.
- Standardized JSX formatting in various components for better readability.
- Enhanced conditional rendering logic in IncomingCallPolicies.tsx and Index.tsx.
- Cleaned up unnecessary whitespace and improved code consistency across multiple files.
2026-01-17 22:21:37 +00:00
Nawaz Dhandala
0e21d2755b feat: Add filters prop to IncomingCallPolicyLogViewPage for enhanced data handling 2026-01-17 22:21:02 +00:00
Nawaz Dhandala
52ed66fafe feat: Add Incoming Call Policy Log View and enhance routing and breadcrumbs 2026-01-17 21:56:33 +00:00
Nawaz Dhandala
37215aca51 feat: Update webhook URL handling for Twilio signature validation 2026-01-17 21:40:26 +00:00
Nawaz Dhandala
441cd5c73d feat: Add debug logging for incoming call webhook and Twilio signature validation 2026-01-17 21:38:59 +00:00
Nawaz Dhandala
046e0c00cd feat: Add IncomingCallLog and IncomingCallLogItem APIs for enhanced call management 2026-01-17 21:35:04 +00:00
Nawaz Dhandala
e0a9ab8cfb Enhance Twilio Call Provider and Nginx Configuration
- Updated TwilioCallProvider to support X-Forwarded-Proto and X-Forwarded-Host headers for improved webhook signature validation when behind proxies.
- Enabled trust proxy in StartServer to ensure correct interpretation of forwarded headers.
- Removed outdated incoming call policy documentation.
- Added Nginx configuration to handle X-Forwarded-Proto and X-Forwarded-Host headers, ensuring proper proxy behavior and preventing crashes when services are unavailable.
2026-01-17 21:18:27 +00:00
Nawaz Dhandala
ea47d2bd2c feat: Refactor OnCallDutyScheduleElement and SchedulesElement for improved schedule handling and navigation 2026-01-17 20:51:30 +00:00
Nawaz Dhandala
7b249f55b5 feat: Add migration for updating default values in OnCallDutyPolicyScheduleLayer and IncomingCallPolicyEscalationRule 2026-01-17 20:35:17 +00:00
Nawaz Dhandala
f261fe98f0 feat: Enhance IncomingCallPolicyEscalationRule service with order management and validation in escalation page 2026-01-17 20:34:12 +00:00
Nawaz Dhandala
3070b549e3 feat: Remove auto-generation of order in IncomingCallPolicyEscalationPage for improved item management 2026-01-17 20:18:56 +00:00
Nawaz Dhandala
4731a67ab4 feat: Revamp form structure in IncomingCallPolicyEscalationPage with multi-step navigation and updated field descriptions 2026-01-17 20:18:33 +00:00
Nawaz Dhandala
b204b05bc3 feat: Update notify type selection in IncomingCallPolicyEscalationPage to use dropdown with async options 2026-01-17 20:11:04 +00:00
Nawaz Dhandala
9217d869dd feat: Remove placeholder rendering in IncomingCallPolicyEscalationPage for improved UI clarity 2026-01-17 20:00:45 +00:00
Nawaz Dhandala
02e512e6e8 feat: Add notify type selection to IncomingCallPolicyEscalationPage with validation and auto-ordering 2026-01-17 19:51:25 +00:00
Nawaz Dhandala
475ad54a8f feat: Enhance IncomingCallPolicyEscalationPage with drag-and-drop support and improved field descriptions 2026-01-17 19:46:54 +00:00
Nawaz Dhandala
739cf632e5 feat: Enhance IncomingCallPolicyView to include escalation rules count and improve setup logic 2026-01-17 19:19:58 +00:00
Nawaz Dhandala
e069e94971 feat: Remove rendering of owned numbers and search results in PhoneNumberPurchase component 2026-01-17 19:03:51 +00:00
Nawaz Dhandala
391c9ea2e7 feat: Update webhook URL construction to use environment variables for protocol and host 2026-01-17 19:01:20 +00:00
Nawaz Dhandala
72a6b426ea feat: Refactor IncomingCallPolicyView to consolidate setup steps and improve layout 2026-01-17 18:54:45 +00:00
Nawaz Dhandala
86aca6a48e feat: Update terminology from "purchase" to "reserve" for phone number actions 2026-01-17 18:53:28 +00:00
Nawaz Dhandala
9b81a82eed feat: Add configuration modal for managing phone numbers with options to use existing or buy new 2026-01-17 18:50:05 +00:00
Nawaz Dhandala
c4ab245824 feat: Add IncomingCall icon to IconProp and update SideMenu to use it 2026-01-17 18:46:05 +00:00
Nawaz Dhandala
06cf878446 feat: Implement Twilio configuration modal with loading and no config warning states 2026-01-17 18:26:36 +00:00
Nawaz Dhandala
cd068f9219 feat: Adjust padding and gap in ConceptCards component for improved layout 2026-01-17 15:56:34 +00:00
Nawaz Dhandala
53c0b1fb92 feat: Refactor diagram components to unify color properties and improve UI consistency 2026-01-17 15:54:02 +00:00
Nawaz Dhandala
642a5a2982 feat: Add visual components for incoming call policy documentation including flow steps, escalation chain, and setup steps 2026-01-17 15:40:33 +00:00
Nawaz Dhandala
c0c162cca5 feat: Add documentation page for Incoming Call Policy with detailed setup and escalation flow 2026-01-17 15:34:18 +00:00
Nawaz Dhandala
c94a2db6fa feat: Add Twilio configuration modal for incoming call policy management 2026-01-17 15:28:08 +00:00
Nawaz Dhandala
3b4828eea1 feat: Add option to hide card wrapper in PhoneNumberPurchase component and improve UI for Twilio configuration steps 2026-01-17 14:00:48 +00:00
Nawaz Dhandala
5907bfe4d1 feat: Enhance Incoming Call Policy view with step indicators and Twilio configuration setup 2026-01-17 13:54:05 +00:00
Nawaz Dhandala
a2d9cda7d9 fix: Update dependency in useAsyncEffect to use modelId as a string for consistency 2026-01-17 13:48:43 +00:00
Nawaz Dhandala
a8be03d3c9 fix: Update voiceUrl type to include undefined for better type safety 2026-01-17 13:28:30 +00:00
Nawaz Dhandala
272ae08048 feat: Implement phone number management features including listing owned numbers and assigning existing numbers to policies 2026-01-17 13:25:12 +00:00
Nawaz Dhandala
5f2bda119a feat: Enhance Incoming Call Policy view with phone number routing and Twilio configuration setup 2026-01-17 13:07:50 +00:00
Nawaz Dhandala
64cfeb5400 feat: Add Incoming Call Policy Settings page and update routing and breadcrumbs 2026-01-17 12:56:05 +00:00
Nawaz Dhandala
1e1d3e939e feat: Add Incoming Call Policy and Escalation Rule APIs to BaseAPI feature set 2026-01-17 12:42:34 +00:00
Nawaz Dhandala
6894cae68c feat: Add project SMS notification checks to ensure notifications are enabled and balance is sufficient 2026-01-17 12:38:28 +00:00
Nawaz Dhandala
65b4a8217b feat: Add SMS balance check before sending notifications to prevent low balance issues 2026-01-17 12:35:48 +00:00
Nawaz Dhandala
5452342f2f feat: Enhance Incoming Call Policies page with form steps and improved labels description 2026-01-17 12:27:58 +00:00
Nawaz Dhandala
d1f583fb47 feat: Add project SMS notification checks in UserIncomingCallNumberService 2026-01-17 12:23:47 +00:00
Nawaz Dhandala
41f3a4ce21 fix: Correct default message in IncomingCallPolicy to improve clarity 2026-01-17 11:57:48 +00:00
Nawaz Dhandala
41f151b8eb feat: Add Incoming Call Policy link to navigation and create documentation 2026-01-17 11:55:12 +00:00
Nawaz Dhandala
60276876bd Refactor Incoming Call Policy Jobs
- Removed the `ReleasePhoneNumbersForCancelledSubscriptions` job as it is no longer needed.
- Removed the `SendWarningEmailsForPastDueSubscriptions` job due to redundancy.
- Updated `EmailTemplateType` to remove unused email templates related to incoming call policies.
- Adjusted the schema migrations index to include a trailing comma for consistency.
- Cleaned up imports in `Worker/Routes.ts` by removing references to the deleted jobs.
2026-01-17 11:47:15 +00:00
Nawaz Dhandala
86fb8fbb30 feat: Add migration to update IncomingCallPolicy and related tables 2026-01-17 11:37:11 +00:00
Nawaz Dhandala
5e9b5be0ad refactor: Remove unnecessary create permissions from IncomingCallLog model 2026-01-17 11:34:45 +00:00
Nawaz Dhandala
a9734dd18e refactor: Consolidate Twilio import statements and enhance type definitions for incoming phone number creation 2026-01-17 11:32:22 +00:00
Nawaz Dhandala
511c65a01b refactor: Simplify Twilio config retrieval and improve code formatting 2026-01-17 11:29:08 +00:00
Nawaz Dhandala
a619f323e7 refactor: Remove unused billing-related code and simplify Twilio configuration handling 2026-01-17 11:25:41 +00:00
Nawaz Dhandala
a021ad41ef feat: Remove alert phone number from User model and related migration 2026-01-17 11:04:12 +00:00
Nawaz Dhandala
1eddfff608 refactor: Improve logic for verifying incoming call numbers and user details retrieval 2026-01-17 11:02:17 +00:00
Nawaz Dhandala
578774df08 feat: Add Incoming Call Number Management
- Introduced UserIncomingCallNumber model to manage phone numbers for incoming call routing.
- Implemented UserIncomingCallNumberAPI for verification and resend functionality.
- Created UserIncomingCallNumberService to handle business logic for incoming call numbers.
- Added IncomingCallNumber component for UI management of incoming call numbers.
- Integrated IncomingCallPhoneNumbers page to display and manage incoming call numbers in user settings.
- Updated routing and breadcrumbs to include Incoming Call Policy section.
- Enhanced BaseAPI to include UserIncomingCallNumberAPI.
2026-01-17 10:58:30 +00:00
Nawaz Dhandala
82d0d68a7c refactor: Simplify phone number handling and improve subscription status checks 2026-01-17 09:51:27 +00:00
Nawaz Dhandala
8d9ba58964 refactor: Improve code readability and consistency across multiple files 2026-01-17 09:38:55 +00:00
Nawaz Dhandala
52dbab88f6 feat: Implement subscription cancellation handling for incoming call policies and add related email notifications 2026-01-17 09:29:12 +00:00
Nawaz Dhandala
387ebc9375 feat: Add project-level Twilio configuration support for incoming call policies 2026-01-17 09:17:32 +00:00
Nawaz Dhandala
26f3e5bd5e feat: Implement single webhook endpoint for incoming calls and update related documentation 2026-01-17 08:58:39 +00:00
Nawaz Dhandala
7ed06d7391 Merge branch 'master' into on-call-route 2026-01-16 21:11:29 +00:00
Nawaz Dhandala
3c2811000e refactor: Update comments for clarity in complex object handling in ResourceGenerator 2026-01-16 20:27:48 +00:00
Nawaz Dhandala
5979e4f345 feat: Add title display to SimpleLogViewer component 2026-01-16 20:26:34 +00:00
Nawaz Dhandala
5c84699bae fix: Enhance handling of complex object responses in ResourceGenerator 2026-01-16 20:24:42 +00:00
Simon Larsen
d153fc4cd4 Merge pull request #2229 from OneUptime/scim-log
SCIM log
2026-01-16 19:54:20 +00:00
Nawaz Dhandala
34475f76f9 refactor: Update type annotations for clarity and consistency in SCIM log components 2026-01-16 19:52:26 +00:00
Nawaz Dhandala
6565b7c803 refactor: Improve code formatting in SCIM and SCIM logs components for consistency 2026-01-16 19:47:21 +00:00
Nawaz Dhandala
c63923ed5b fix: Update SCIM user filtering to handle non-email usernames; log adjustments for clarity 2026-01-16 19:19:21 +00:00
Nawaz Dhandala
33d51932c5 refactor: Remove title prop from SimpleLogViewer in SCIM logs tables for consistency 2026-01-16 19:10:51 +00:00
Nawaz Dhandala
557d14106c feat: Enhance SimpleLogViewer with line numbers and height customization; update modal implementations across SCIM logs tables 2026-01-16 19:07:44 +00:00
Nawaz Dhandala
8d5395ae74 refactor: Replace ConfirmModal with Modal and integrate SimpleLogViewer for SCIM logs 2026-01-16 18:51:47 +00:00
Nawaz Dhandala
06e0100ede refactor: Remove unused fields from SCIM logs tables for cleaner display 2026-01-16 18:50:25 +00:00
Nawaz Dhandala
3db29ab264 fix: Update SCIM error logging to handle NotFoundException as a success case 2026-01-16 18:48:13 +00:00
Nawaz Dhandala
7442e36b18 feat: Implement "Unassigned" team management for SCIM provisioning 2026-01-16 18:41:15 +00:00
Nawaz Dhandala
1fa446ec0c feat: Add SCIM logging routes for Project and Status Page 2026-01-16 18:22:39 +00:00
Nawaz Dhandala
ef85d98362 Refactor SCIM configuration components and update modal descriptions
- Added a missing comma in the schema migrations index file.
- Improved formatting of the SCIM Logs table description for better readability.
- Refactored the SCIM settings page to enhance code clarity and maintainability, including restructuring form fields and action buttons.
- Updated SCIM URL confirmation modal to improve user instructions and formatting.
- Enhanced error and success modal handling for bearer token resets in the SCIM pages.
2026-01-16 17:21:27 +00:00
Nawaz Dhandala
46bccfb596 feat: Add migration for SCIM logging tables and constraints 2026-01-16 17:20:19 +00:00
Nawaz Dhandala
f7b2588647 Enhance SCIM logging and execution tracking in StatusPageSCIM API
- Added detailed execution steps tracking throughout the SCIM BulkOperation, ListUsers, GetUser, CreateUser, UpdateUser, and DeleteUser endpoints.
- Improved logging structure to include execution steps, user info, and additional context in SCIM logs.
- Updated SCIMLogger to handle new fields for query parameters, execution steps, user info, and additional context.
- Ensured all error handling paths also log execution steps for better traceability.
2026-01-16 17:15:45 +00:00
Nawaz Dhandala
b4106eb580 feat: Add SCIM logging functionality for projects and status pages
- Implemented ProjectSCIMLog and StatusPageSCIMLog models to store SCIM operation logs.
- Created services for managing ProjectSCIMLog and StatusPageSCIMLog entries with automatic deletion of old logs.
- Developed SCIMLogger utility for creating logs with sanitized sensitive data.
- Added SCIMLogStatus enum to represent the status of SCIM operations.
- Introduced ProjectSCIMLogsTable and StatusPageSCIMLogsTable components for displaying logs in the dashboard.
- Enhanced logging with detailed request/response information and error handling.
2026-01-16 16:42:10 +00:00
Nawaz Dhandala
de05f727d7 fix: Ensure tenantid is consistently set to an empty string in getDefaultHeaders method 2026-01-16 16:11:47 +00:00
Nawaz Dhandala
5a3d6d9ccc fix: Update tenantid in getDefaultHeaders method to use an empty string instead of null 2026-01-16 15:36:26 +00:00
Nawaz Dhandala
7d5f813bac chore: Bump version to 9.3.22 2026-01-16 14:35:06 +00:00
Nawaz Dhandala
d4cb2587c9 chore: Bump version to 9.3.21 2026-01-16 12:16:46 +00:00
Nawaz Dhandala
75ca86d92d refactor: Improve code formatting for better readability in StartAndEndDate and Input components 2026-01-16 12:16:37 +00:00
Nawaz Dhandala
ddf3dcd8a8 feat: Add resetSecondsAndMilliseconds method to OneUptimeDate class 2026-01-16 12:16:02 +00:00
Nawaz Dhandala
bc1a30f877 fix: Update date conversion methods to use Clickhouse format 2026-01-16 12:10:58 +00:00
Nawaz Dhandala
194d87041c feat: Add option to display seconds in date formatting for filters 2026-01-16 12:07:29 +00:00
Nawaz Dhandala
ba950928a4 feat: Add support for seconds in datetime-local input 2026-01-16 12:05:18 +00:00
Nawaz Dhandala
449f780201 refactor: Clean up tenant ID property declaration in API class 2026-01-16 11:37:01 +00:00
Nawaz Dhandala
b95fe3ad4f chore: Bump version to 9.3.20 2026-01-16 11:36:34 +00:00
Nawaz Dhandala
0dc3e5fe8d refactor: Remove unused props initialization in hasReadAccess method 2026-01-16 11:20:15 +00:00
Nawaz Dhandala
36e0b18f13 refactor: Remove redundant access check in hasReadAccess method 2026-01-16 11:19:45 +00:00
Nawaz Dhandala
c9e1a3b2b6 fix: Add null tenant ID to default headers in API class 2026-01-16 11:04:21 +00:00
Nawaz Dhandala
1a15e446ff refactor: Remove redundant comment about project ID validation 2026-01-16 10:29:52 +00:00
Nawaz Dhandala
3947b0bba1 fix: Enhance UUID validation in ObjectID and ProjectUtil classes 2026-01-16 10:29:20 +00:00
Nawaz Dhandala
3f24c910c0 feat: Introduce NotificationWebhookHost for consistent webhook URL construction 2026-01-14 21:57:13 +00:00
Nawaz Dhandala
8619ba379a feat: Validate webhook signature for incoming call and dial status callback 2026-01-14 21:49:18 +00:00
Nawaz Dhandala
34396c764e feat: Enhance call logging by creating logs early and updating statuses for various failure scenarios 2026-01-14 21:43:45 +00:00
Nawaz Dhandala
7ab6b8e135 feat: Remove redundant properties and access controls from IncomingCallPolicy model 2026-01-14 21:40:21 +00:00
Nawaz Dhandala
54c7955c78 feat: Simplify incoming call handling by removing concurrent call checks and related properties 2026-01-14 21:38:07 +00:00
Nawaz Dhandala
d5ae25545c feat: Refactor call cost handling by introducing configuration constants for minimum balance and price multipliers 2026-01-14 21:35:56 +00:00
Nawaz Dhandala
f449191f84 feat: Refactor Incoming Call and Phone Number handling, update icons and improve data validation 2026-01-14 21:31:57 +00:00
Nawaz Dhandala
3168b57e28 feat: Add Incoming Call Policy management features
- Implement IncomingCallLogItemService, IncomingCallLogService, IncomingCallPolicyService, and IncomingCallPolicyEscalationRuleService for handling database operations.
- Define types for call providers, including AvailablePhoneNumber, PurchasedPhoneNumber, and DialOptions.
- Create IncomingCallStatus enum to manage call statuses.
- Develop IncomingCallPolicies page with model table for managing incoming call policies.
- Add delete functionality for IncomingCallPolicy with a dedicated Delete component.
- Implement escalation rules management for IncomingCallPolicy with a dedicated Escalation component.
- Create detailed view for IncomingCallPolicy with Labels and status indicators.
- Establish layout and side menu for IncomingCallPolicy management.
- Add logs page to view incoming call history associated with policies.
2026-01-14 21:14:53 +00:00
Nawaz Dhandala
dbff165c34 Merge branch 'master' into on-call-route 2026-01-14 20:36:09 +00:00
Nawaz Dhandala
497394e5ee feat: Enhance Incoming Call Policy with cost deduction timing, webhook security, and user phone number verification flow 2026-01-14 19:45:41 +00:00
Nawaz Dhandala
bdc9683c04 feat: Revamp Incoming Call Policy to support provider-agnostic phone number management and enhance API endpoints 2026-01-14 19:36:50 +00:00
Nawaz Dhandala
ad5372e354 feat: Enhance Incoming Call Policy with phone number purchasing flow, API endpoints, and UI components 2026-01-14 19:18:14 +00:00
Nawaz Dhandala
72a31714a8 feat: Add Incoming Call Policy implementation plan with database models, API endpoints, and UI components 2026-01-14 19:01:17 +00:00
1461 changed files with 186857 additions and 18672 deletions

View File

@@ -33,6 +33,15 @@ stop
nohup.out*
# Large directories not needed for Docker builds
E2E/playwright-report
E2E/test-results
Terraform
HelmChart
Scripts
.git
GoSDK
encrypted-credentials.tar
encrypted-credentials/

View File

@@ -125,29 +125,6 @@ jobs:
max_attempts: 3
command: sudo docker build --no-cache -f ./Workflow/Dockerfile .
docker-build-api-reference:
runs-on: ubuntu-latest
env:
CI_PIPELINE_ID: ${{github.run_number}}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Preinstall
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
uses: nick-fields/retry@v3
with:
timeout_minutes: 45
max_attempts: 3
command: sudo docker build --no-cache -f ./APIReference/Dockerfile .
docker-build-docs:
runs-on: ubuntu-latest
env:

View File

@@ -128,23 +128,6 @@ jobs:
max_attempts: 3
command: cd Workflow && npm install && npm run compile && npm run dep-check
compile-api-reference:
runs-on: ubuntu-latest
env:
CI_PIPELINE_ID: ${{github.run_number}}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: latest
- run: cd Common && npm install
- 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
env:
@@ -389,6 +372,23 @@ jobs:
max_attempts: 3
command: cd MCP && npm update @oneuptime/common && npm install && npm run compile && npm run dep-check
compile-mobile-app:
runs-on: ubuntu-latest
env:
CI_PIPELINE_ID: ${{github.run_number}}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: latest
- run: cd Common && npm install && npm run compile
- name: Compile MobileApp
uses: nick-fields/retry@v3
with:
timeout_minutes: 30
max_attempts: 3
command: cd MobileApp && npm install && npm run compile
compile-ai-agent:
runs-on: ubuntu-latest
env:
@@ -404,4 +404,21 @@ jobs:
with:
timeout_minutes: 30
max_attempts: 3
command: cd AIAgent && npm install && npm run compile && npm run dep-check
command: cd AIAgent && npm install && npm run compile && npm run dep-check
compile-cli:
runs-on: ubuntu-latest
env:
CI_PIPELINE_ID: ${{github.run_number}}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: latest
- run: cd Common && npm install
- name: Compile CLI
uses: nick-fields/retry@v3
with:
timeout_minutes: 30
max_attempts: 3
command: cd CLI && npm install && npm run compile && npm run dep-check

View File

@@ -4,6 +4,13 @@ on:
push:
branches:
- "release"
workflow_dispatch:
inputs:
publish_android_to_store:
description: 'Publish Android app to Google Play Store'
required: false
type: boolean
default: false
jobs:
generate-build-number:
@@ -1701,7 +1708,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: '1.21'
go-version: 'stable'
cache: true
- name: Install GoReleaser
@@ -1736,75 +1743,6 @@ jobs:
api-reference-docker-image-deploy:
needs: [generate-build-number, read-version]
runs-on: ubuntu-latest
env:
QEMU_CPU: max
steps:
- name: Free Disk Space (Ubuntu)
uses: jlumbroso/free-disk-space@main
with:
tool-cache: false
android: true
dotnet: true
haskell: true
large-packages: true
docker-images: true
swap-storage: true
- name: Docker Meta
id: meta
uses: docker/metadata-action@v4
with:
images: |
oneuptime/api-reference
ghcr.io/oneuptime/api-reference
tags: |
type=raw,value=release,enable=true
type=semver,value=${{needs.read-version.outputs.major_minor}},pattern={{version}},enable=true
- uses: actions/checkout@v4
with:
ref: ${{ github.ref }}
- uses: actions/setup-node@v4
with:
node-version: latest
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
with:
image: tonistiigi/binfmt:qemu-v10.0.4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Generate Dockerfile from Dockerfile.tpl
run: npm run prerun
# Build and deploy nginx.
- name: Login to Docker Hub
run: |
echo "${{ secrets.DOCKERHUB_PASSWORD }}" | docker login --username "${{ secrets.DOCKERHUB_USERNAME }}" --password-stdin
- name: Login to GitHub Container Registry
run: |
echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io --username "${{ github.repository_owner }}" --password-stdin
- name: Build and push
run: |
bash ./Scripts/GHA/build_docker_images.sh \
--image api-reference \
--version "${{needs.read-version.outputs.major_minor}}" \
--dockerfile ./APIReference/Dockerfile \
--context . \
--platforms linux/amd64,linux/arm64 \
--git-sha "${{ github.sha }}"
push-release-tags:
name: Push release tags before GitHub release
needs:
@@ -1832,7 +1770,6 @@ jobs:
- docs-docker-image-deploy
- worker-docker-image-deploy
- workflow-docker-image-deploy
- api-reference-docker-image-deploy
- test-e2e-release-saas
- test-e2e-release-self-hosted
runs-on: ubuntu-latest
@@ -1861,8 +1798,7 @@ jobs:
"ai-agent",
"docs",
"worker",
"workflow",
"api-reference"
"workflow"
]
steps:
- name: Set up Docker Buildx
@@ -1909,16 +1845,55 @@ jobs:
test-e2e-release-saas:
runs-on: ubuntu-latest
needs: [telemetry-docker-image-deploy, mcp-docker-image-deploy, docs-docker-image-deploy, api-reference-docker-image-deploy, workflow-docker-image-deploy, accounts-docker-image-deploy, ai-agent-docker-image-deploy, admin-dashboard-docker-image-deploy, app-docker-image-deploy, dashboard-docker-image-deploy, probe-ingest-docker-image-deploy, server-monitor-ingest-docker-image-deploy, isolated-vm-docker-image-deploy, home-docker-image-deploy, worker-docker-image-deploy, otel-collector-docker-image-deploy, probe-docker-image-deploy, status-page-docker-image-deploy, test-docker-image-deploy, test-server-docker-image-deploy, publish-npm-packages, e2e-docker-image-deploy, helm-chart-deploy, generate-build-number, read-version, nginx-docker-image-deploy, incoming-request-ingest-docker-image-deploy]
needs: [telemetry-docker-image-deploy, mcp-docker-image-deploy, docs-docker-image-deploy, workflow-docker-image-deploy, accounts-docker-image-deploy, ai-agent-docker-image-deploy, admin-dashboard-docker-image-deploy, app-docker-image-deploy, dashboard-docker-image-deploy, probe-ingest-docker-image-deploy, server-monitor-ingest-docker-image-deploy, isolated-vm-docker-image-deploy, home-docker-image-deploy, worker-docker-image-deploy, otel-collector-docker-image-deploy, probe-docker-image-deploy, status-page-docker-image-deploy, test-docker-image-deploy, test-server-docker-image-deploy, publish-npm-packages, e2e-docker-image-deploy, helm-chart-deploy, generate-build-number, read-version, nginx-docker-image-deploy, incoming-request-ingest-docker-image-deploy]
env:
CI_PIPELINE_ID: ${{github.run_number}}
steps:
# Docker compose needs a lot of space to build images, so we need to free up some space first in the GitHub Actions runner
# Aggressively free disk space before anything else
- name: Aggressive Disk Cleanup
run: |
echo "=== Disk space BEFORE cleanup ==="
df -h /
# Remove pre-installed software not needed for this job
sudo rm -rf /usr/share/dotnet || true
sudo rm -rf /usr/local/lib/android || true
sudo rm -rf /opt/ghc || true
sudo rm -rf /opt/hostedtoolcache || true
sudo rm -rf /usr/local/share/boost || true
sudo rm -rf /usr/local/graalvm/ || true
sudo rm -rf /usr/local/share/powershell || true
sudo rm -rf /usr/local/share/chromium || true
sudo rm -rf /usr/local/lib/node_modules || true
sudo rm -rf /usr/share/swift || true
sudo rm -rf /usr/share/miniconda || true
sudo rm -rf /usr/lib/google-cloud-sdk || true
sudo rm -rf /usr/lib/jvm || true
sudo rm -rf /usr/lib/firefox || true
sudo rm -rf /usr/lib/heroku || true
sudo rm -rf /usr/local/julia* || true
sudo rm -rf /opt/az || true
sudo rm -rf /opt/microsoft || true
sudo rm -rf /opt/pipx || true
sudo rm -rf /opt/actionarchivecache || true
sudo rm -rf /imagegeneration || true
sudo rm -rf /usr/share/az_* || true
sudo rm -rf /usr/share/sbt || true
sudo rm -rf /usr/share/gradle* || true
sudo rm -rf /usr/share/kotlinc || true
sudo rm -rf /usr/share/ri || true
sudo rm -rf /usr/local/.ghcup || true
# Clean apt cache
sudo apt-get clean || true
sudo rm -rf /var/lib/apt/lists/* || true
# Clean temp files
sudo rm -rf /tmp/* || true
# Docker cleanup
docker system prune -af --volumes || true
echo "=== Disk space AFTER aggressive cleanup ==="
df -h /
- name: Free Disk Space (Ubuntu)
uses: jlumbroso/free-disk-space@main
with:
# this might remove tools that are actually needed,
# if set to "true" but frees about 6 GB
tool-cache: true
android: true
dotnet: true
@@ -1926,6 +1901,10 @@ jobs:
large-packages: true
docker-images: true
swap-storage: true
- name: Final Disk Space Check
run: |
echo "=== Disk space after all cleanup ==="
df -h /
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
@@ -1997,16 +1976,55 @@ jobs:
test-e2e-release-self-hosted:
runs-on: ubuntu-latest
# After all the jobs runs
needs: [telemetry-docker-image-deploy, mcp-docker-image-deploy, incoming-request-ingest-docker-image-deploy, docs-docker-image-deploy, api-reference-docker-image-deploy, workflow-docker-image-deploy, accounts-docker-image-deploy, ai-agent-docker-image-deploy, admin-dashboard-docker-image-deploy, app-docker-image-deploy, dashboard-docker-image-deploy, probe-ingest-docker-image-deploy, server-monitor-ingest-docker-image-deploy, isolated-vm-docker-image-deploy, home-docker-image-deploy, worker-docker-image-deploy, otel-collector-docker-image-deploy, probe-docker-image-deploy, status-page-docker-image-deploy, test-docker-image-deploy, test-server-docker-image-deploy, publish-npm-packages, e2e-docker-image-deploy, helm-chart-deploy, generate-build-number, read-version, nginx-docker-image-deploy]
needs: [telemetry-docker-image-deploy, mcp-docker-image-deploy, incoming-request-ingest-docker-image-deploy, docs-docker-image-deploy, workflow-docker-image-deploy, accounts-docker-image-deploy, ai-agent-docker-image-deploy, admin-dashboard-docker-image-deploy, app-docker-image-deploy, dashboard-docker-image-deploy, probe-ingest-docker-image-deploy, server-monitor-ingest-docker-image-deploy, isolated-vm-docker-image-deploy, home-docker-image-deploy, worker-docker-image-deploy, otel-collector-docker-image-deploy, probe-docker-image-deploy, status-page-docker-image-deploy, test-docker-image-deploy, test-server-docker-image-deploy, publish-npm-packages, e2e-docker-image-deploy, helm-chart-deploy, generate-build-number, read-version, nginx-docker-image-deploy]
env:
CI_PIPELINE_ID: ${{github.run_number}}
steps:
# Docker compose needs a lot of space to build images, so we need to free up some space first in the GitHub Actions runner
# Aggressively free disk space before anything else
- name: Aggressive Disk Cleanup
run: |
echo "=== Disk space BEFORE cleanup ==="
df -h /
# Remove pre-installed software not needed for this job
sudo rm -rf /usr/share/dotnet || true
sudo rm -rf /usr/local/lib/android || true
sudo rm -rf /opt/ghc || true
sudo rm -rf /opt/hostedtoolcache || true
sudo rm -rf /usr/local/share/boost || true
sudo rm -rf /usr/local/graalvm/ || true
sudo rm -rf /usr/local/share/powershell || true
sudo rm -rf /usr/local/share/chromium || true
sudo rm -rf /usr/local/lib/node_modules || true
sudo rm -rf /usr/share/swift || true
sudo rm -rf /usr/share/miniconda || true
sudo rm -rf /usr/lib/google-cloud-sdk || true
sudo rm -rf /usr/lib/jvm || true
sudo rm -rf /usr/lib/firefox || true
sudo rm -rf /usr/lib/heroku || true
sudo rm -rf /usr/local/julia* || true
sudo rm -rf /opt/az || true
sudo rm -rf /opt/microsoft || true
sudo rm -rf /opt/pipx || true
sudo rm -rf /opt/actionarchivecache || true
sudo rm -rf /imagegeneration || true
sudo rm -rf /usr/share/az_* || true
sudo rm -rf /usr/share/sbt || true
sudo rm -rf /usr/share/gradle* || true
sudo rm -rf /usr/share/kotlinc || true
sudo rm -rf /usr/share/ri || true
sudo rm -rf /usr/local/.ghcup || true
# Clean apt cache
sudo apt-get clean || true
sudo rm -rf /var/lib/apt/lists/* || true
# Clean temp files
sudo rm -rf /tmp/* || true
# Docker cleanup
docker system prune -af --volumes || true
echo "=== Disk space AFTER aggressive cleanup ==="
df -h /
- name: Free Disk Space (Ubuntu)
uses: jlumbroso/free-disk-space@main
with:
# this might remove tools that are actually needed,
# if set to "true" but frees about 6 GB
tool-cache: true
android: true
dotnet: true
@@ -2014,6 +2032,10 @@ jobs:
large-packages: true
docker-images: true
swap-storage: true
- name: Final Disk Space Check
run: |
echo "=== Disk space after all cleanup ==="
df -h /
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
@@ -2200,6 +2222,151 @@ jobs:
tag_name: ${{needs.read-version.outputs.major_minor}}
# Build Android release APK and attach to GitHub Release.
# Required secrets setup guide: MobileApp/docs/RELEASE_SIGNING.md
mobile-app-android-deploy:
needs: [draft-github-release, generate-build-number, read-version]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.ref }}
- name: Setup Java 17
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '17'
- uses: actions/setup-node@v4
with:
node-version: latest
- name: Install dependencies
run: cd MobileApp && npm install
- name: Generate native Android project
run: cd MobileApp && npx expo prebuild --platform android --no-install
- name: Decode Android keystore
run: |
echo "${{ secrets.ANDROID_KEYSTORE_BASE64 }}" | base64 --decode > /tmp/release.keystore
- name: Build release APK
env:
ANDROID_KEYSTORE_FILE: /tmp/release.keystore
ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
run: |
cd MobileApp/android
./gradlew assembleRelease \
-PversionName=${{ needs.read-version.outputs.major_minor }} \
-PversionCode=${{ needs.generate-build-number.outputs.build_number }}
- name: Upload APK to GitHub Release
uses: softprops/action-gh-release@v2
with:
files: MobileApp/android/app/build/outputs/apk/release/*.apk
token: ${{ secrets.GITHUB_TOKEN }}
draft: true
prerelease: false
tag_name: ${{ needs.read-version.outputs.major_minor }}
# Build iOS release IPA and attach to GitHub Release.
# Required secrets setup guide: MobileApp/docs/RELEASE_SIGNING.md
mobile-app-ios-deploy:
needs: [draft-github-release, generate-build-number, read-version]
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.ref }}
- uses: actions/setup-node@v4
with:
node-version: latest
- name: Install dependencies
run: cd MobileApp && npm install
- name: Generate native iOS project
run: cd MobileApp && npx expo prebuild --platform ios --no-install
- name: Install CocoaPods dependencies
run: cd MobileApp/ios && pod install
- name: Import signing certificate
env:
IOS_DISTRIBUTION_CERTIFICATE_BASE64: ${{ secrets.IOS_DISTRIBUTION_CERTIFICATE_BASE64 }}
IOS_DISTRIBUTION_CERTIFICATE_PASSWORD: ${{ secrets.IOS_DISTRIBUTION_CERTIFICATE_PASSWORD }}
run: |
CERTIFICATE_PATH=$RUNNER_TEMP/distribution.p12
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
KEYCHAIN_PASSWORD=$(openssl rand -base64 32)
echo "$IOS_DISTRIBUTION_CERTIFICATE_BASE64" | base64 --decode > "$CERTIFICATE_PATH"
security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
security set-keychain-settings -lut 21600 "$KEYCHAIN_PATH"
security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
security import "$CERTIFICATE_PATH" -P "$IOS_DISTRIBUTION_CERTIFICATE_PASSWORD" \
-A -t cert -f pkcs12 -k "$KEYCHAIN_PATH"
security set-key-partition-list -S apple-tool:,apple: -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
security list-keychain -d user -s "$KEYCHAIN_PATH"
- name: Install provisioning profile
env:
IOS_PROVISIONING_PROFILE_BASE64: ${{ secrets.IOS_PROVISIONING_PROFILE_BASE64 }}
run: |
PROFILE_PATH=$RUNNER_TEMP/profile.mobileprovision
echo "$IOS_PROVISIONING_PROFILE_BASE64" | base64 --decode > "$PROFILE_PATH"
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
cp "$PROFILE_PATH" ~/Library/MobileDevice/Provisioning\ Profiles/
- name: Build archive
run: |
cd MobileApp
xcodebuild -workspace ios/OneUptime.xcworkspace \
-scheme OneUptime \
-configuration Release \
-sdk iphoneos \
-archivePath $RUNNER_TEMP/OneUptime.xcarchive \
archive \
MARKETING_VERSION=${{ needs.read-version.outputs.major_minor }} \
CURRENT_PROJECT_VERSION=${{ needs.generate-build-number.outputs.build_number }}
- name: Prepare ExportOptions.plist with team ID
env:
IOS_TEAM_ID: ${{ secrets.IOS_TEAM_ID }}
run: |
/usr/libexec/PlistBuddy -c "Add :teamID string $IOS_TEAM_ID" MobileApp/ios/ExportOptions.plist || \
/usr/libexec/PlistBuddy -c "Set :teamID $IOS_TEAM_ID" MobileApp/ios/ExportOptions.plist
- name: Export IPA
run: |
cd MobileApp
xcodebuild -exportArchive \
-archivePath $RUNNER_TEMP/OneUptime.xcarchive \
-exportOptionsPlist ios/ExportOptions.plist \
-exportPath $RUNNER_TEMP/build
- name: Upload IPA to GitHub Release
uses: softprops/action-gh-release@v2
with:
files: ${{ runner.temp }}/build/*.ipa
token: ${{ secrets.GITHUB_TOKEN }}
draft: true
prerelease: false
tag_name: ${{ needs.read-version.outputs.major_minor }}
- name: Cleanup keychain
if: always()
run: |
security delete-keychain $RUNNER_TEMP/app-signing.keychain-db || true
finalize-github-release:
name: Publish GitHub release
needs: [infrastructure-agent-deploy, generate-build-number, read-version]
@@ -2241,3 +2408,69 @@ jobs:
throw new Error(`Failed to publish release for tag ${tag}: ${error.message ?? error}`);
}
# Publish Android app to Google Play Store.
# This job only runs when manually triggered via workflow_dispatch with publish_android_to_store=true.
# Required secrets:
# - GOOGLE_PLAY_SERVICE_ACCOUNT_JSON: Service account JSON key with Play Store publishing access
# - ANDROID_KEYSTORE_BASE64: Base64-encoded release keystore
# - ANDROID_KEYSTORE_PASSWORD: Keystore password
# - ANDROID_KEY_ALIAS: Signing key alias
# - ANDROID_KEY_PASSWORD: Signing key password
publish-android-to-play-store:
needs: [generate-build-number, read-version]
if: github.event_name == 'workflow_dispatch' && github.event.inputs.publish_android_to_store == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.ref }}
- name: Setup Java 17
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '17'
- uses: actions/setup-node@v4
with:
node-version: latest
- name: Install dependencies
run: cd MobileApp && npm install
- name: Generate native Android project
run: cd MobileApp && npx expo prebuild --platform android --no-install
- name: Decode Android keystore
run: |
echo "${{ secrets.ANDROID_KEYSTORE_BASE64 }}" | base64 --decode > /tmp/release.keystore
- name: Build release AAB for Play Store
env:
ANDROID_KEYSTORE_FILE: /tmp/release.keystore
ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
run: |
cd MobileApp/android
./gradlew bundleRelease \
-PversionName=${{ needs.read-version.outputs.major_minor }} \
-PversionCode=${{ needs.generate-build-number.outputs.build_number }}
- name: Upload AAB as build artifact
uses: actions/upload-artifact@v4
if: always()
with:
name: android-aab-${{ needs.read-version.outputs.major_minor }}
path: MobileApp/android/app/build/outputs/bundle/release/*.aab
retention-days: 90
- name: Upload AAB to Google Play Store
uses: r0adkll/upload-google-play@v1
with:
serviceAccountJsonPlainText: ${{ secrets.GOOGLE_PLAY_SERVICE_ACCOUNT_JSON }}
packageName: com.oneuptime.oncall
releaseFiles: MobileApp/android/app/build/outputs/bundle/release/*.aab
track: production
status: completed

View File

@@ -0,0 +1,143 @@
name: Terraform Provider E2E Tests
permissions:
contents: read
on:
pull_request:
push:
branches:
- main
- master
- develop
workflow_dispatch:
jobs:
terraform-e2e-tests:
runs-on: ubuntu-latest
timeout-minutes: 120
env:
CI_PIPELINE_ID: ${{ github.run_number }}
APP_TAG: latest
steps:
- name: Free Disk Space (Ubuntu)
uses: jlumbroso/free-disk-space@main
with:
tool-cache: true
android: true
dotnet: true
haskell: true
large-packages: true
docker-images: true
swap-storage: true
- name: Additional Disk Cleanup
run: |
echo "=== Initial disk space ==="
df -h
echo "=== Removing unnecessary tools and libraries ==="
# Remove Android SDK (if not already removed)
sudo rm -rf /usr/local/lib/android || true
# Remove .NET SDK and runtime
sudo rm -rf /usr/share/dotnet || true
sudo rm -rf /etc/skel/.dotnet || true
# Remove Haskell/GHC
sudo rm -rf /opt/ghc || true
sudo rm -rf /usr/local/.ghcup || true
# Remove CodeQL
sudo rm -rf /opt/hostedtoolcache/CodeQL || true
# Remove Boost
sudo rm -rf /usr/local/share/boost || true
# Remove Swift
sudo rm -rf /usr/share/swift || true
# Remove Julia
sudo rm -rf /usr/local/julia* || true
# Remove Rust (cargo/rustup)
sudo rm -rf /usr/share/rust || true
sudo rm -rf /home/runner/.rustup || true
sudo rm -rf /home/runner/.cargo || true
# Remove unnecessary hostedtoolcache items
sudo rm -rf /opt/hostedtoolcache/Python || true
sudo rm -rf /opt/hostedtoolcache/PyPy || true
sudo rm -rf /opt/hostedtoolcache/Ruby || true
sudo rm -rf /opt/hostedtoolcache/Java* || true
# Remove additional large directories
sudo rm -rf /usr/share/miniconda || true
sudo rm -rf /usr/local/graalvm || true
sudo rm -rf /usr/local/share/chromium || true
sudo rm -rf /usr/local/share/powershell || true
sudo rm -rf /usr/share/az_* || true
# Remove documentation
sudo rm -rf /usr/share/doc || true
sudo rm -rf /usr/share/man || true
# Remove unnecessary locales
sudo rm -rf /usr/share/locale || true
# Clean apt cache
sudo apt-get clean || true
sudo rm -rf /var/lib/apt/lists/* || true
sudo rm -rf /var/cache/apt/archives/* || true
# Clean tmp
sudo rm -rf /tmp/* || true
echo "=== Moving Docker data to /mnt for more space ==="
# Stop docker
sudo systemctl stop docker || true
# Move docker data directory to /mnt (which has ~70GB)
sudo mv /var/lib/docker /mnt/docker || true
sudo mkdir -p /var/lib/docker || true
sudo mount --bind /mnt/docker /var/lib/docker || true
# Restart docker
sudo systemctl start docker || true
echo "=== Final disk space ==="
df -h
echo "=== Docker info ==="
docker info | grep -E "Docker Root Dir|Storage Driver" || true
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: latest
cache: 'npm'
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: 'stable'
cache: true
- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_version: "1.6.0"
terraform_wrapper: false
- name: Run E2E Tests
uses: nick-fields/retry@v3
with:
timeout_minutes: 60
max_attempts: 3
command: |
chmod +x ./E2E/Terraform/e2e-tests/scripts/*.sh
./E2E/Terraform/e2e-tests/scripts/index.sh

View File

@@ -28,7 +28,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: '1.21'
go-version: 'stable'
cache: true
- name: Install Common dependencies

View File

@@ -1287,77 +1287,6 @@ jobs:
api-reference-docker-image-deploy:
needs: [read-version, generate-build-number]
runs-on: ubuntu-latest
env:
QEMU_CPU: max
steps:
- name: Free Disk Space (Ubuntu)
uses: jlumbroso/free-disk-space@main
with:
tool-cache: false
android: true
dotnet: true
haskell: true
large-packages: true
docker-images: true
swap-storage: true
- name: Docker Meta
id: meta
uses: docker/metadata-action@v4
with:
images: |
oneuptime/api-reference
ghcr.io/oneuptime/api-reference
tags: |
type=raw,value=test,enable=true
type=raw,value=${{needs.read-version.outputs.major_minor}}-test,enable=true
- uses: actions/checkout@v4
with:
ref: ${{ github.ref }}
- uses: actions/setup-node@v4
with:
node-version: latest
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
with:
image: tonistiigi/binfmt:qemu-v10.0.4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Generate Dockerfile from Dockerfile.tpl
run: npm run prerun
# Build and deploy app.
- name: Login to Docker Hub
run: |
echo "${{ secrets.DOCKERHUB_PASSWORD }}" | docker login --username "${{ secrets.DOCKERHUB_USERNAME }}" --password-stdin
- name: Login to GitHub Container Registry
run: |
echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io --username "${{ github.repository_owner }}" --password-stdin
- name: Build and push
run: |
bash ./Scripts/GHA/build_docker_images.sh \
--image api-reference \
--version "${{needs.read-version.outputs.major_minor}}-test" \
--dockerfile ./APIReference/Dockerfile \
--context . \
--platforms linux/amd64,linux/arm64 \
--git-sha "${{ github.sha }}" \
--extra-tags test \
--extra-enterprise-tags enterprise-test
accounts-docker-image-deploy:
needs: [read-version, generate-build-number]
runs-on: ubuntu-latest
@@ -1725,7 +1654,7 @@ jobs:
test-helm-chart:
runs-on: ubuntu-latest
needs: [infrastructure-agent-deploy, mcp-docker-image-deploy, publish-terraform-provider, telemetry-docker-image-deploy, docs-docker-image-deploy, worker-docker-image-deploy, workflow-docker-image-deploy, isolated-vm-docker-image-deploy, home-docker-image-deploy, api-reference-docker-image-deploy, test-server-docker-image-deploy, test-docker-image-deploy, probe-ingest-docker-image-deploy, server-monitor-ingest-docker-image-deploy, probe-docker-image-deploy, dashboard-docker-image-deploy, admin-dashboard-docker-image-deploy, app-docker-image-deploy, accounts-docker-image-deploy, ai-agent-docker-image-deploy, otel-collector-docker-image-deploy, status-page-docker-image-deploy, nginx-docker-image-deploy, e2e-docker-image-deploy, incoming-request-ingest-docker-image-deploy]
needs: [infrastructure-agent-deploy, mcp-docker-image-deploy, publish-terraform-provider, telemetry-docker-image-deploy, docs-docker-image-deploy, worker-docker-image-deploy, workflow-docker-image-deploy, isolated-vm-docker-image-deploy, home-docker-image-deploy, test-server-docker-image-deploy, test-docker-image-deploy, probe-ingest-docker-image-deploy, server-monitor-ingest-docker-image-deploy, probe-docker-image-deploy, dashboard-docker-image-deploy, admin-dashboard-docker-image-deploy, app-docker-image-deploy, accounts-docker-image-deploy, ai-agent-docker-image-deploy, otel-collector-docker-image-deploy, status-page-docker-image-deploy, nginx-docker-image-deploy, e2e-docker-image-deploy, incoming-request-ingest-docker-image-deploy]
env:
CI_PIPELINE_ID: ${{github.run_number}}
steps:

21
.github/workflows/test.cli.yaml vendored Normal file
View File

@@ -0,0 +1,21 @@
name: CLI Test
on:
pull_request:
push:
branches-ignore:
- 'hotfix-*' # excludes hotfix branches
- 'release'
jobs:
test:
runs-on: ubuntu-latest
env:
CI_PIPELINE_ID: ${{github.run_number}}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: latest
- run: cd Common && npm install
- run: cd CLI && npm install && npm run test

39
.github/workflows/test.mobile-app.yaml vendored Normal file
View File

@@ -0,0 +1,39 @@
name: MobileApp Test
on:
pull_request:
push:
branches-ignore:
- 'hotfix-*' # excludes hotfix branches
- 'release'
jobs:
expo-doctor:
runs-on: ubuntu-latest
env:
CI_PIPELINE_ID: ${{github.run_number}}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: latest
- run: cd MobileApp && npm install
- name: Run Expo Doctor
run: cd MobileApp && npx expo-doctor@latest
expo-web-export:
runs-on: ubuntu-latest
env:
CI_PIPELINE_ID: ${{github.run_number}}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: latest
- run: cd MobileApp && npm install
- name: Export Web Bundle
uses: nick-fields/retry@v3
with:
timeout_minutes: 30
max_attempts: 3
command: cd MobileApp && npx expo export --platform web

13
.gitignore vendored
View File

@@ -116,8 +116,8 @@ InfrastructureAgent/oneuptime-infrastructure-agent
# Terraform generated files
openapi.json
Terraform/**
Terraform/terraform-provider-oneuptime/**
Terraform/openapi.json
TerraformTest/**
terraform-provider-example/**
@@ -129,3 +129,12 @@ MCP/node_modules
Dashboard/public/sw.js
.claude/settings.local.json
Common/.claude/settings.local.json
E2E/Terraform/e2e-tests/test-env.sh
# Terraform state and plan files
*.tfplan
tfplan
terraform.tfstate
terraform.tfstate.backup
.terraform/
.terraform.lock.hcl

View File

@@ -49,5 +49,4 @@ LICENSE
marketing/*/*
licenses/*
certifications/*
ApiReference/public/assets/*
JavaScriptSDK/src/cli/server-monitor/out/scripts/prettify/*

14
.vscode/launch.json vendored
View File

@@ -105,20 +105,6 @@
"restart": true,
"autoAttachChildProcesses": true
},
{
"address": "127.0.0.1",
"localRoot": "${workspaceFolder}/APIReference",
"name": "API Reference: Debug with Docker",
"port": 8737,
"remoteRoot": "/usr/src/app",
"request": "attach",
"skipFiles": [
"<node_internals>/**"
],
"type": "node",
"restart": true,
"autoAttachChildProcesses": true
},
{
"address": "127.0.0.1",
"localRoot": "${workspaceFolder}/TestServer",

View File

@@ -73,15 +73,18 @@
"ejs": "^3.1.10",
"elkjs": "^0.10.0",
"esbuild": "^0.25.5",
"expo-server-sdk": "^3.15.0",
"express": "^4.21.1",
"formik": "^2.4.6",
"history": "^5.3.0",
"ioredis": "^5.3.2",
"isolated-vm": "^6.0.2",
"json2csv": "^5.0.7",
"json5": "^2.2.3",
"jsonwebtoken": "^9.0.0",
"jwt-decode": "^4.0.0",
"marked": "^12.0.2",
"mermaid": "^11.12.2",
"moment": "^2.30.1",
"moment-timezone": "^0.5.45",
"multer": "^2.0.2",
@@ -101,7 +104,7 @@
"react-dropzone": "^14.2.2",
"react-error-boundary": "^4.0.13",
"react-highlight": "^0.15.0",
"react-markdown": "^8.0.3",
"react-markdown": "^9.0.0",
"react-router-dom": "^6.30.1",
"react-select": "^5.4.0",
"react-spinners": "^0.14.1",
@@ -111,7 +114,7 @@
"recharts": "^2.12.7",
"redis-semaphore": "^5.5.1",
"reflect-metadata": "^0.2.2",
"remark-gfm": "^3.0.1",
"remark-gfm": "^4.0.0",
"slackify-markdown": "^4.4.0",
"slugify": "^1.6.5",
"socket.io": "^4.7.4",
@@ -1808,9 +1811,9 @@
}
},
"node_modules/diff": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz",
"integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==",
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.3.1"

View File

@@ -1,56 +0,0 @@
.git
node_modules
# See https://help.github.com/ignore-files/ for more about ignoring files.
# dependencies
/node_modules
node_modules
.idea
# testing
/coverage
# production
/build
# misc
.DS_Store
env.js
npm-debug.log*
yarn-debug.log*
yarn-error.log*
yarn.lock
Untitled-1
*.local.sh
*.local.yaml
run
stop
nohup.out*
encrypted-credentials.tar
encrypted-credentials/
_README.md
# Important Add production values to gitignore.
values-saas-production.yaml
kubernetes/values-saas-production.yaml
/private
/tls_cert.pem
/tls_key.pem
/keys
temp_readme.md
tests/coverage
settings.json
GoSDK/tester/

View File

@@ -1 +0,0 @@
*.js text eol=lf

View File

@@ -1,30 +0,0 @@
# See https://help.github.com/ignore-files/ for more about ignoring files.
# dependencies
#/backend/node_modules
/kubernetes
/node_modules
.idea
# misc
.DS_Store
npm-debug.log*
yarn-debug.log*
yarn-error.log*
yarn.lock
**/*/paymentService.test.js
apiTest.rest
application_security_dir
container_security_dir
# coverage
/coverage
/.nyc_output
/greenlock.d/config.json
/greenlock.d/config.json.bak
/.greenlockrc

View File

@@ -1,75 +0,0 @@
#
# OneUptime-App Dockerfile
#
# Pull base image nodejs image.
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
RUN npm config set fetch-retry-mintimeout 20000
RUN npm config set fetch-retry-maxtimeout 60000
ARG GIT_SHA
ARG APP_VERSION
ARG IS_ENTERPRISE_EDITION=false
ENV GIT_SHA=${GIT_SHA}
ENV APP_VERSION=${APP_VERSION}
ENV IS_ENTERPRISE_EDITION=${IS_ENTERPRISE_EDITION}
ENV PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1
# IF APP_VERSION is not set, set it to 1.0.0
RUN if [ -z "$APP_VERSION" ]; then export APP_VERSION=1.0.0; fi
# Install bash.
RUN apk add bash && apk add curl
# Install python
RUN apk update && apk add --no-cache --virtual .gyp python3 make g++
#Use bash shell by default
SHELL ["/bin/bash", "-c"]
RUN mkdir /usr/src
WORKDIR /usr/src/Common
COPY ./Common/package*.json /usr/src/Common/
# Set version in ./Common/package.json to the APP_VERSION
RUN sed -i "s/\"version\": \".*\"/\"version\": \"$APP_VERSION\"/g" /usr/src/Common/package.json
RUN npm install
COPY ./Common /usr/src/Common
ENV PRODUCTION=true
WORKDIR /usr/src/app
# Install app dependencies
COPY ./APIReference/package*.json /usr/src/app/
# Set version in ./App/package.json to the APP_VERSION
RUN sed -i "s/\"version\": \".*\"/\"version\": \"$APP_VERSION\"/g" /usr/src/app/package.json
RUN npm install
# Expose ports.
# - 1446: OneUptime-api-reference
EXPOSE 1446
{{ if eq .Env.ENVIRONMENT "development" }}
#Run the app
CMD [ "npm", "run", "dev" ]
{{ else }}
# Copy app source
COPY ./APIReference /usr/src/app
# Bundle app source
RUN npm run compile
# Set permission to write logs and cache in case container run as non root
RUN chown -R 1000:1000 "/tmp/npm" && chmod -R 2777 "/tmp/npm"
#Run the app
CMD [ "npm", "start" ]
{{ end }}

View File

@@ -1,52 +0,0 @@
import APIReferenceRoutes from "./Routes";
import { PromiseVoidFunction } from "Common/Types/FunctionTypes";
import InfrastructureStatus from "Common/Server/Infrastructure/Status";
import logger from "Common/Server/Utils/Logger";
import App from "Common/Server/Utils/StartServer";
import Telemetry from "Common/Server/Utils/Telemetry";
import "ejs";
const APP_NAME: string = "reference";
const init: PromiseVoidFunction = async (): Promise<void> => {
try {
// Initialize telemetry
Telemetry.init({
serviceName: APP_NAME,
});
const statusCheck: PromiseVoidFunction = async (): Promise<void> => {
// Check the status of infrastructure components
return await InfrastructureStatus.checkStatusWithRetry({
checkClickhouseStatus: false,
checkPostgresStatus: false,
checkRedisStatus: false,
retryCount: 3,
});
};
// Initialize the app with service name and status checks
await App.init({
appName: APP_NAME,
statusOptions: {
liveCheck: statusCheck,
readyCheck: statusCheck,
},
});
await APIReferenceRoutes.init();
// Add default routes to the app
await App.addDefaultRoutes();
} catch (err) {
logger.error("App Init Failed:");
logger.error(err);
throw err;
}
};
init().catch((err: Error) => {
logger.error(err);
logger.error("Exiting node process");
process.exit(1);
});

View File

@@ -1,29 +0,0 @@
# README
This README would normally document whatever steps are necessary to get your application up and running.
### What is this repository for?
- Quick summary
- Version
- [Learn Markdown](https://bitbucket.org/tutorials/markdowndemo)
### How do I get set up?
- Summary of set up
- Configuration
- Dependencies
- Database configuration
- How to run tests
- Deployment instructions
### Contribution guidelines
- Writing tests
- Code review
- Other guidelines
### Who do I talk to?
- Repo owner or admin
- Other community or team contact

View File

@@ -1,3 +0,0 @@
export const ViewsPath: string = "/usr/src/app/views";
export const StaticPath: string = "/usr/src/app/Static";
export const CodeExamplesPath: string = "/usr/src/app/CodeExamples";

View File

@@ -1,14 +0,0 @@
{
"watch": ["./","../Common/Server", "../Common/Types", "../Common/Utils", "../Common/Models"],
"ext": "ts,tsx",
"ignore": [
"./node_modules/**",
"./public/**",
"./bin/**",
"./build/**",
"greenlock.d/*"
],
"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"
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,33 +0,0 @@
{
"name": "@oneuptime/api-reference",
"version": "1.0.0",
"description": "",
"repository": {
"type": "git",
"url": "https://github.com/OneUptime/oneuptime"
},
"main": "Index.ts",
"scripts": {
"start": "export NODE_OPTIONS='--max-old-space-size=8096' && node --require ts-node/register Index.ts",
"compile": "tsc",
"clear-modules": "rm -rf node_modules && rm package-lock.json && npm install",
"dev": "npx nodemon",
"audit": "npm audit --audit-level=low",
"dep-check": "npm install -g depcheck && depcheck ./ --skip-missing=true",
"test": "rm -rf build && jest --detectOpenHandles --passWithNoTests",
"coverage": "jest --detectOpenHandles --coverage"
},
"author": "OneUptime <hello@oneuptime.com> (https://oneuptime.com/)",
"license": "Apache-2.0",
"dependencies": {
"Common": "file:../Common",
"ejs": "^3.1.9",
"ts-node": "^10.9.1"
},
"devDependencies": {
"@types/jest": "^29.5.11",
"@types/node": "^17.0.31",
"jest": "^28.1.0",
"nodemon": "^2.0.20"
}
}

View File

@@ -1,112 +0,0 @@
{
"ts-node": {
// these options are overrides used only by ts-node
// same as the --compilerOptions flag and the TS_NODE_COMPILER_OPTIONS environment variable
"compilerOptions": {
"module": "commonjs",
"resolveJsonModule": true,
}
},
"compilerOptions": {
/* Visit https://aka.ms/tsconfig.json to read more about this file */
/* Projects */
// "incremental": true, /* Enable incremental compilation */
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
// "tsBuildInfoFile": "./", /* Specify the folder for .tsbuildinfo incremental compilation files. */
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects */
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
/* Language and Environment */
"target": "es2017" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
"jsx": "react" /* Specify what JSX code is generated. */,
"experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
"emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h' */
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using `jsx: react-jsx*`.` */
// "reactNamespace": "", /* Specify the object invoked for `createElement`. This only applies when targeting `react` JSX emit. */
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
/* Modules */
// "module": "es2022" /* Specify what module code is generated. */,
// "rootDir": "./", /* Specify the root folder within your source files. */
"moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
"typeRoots": [
"./node_modules/@types"
], /* Specify multiple folders that act like `./node_modules/@types`. */
"types": ["node", "jest"], /* Specify type package names to be included without being referenced in a source file. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
// "resolveJsonModule": true, /* Enable importing .json files */
// "noResolve": true, /* Disallow `import`s, `require`s or `<reference>`s from expanding the number of files TypeScript should add to a project. */
/* JavaScript Support */
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */
/* Emit */
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
"sourceMap": true, /* Create source map files for emitted JavaScript files. */
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */
"outDir": "./build/dist", /* Specify an output folder for all emitted files. */
// "removeComments": true, /* Disable emitting comments. */
// "noEmit": true, /* Disable emitting files from a compilation. */
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types */
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
// "newLine": "crlf", /* Set the newline character for emitting files. */
// "stripInternal": true, /* Disable emitting declarations that have `@internal` in their JSDoc comments. */
// "noEmitHelpers": true, /* Disable generating custom helper functions like `__extends` in compiled output. */
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
// "preserveConstEnums": true, /* Disable erasing `const enum` declarations in generated code. */
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
/* Interop Constraints */
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
"esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */,
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
"forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
/* Type Checking */
"strict": true /* Enable all strict type-checking options. */,
"noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied `any` type.. */
"strictNullChecks": true, /* When type checking, take into account `null` and `undefined`. */
"strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
"strictBindCallApply": true, /* Check that the arguments for `bind`, `call`, and `apply` methods match the original function. */
"strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
"noImplicitThis": true, /* Enable error reporting when `this` is given the type `any`. */
"useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */
"alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
"noUnusedLocals": true, /* Enable error reporting when a local variables aren't read. */
"noUnusedParameters": true, /* Raise an error when a function parameter isn't read */
"exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
"noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
"noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
"noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */
"noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
"noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type */
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
/* Completeness */
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
"skipLibCheck": true, /* Skip type checking all .d.ts files. */
"resolveJsonModule": true
}
}

View File

@@ -1,36 +0,0 @@
<main class="py-12">
<article class="prose">
<!-- Hero Section -->
<div class="mb-10">
<div class="flex items-center gap-3 mb-4">
<div class="flex items-center justify-center w-10 h-10 rounded-xl bg-indigo-600 shadow-lg shadow-indigo-500/30">
<svg class="w-5 h-5 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z"></path>
</svg>
</div>
<span class="text-xs font-semibold text-indigo-600 uppercase tracking-wider">Guide</span>
</div>
<h1 class="font-bold text-3xl text-slate-900 tracking-tight mb-3">Permissions</h1>
<p class="text-lg text-slate-600 leading-relaxed max-w-2xl">Your API Token needs permissions to create, update, read or delete any resource. If you do not have permissions to make a request a <code class="inline-code">4xx</code> status will be sent as response. You can manage permissions for your API Key in Project Settings > API Keys.</p>
</div>
<h2 id="consuming-webhooks" class="scroll-mt-24 text-xl font-semibold text-slate-900 mb-6 mt-12">
Permissions List
</h2>
<p class="text-slate-600 leading-relaxed mb-6">Here is a list of all the permissions:</p>
<div class="rounded-xl border border-slate-200 bg-white overflow-hidden">
<ul role="list" class="m-0 divide-y divide-slate-100 p-0">
<% for(var i=0; i<pageData.permissions.length; i++) {%>
<li class="m-0 px-5 py-4 hover:bg-slate-50/50 transition-colors">
<dl class="m-0 flex flex-wrap items-center gap-x-3 gap-y-2">
<dd><code class="inline-code"><%= pageData.permissions[i].permission -%></code></dd>
<dd class="font-mono text-xs text-slate-500"><%= pageData.permissions[i].title -%></dd>
<dd class="w-full flex-none text-sm text-slate-600 mt-1"><%= pageData.permissions[i].description -%></dd>
</dl>
</li>
<% } %>
</ul>
</div>
</article>
</main>

26
Accounts/index.d.ts vendored
View File

@@ -2,3 +2,29 @@ declare module "*.png";
declare module "*.svg";
declare module "*.jpg";
declare module "*.gif";
declare module "react-syntax-highlighter/dist/esm/prism-light";
declare module "react-syntax-highlighter/dist/esm/styles/prism";
declare module "react-syntax-highlighter/dist/esm/languages/prism/javascript";
declare module "react-syntax-highlighter/dist/esm/languages/prism/typescript";
declare module "react-syntax-highlighter/dist/esm/languages/prism/jsx";
declare module "react-syntax-highlighter/dist/esm/languages/prism/tsx";
declare module "react-syntax-highlighter/dist/esm/languages/prism/python";
declare module "react-syntax-highlighter/dist/esm/languages/prism/bash";
declare module "react-syntax-highlighter/dist/esm/languages/prism/json";
declare module "react-syntax-highlighter/dist/esm/languages/prism/yaml";
declare module "react-syntax-highlighter/dist/esm/languages/prism/sql";
declare module "react-syntax-highlighter/dist/esm/languages/prism/go";
declare module "react-syntax-highlighter/dist/esm/languages/prism/java";
declare module "react-syntax-highlighter/dist/esm/languages/prism/css";
declare module "react-syntax-highlighter/dist/esm/languages/prism/markup";
declare module "react-syntax-highlighter/dist/esm/languages/prism/markdown";
declare module "react-syntax-highlighter/dist/esm/languages/prism/docker";
declare module "react-syntax-highlighter/dist/esm/languages/prism/rust";
declare module "react-syntax-highlighter/dist/esm/languages/prism/c";
declare module "react-syntax-highlighter/dist/esm/languages/prism/cpp";
declare module "react-syntax-highlighter/dist/esm/languages/prism/csharp";
declare module "react-syntax-highlighter/dist/esm/languages/prism/ruby";
declare module "react-syntax-highlighter/dist/esm/languages/prism/php";
declare module "react-syntax-highlighter/dist/esm/languages/prism/graphql";
declare module "react-syntax-highlighter/dist/esm/languages/prism/http";

View File

@@ -77,15 +77,18 @@
"ejs": "^3.1.10",
"elkjs": "^0.10.0",
"esbuild": "^0.25.5",
"expo-server-sdk": "^3.15.0",
"express": "^4.21.1",
"formik": "^2.4.6",
"history": "^5.3.0",
"ioredis": "^5.3.2",
"isolated-vm": "^6.0.2",
"json2csv": "^5.0.7",
"json5": "^2.2.3",
"jsonwebtoken": "^9.0.0",
"jwt-decode": "^4.0.0",
"marked": "^12.0.2",
"mermaid": "^11.12.2",
"moment": "^2.30.1",
"moment-timezone": "^0.5.45",
"multer": "^2.0.2",
@@ -105,7 +108,7 @@
"react-dropzone": "^14.2.2",
"react-error-boundary": "^4.0.13",
"react-highlight": "^0.15.0",
"react-markdown": "^8.0.3",
"react-markdown": "^9.0.0",
"react-router-dom": "^6.30.1",
"react-select": "^5.4.0",
"react-spinners": "^0.14.1",
@@ -115,7 +118,7 @@
"recharts": "^2.12.7",
"redis-semaphore": "^5.5.1",
"reflect-metadata": "^0.2.2",
"remark-gfm": "^3.0.1",
"remark-gfm": "^4.0.0",
"slackify-markdown": "^4.4.0",
"slugify": "^1.6.5",
"socket.io": "^4.7.4",
@@ -474,20 +477,6 @@
"node": ">=0.4.0"
}
},
"node_modules/ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dependencies": {
"color-convert": "^2.0.1"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
"node_modules/anymatch": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
@@ -531,6 +520,7 @@
"version": "1.1.12",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
"dev": true,
"license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0",
@@ -550,40 +540,6 @@
"node": ">=8"
}
},
"node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/chalk/node_modules/has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"engines": {
"node": ">=8"
}
},
"node_modules/chalk/node_modules/supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dependencies": {
"has-flag": "^4.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/chokidar": {
"version": "3.5.3",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
@@ -611,22 +567,6 @@
"fsevents": "~2.3.2"
}
},
"node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dependencies": {
"color-name": "~1.1.4"
},
"engines": {
"node": ">=7.0.0"
}
},
"node_modules/color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
"node_modules/Common": {
"resolved": "../Common",
"link": true
@@ -634,7 +574,8 @@
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
"dev": true
},
"node_modules/create-require": {
"version": "1.1.1",
@@ -658,10 +599,11 @@
}
},
"node_modules/diff": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz",
"integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==",
"dev": true,
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.3.1"
}
@@ -809,15 +751,14 @@
}
},
"node_modules/jake": {
"version": "10.9.2",
"resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz",
"integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==",
"version": "10.9.4",
"resolved": "https://registry.npmjs.org/jake/-/jake-10.9.4.tgz",
"integrity": "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==",
"license": "Apache-2.0",
"dependencies": {
"async": "^3.2.3",
"chalk": "^4.0.2",
"async": "^3.2.6",
"filelist": "^1.0.4",
"minimatch": "^3.1.2"
"picocolors": "^1.1.1"
},
"bin": {
"jake": "bin/cli.js"
@@ -852,6 +793,7 @@
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dev": true,
"dependencies": {
"brace-expansion": "^1.1.7"
},
@@ -926,6 +868,12 @@
"node": ">=0.10.0"
}
},
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
"license": "ISC"
},
"node_modules/picomatch": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",

View File

@@ -32,6 +32,7 @@ import Reseller from "Common/Models/DatabaseModels/Reseller";
import User from "Common/Models/DatabaseModels/User";
import React, { useState } from "react";
import useAsyncEffect from "use-async-effect";
import { IsBillingEnabled } from "Common/Server/EnvironmentConfig";
const RegisterPage: () => JSX.Element = () => {
const apiUrl: URL = SIGNUP_API_URL;
@@ -172,6 +173,36 @@ const RegisterPage: () => JSX.Element = () => {
}
}
if (!BILLING_ENABLED) {
formFields = formFields.concat([
{
overrideField: {
selfHostedCompanyName: true,
},
overrideFieldKey: "selfHostedCompanyName",
fieldType: FormFieldSchemaType.Text,
placeholder: "Acme, Inc.",
required: false,
title: "Company Name",
dataTestId: "selfHostedCompanyName",
showEvenIfPermissionDoesNotExist: true,
disableSpellCheck: true,
},
{
overrideField: {
selfHostedPhoneNumber: true,
},
overrideFieldKey: "selfHostedPhoneNumber",
fieldType: FormFieldSchemaType.Phone,
required: false,
placeholder: "+11234567890",
title: "Phone Number",
dataTestId: "selfHostedPhoneNumber",
showEvenIfPermissionDoesNotExist: true,
},
]);
}
formFields = formFields.concat([
{
field: {
@@ -206,6 +237,25 @@ const RegisterPage: () => JSX.Element = () => {
},
]);
if (!IsBillingEnabled) {
formFields = formFields.concat([
{
overrideField: {
notifySelfHosted: true,
},
overrideFieldKey: "notifySelfHosted",
fieldType: FormFieldSchemaType.Checkbox,
required: false,
defaultValue: true,
title: "Notify me about security patches and new releases",
dataTestId: "notifySelfHosted",
showEvenIfPermissionDoesNotExist: true,
spanFullRow: true,
},
]);
}
if (isCaptchaEnabled) {
formFields = formFields.concat([
{
@@ -330,6 +380,7 @@ const RegisterPage: () => JSX.Element = () => {
if (value && value.email) {
UiAnalytics.userAuth(value.email);
UiAnalytics.capture("accounts/register");
UiAnalytics.capture("sign_up");
}
LoginUtil.login({

View File

@@ -42,10 +42,11 @@
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="preload" href="https://fonts.googleapis.com/css2?family=Inter:wght@100;200;300;400;500;600;700;800;900&display=swap" as="style">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@100;200;300;400;500;600;700;800;900&display=swap" rel="stylesheet">
<style>
* {
font-family: Inter;
font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
}
@@ -75,6 +76,7 @@
width: auto;
}
</style>
<link rel="preload" href="/accounts/assets/js/tailwind-3.4.5.js" as="script">
<script src="/accounts/assets/js/tailwind-3.4.5.js"></script>
<title>OneUptime Accounts</title>

View File

@@ -2,3 +2,29 @@ declare module "*.png";
declare module "*.svg";
declare module "*.jpg";
declare module "*.gif";
declare module "react-syntax-highlighter/dist/esm/prism-light";
declare module "react-syntax-highlighter/dist/esm/styles/prism";
declare module "react-syntax-highlighter/dist/esm/languages/prism/javascript";
declare module "react-syntax-highlighter/dist/esm/languages/prism/typescript";
declare module "react-syntax-highlighter/dist/esm/languages/prism/jsx";
declare module "react-syntax-highlighter/dist/esm/languages/prism/tsx";
declare module "react-syntax-highlighter/dist/esm/languages/prism/python";
declare module "react-syntax-highlighter/dist/esm/languages/prism/bash";
declare module "react-syntax-highlighter/dist/esm/languages/prism/json";
declare module "react-syntax-highlighter/dist/esm/languages/prism/yaml";
declare module "react-syntax-highlighter/dist/esm/languages/prism/sql";
declare module "react-syntax-highlighter/dist/esm/languages/prism/go";
declare module "react-syntax-highlighter/dist/esm/languages/prism/java";
declare module "react-syntax-highlighter/dist/esm/languages/prism/css";
declare module "react-syntax-highlighter/dist/esm/languages/prism/markup";
declare module "react-syntax-highlighter/dist/esm/languages/prism/markdown";
declare module "react-syntax-highlighter/dist/esm/languages/prism/docker";
declare module "react-syntax-highlighter/dist/esm/languages/prism/rust";
declare module "react-syntax-highlighter/dist/esm/languages/prism/c";
declare module "react-syntax-highlighter/dist/esm/languages/prism/cpp";
declare module "react-syntax-highlighter/dist/esm/languages/prism/csharp";
declare module "react-syntax-highlighter/dist/esm/languages/prism/ruby";
declare module "react-syntax-highlighter/dist/esm/languages/prism/php";
declare module "react-syntax-highlighter/dist/esm/languages/prism/graphql";
declare module "react-syntax-highlighter/dist/esm/languages/prism/http";

View File

@@ -76,15 +76,18 @@
"ejs": "^3.1.10",
"elkjs": "^0.10.0",
"esbuild": "^0.25.5",
"expo-server-sdk": "^3.15.0",
"express": "^4.21.1",
"formik": "^2.4.6",
"history": "^5.3.0",
"ioredis": "^5.3.2",
"isolated-vm": "^6.0.2",
"json2csv": "^5.0.7",
"json5": "^2.2.3",
"jsonwebtoken": "^9.0.0",
"jwt-decode": "^4.0.0",
"marked": "^12.0.2",
"mermaid": "^11.12.2",
"moment": "^2.30.1",
"moment-timezone": "^0.5.45",
"multer": "^2.0.2",
@@ -104,7 +107,7 @@
"react-dropzone": "^14.2.2",
"react-error-boundary": "^4.0.13",
"react-highlight": "^0.15.0",
"react-markdown": "^8.0.3",
"react-markdown": "^9.0.0",
"react-router-dom": "^6.30.1",
"react-select": "^5.4.0",
"react-spinners": "^0.14.1",
@@ -114,7 +117,7 @@
"recharts": "^2.12.7",
"redis-semaphore": "^5.5.1",
"reflect-metadata": "^0.2.2",
"remark-gfm": "^3.0.1",
"remark-gfm": "^4.0.0",
"slackify-markdown": "^4.4.0",
"slugify": "^1.6.5",
"socket.io": "^4.7.4",
@@ -478,9 +481,10 @@
}
},
"node_modules/async": {
"version": "3.2.5",
"resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz",
"integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg=="
"version": "3.2.6",
"resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
"integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==",
"license": "MIT"
},
"node_modules/balanced-match": {
"version": "1.0.2",
@@ -500,6 +504,7 @@
"version": "1.1.12",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
"dev": true,
"license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0",
@@ -553,7 +558,8 @@
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
"dev": true
},
"node_modules/create-require": {
"version": "1.1.1",
@@ -577,10 +583,11 @@
}
},
"node_modules/diff": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz",
"integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==",
"dev": true,
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.3.1"
}
@@ -726,14 +733,14 @@
}
},
"node_modules/jake": {
"version": "10.8.7",
"resolved": "https://registry.npmjs.org/jake/-/jake-10.8.7.tgz",
"integrity": "sha512-ZDi3aP+fG/LchyBzUM804VjddnwfSfsdeYkwt8NcbKRvo4rFkjhs456iLFn3k2ZUWvNe4i48WACDbza8fhq2+w==",
"version": "10.9.4",
"resolved": "https://registry.npmjs.org/jake/-/jake-10.9.4.tgz",
"integrity": "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==",
"license": "Apache-2.0",
"dependencies": {
"async": "^3.2.3",
"chalk": "^4.0.2",
"async": "^3.2.6",
"filelist": "^1.0.4",
"minimatch": "^3.1.2"
"picocolors": "^1.1.1"
},
"bin": {
"jake": "bin/cli.js"
@@ -742,70 +749,6 @@
"node": ">=10"
}
},
"node_modules/jake/node_modules/ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dependencies": {
"color-convert": "^2.0.1"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
"node_modules/jake/node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/jake/node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dependencies": {
"color-name": "~1.1.4"
},
"engines": {
"node": ">=7.0.0"
}
},
"node_modules/jake/node_modules/color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
"node_modules/jake/node_modules/has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"engines": {
"node": ">=8"
}
},
"node_modules/jake/node_modules/supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dependencies": {
"has-flag": "^4.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@@ -832,6 +775,7 @@
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dev": true,
"dependencies": {
"brace-expansion": "^1.1.7"
},
@@ -906,6 +850,12 @@
"node": ">=0.10.0"
}
},
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
"license": "ISC"
},
"node_modules/picomatch": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",

View File

@@ -4,6 +4,7 @@ import Logout from "./Pages/Logout/Logout";
import Projects from "./Pages/Projects/Index";
import SettingsAPIKey from "./Pages/Settings/APIKey/Index";
import SettingsAuthentication from "./Pages/Settings/Authentication/Index";
import SettingsDataRetention from "./Pages/Settings/DataRetention/Index";
import SettingsCallSMS from "./Pages/Settings/CallSMS/Index";
import SettingsWhatsApp from "./Pages/Settings/WhatsApp/Index";
// Settings Pages.
@@ -143,6 +144,11 @@ const App: () => JSX.Element = () => {
path={RouteMap[PageMap.SETTINGS_API_KEY]?.toString() || ""}
element={<SettingsAPIKey />}
/>
<PageRoute
path={RouteMap[PageMap.SETTINGS_DATA_RETENTION]?.toString() || ""}
element={<SettingsDataRetention />}
/>
</Routes>
</MasterPage>
);

View File

@@ -12,7 +12,6 @@ import Toggle from "Common/UI/Components/Toggle/Toggle";
import FieldType from "Common/UI/Components/Types/FieldType";
import { BILLING_ENABLED, getAllEnvVars } from "Common/UI/Config";
import { GetReactElementFunction } from "Common/UI/Types/FunctionTypes";
import Navigation from "Common/UI/Utils/Navigation";
import Project from "Common/Models/DatabaseModels/Project";
import User from "Common/Models/DatabaseModels/User";
import React, {
@@ -21,6 +20,7 @@ import React, {
useEffect,
useState,
} from "react";
import Navigation from "Common/UI/Utils/Navigation";
const Projects: FunctionComponent = (): ReactElement => {
const [isSubscriptionPlanYearly, setIsSubscriptionPlanYearly] =

View File

@@ -1,3 +1,4 @@
import AdminModelAPI from "../../../Utils/ModelAPI";
import PageMap from "../../../Utils/PageMap";
import RouteMap, { RouteUtil } from "../../../Utils/RouteMap";
import Route from "Common/Types/API/Route";
@@ -17,6 +18,7 @@ const DeletePage: FunctionComponent = (): ReactElement => {
modelId={modelId}
modelNameField="name"
modelType={Project}
modelAPI={AdminModelAPI}
title={"Project"}
breadcrumbLinks={[
{
@@ -41,6 +43,7 @@ const DeletePage: FunctionComponent = (): ReactElement => {
<ModelDelete
modelType={Project}
modelId={modelId}
modelAPI={AdminModelAPI}
onDeleteSuccess={() => {
Navigation.navigate(RouteMap[PageMap.PROJECTS] as Route);
}}

View File

@@ -1,3 +1,4 @@
import AdminModelAPI from "../../../Utils/ModelAPI";
import ObjectID from "Common/Types/ObjectID";
import PageMap from "../../../Utils/PageMap";
import RouteMap, { RouteUtil } from "../../../Utils/RouteMap";
@@ -19,6 +20,7 @@ const Projects: FunctionComponent = (): ReactElement => {
modelId={modelId}
modelNameField="name"
modelType={Project}
modelAPI={AdminModelAPI}
title={"Project"}
breadcrumbLinks={[
{
@@ -43,6 +45,7 @@ const Projects: FunctionComponent = (): ReactElement => {
<div>
<CardModelDetail<Project>
name="Project"
modelAPI={AdminModelAPI}
cardProps={{
title: "Project",
description: "Project details",

View File

@@ -73,6 +73,46 @@ const Settings: FunctionComponent = (): ReactElement => {
modelId: ObjectID.getZeroObjectID(),
}}
/>
<CardModelDetail
name="Project Creation Settings"
cardProps={{
title: "Project Creation",
description:
"Control who can create new projects on this OneUptime Server.",
}}
isEditable={true}
editButtonText="Edit Settings"
formFields={[
{
field: {
disableUserProjectCreation: true,
},
title: "Restrict Project Creation to Admins Only",
fieldType: FormFieldSchemaType.Toggle,
required: false,
description:
"When enabled, only master admin users can create new projects.",
},
]}
modelDetailProps={{
modelType: GlobalConfig,
id: "model-detail-project-creation",
fields: [
{
field: {
disableUserProjectCreation: true,
},
fieldType: FieldType.Boolean,
title: "Restrict Project Creation to Admins Only",
placeholder: "No",
description:
"When enabled, only master admin users can create new projects.",
},
],
modelId: ObjectID.getZeroObjectID(),
}}
/>
</Page>
);
};

View File

@@ -0,0 +1,85 @@
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 } from "react";
const Settings: FunctionComponent = (): ReactElement => {
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: "Data Retention",
to: RouteUtil.populateRouteParams(
RouteMap[PageMap.SETTINGS_DATA_RETENTION] as Route,
),
},
]}
sideMenu={<DashboardSideMenu />}
>
<CardModelDetail
name="Monitor Log Retention Settings"
cardProps={{
title: "Monitor Log Retention",
description:
"Configure how long monitor logs are retained before being automatically deleted.",
}}
isEditable={true}
editButtonText="Edit Settings"
formFields={[
{
field: {
monitorLogRetentionInDays: true,
},
title: "Monitor Log Retention (Days)",
fieldType: FormFieldSchemaType.PositiveNumber,
required: false,
description:
"Number of days to retain monitor logs. Monitor logs older than this will be automatically deleted. Default is 1 day if not set. Minimum: 1 day, Maximum: 365 days.",
validation: {
minValue: 1,
maxValue: 365,
},
placeholder: "1",
},
]}
modelDetailProps={{
modelType: GlobalConfig,
id: "model-detail-global-config-data-retention",
fields: [
{
field: {
monitorLogRetentionInDays: true,
},
fieldType: FieldType.Number,
title: "Monitor Log Retention (Days)",
placeholder: "1 (default)",
description:
"Number of days to retain monitor logs. Monitor logs older than this will be automatically deleted.",
},
],
modelId: ObjectID.getZeroObjectID(),
}}
/>
</Page>
);
};
export default Settings;

View File

@@ -72,6 +72,17 @@ const DashboardSideMenu: () => JSX.Element = (): ReactElement => {
icon={IconProp.Signal}
/>
</SideMenuSection>
<SideMenuSection title="Data Retention">
<SideMenuItem
link={{
title: "Data Retention",
to: RouteUtil.populateRouteParams(
RouteMap[PageMap.SETTINGS_DATA_RETENTION] as Route,
),
}}
icon={IconProp.Database}
/>
</SideMenuSection>
<SideMenuSection title="AI">
<SideMenuItem
link={{

View File

@@ -1,3 +1,4 @@
import AdminModelAPI from "../../../Utils/ModelAPI";
import PageMap from "../../../Utils/PageMap";
import RouteMap, { RouteUtil } from "../../../Utils/RouteMap";
import Route from "Common/Types/API/Route";
@@ -17,6 +18,7 @@ const DeletePage: FunctionComponent = (): ReactElement => {
modelId={modelId}
modelNameField="email"
modelType={User}
modelAPI={AdminModelAPI}
title={"User"}
breadcrumbLinks={[
{
@@ -39,6 +41,7 @@ const DeletePage: FunctionComponent = (): ReactElement => {
<ModelDelete
modelType={User}
modelId={modelId}
modelAPI={AdminModelAPI}
onDeleteSuccess={() => {
Navigation.navigate(RouteMap[PageMap.USERS] as Route);
}}

View File

@@ -1,3 +1,4 @@
import AdminModelAPI from "../../../Utils/ModelAPI";
import ObjectID from "Common/Types/ObjectID";
import PageMap from "../../../Utils/PageMap";
import RouteMap, { RouteUtil } from "../../../Utils/RouteMap";
@@ -19,6 +20,7 @@ const Users: FunctionComponent = (): ReactElement => {
modelId={modelId}
modelNameField="email"
modelType={User}
modelAPI={AdminModelAPI}
title={"User"}
breadcrumbLinks={[
{
@@ -41,6 +43,7 @@ const Users: FunctionComponent = (): ReactElement => {
<div>
<CardModelDetail<User>
name="User"
modelAPI={AdminModelAPI}
cardProps={{
title: "User",
description: "User details",

View File

@@ -1,3 +1,4 @@
import AdminModelAPI from "../../../Utils/ModelAPI";
import PageMap from "../../../Utils/PageMap";
import RouteMap, { RouteUtil } from "../../../Utils/RouteMap";
import Route from "Common/Types/API/Route";
@@ -19,6 +20,7 @@ const UserSettings: FunctionComponent = (): ReactElement => {
modelId={modelId}
modelNameField="email"
modelType={User}
modelAPI={AdminModelAPI}
title={"User"}
breadcrumbLinks={[
{
@@ -52,6 +54,7 @@ const UserSettings: FunctionComponent = (): ReactElement => {
>
<CardModelDetail<User>
name="user-master-admin-settings"
modelAPI={AdminModelAPI}
cardProps={{
title: "Master Admin Access",
description:

View File

@@ -22,6 +22,7 @@ enum PageMap {
SETTINGS_LLM_PROVIDERS = "SETTINGS_LLM_PROVIDERS",
SETTINGS_AUTHENTICATION = "SETTINGS_AUTHENTICATION",
SETTINGS_API_KEY = "SETTINGS_API_KEY",
SETTINGS_DATA_RETENTION = "SETTINGS_DATA_RETENTION",
}
export default PageMap;

View File

@@ -36,6 +36,9 @@ const RouteMap: Dictionary<Route> = {
`/admin/settings/authentication`,
),
[PageMap.SETTINGS_API_KEY]: new Route(`/admin/settings/api-key`),
[PageMap.SETTINGS_DATA_RETENTION]: new Route(
`/admin/settings/data-retention`,
),
};
export class RouteUtil {

View File

@@ -34,10 +34,11 @@
<meta name="theme-color" content="#121212">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="preload" href="https://fonts.googleapis.com/css2?family=Inter:wght@100;200;300;400;500;600;700;800;900&display=swap" as="style">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@100;200;300;400;500;600;700;800;900&display=swap" rel="stylesheet">
<style>
* {
font-family: Inter;
font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
}
@@ -67,6 +68,7 @@
width: auto;
}
</style>
<link rel="preload" href="/admin/assets/js/tailwind-3.4.5.js" as="script">
<script src="/admin/assets/js/tailwind-3.4.5.js"></script>
<script src="/admin/env.js"></script>

View File

@@ -23,6 +23,7 @@ import WhatsAppLogAPI from "./WhatsAppLogAPI";
// Import API
import ResellerPlanAPI from "Common/Server/API/ResellerPlanAPI";
import EnterpriseLicenseAPI from "Common/Server/API/EnterpriseLicenseAPI";
import OpenSourceDeploymentAPI from "Common/Server/API/OpenSourceDeploymentAPI";
import MonitorAPI from "Common/Server/API/MonitorAPI";
import ShortLinkAPI from "Common/Server/API/ShortLinkAPI";
import StatusPageAPI from "Common/Server/API/StatusPageAPI";
@@ -35,15 +36,18 @@ import UserWebAuthnAPI from "Common/Server/API/UserWebAuthnAPI";
import MonitorTest from "Common/Models/DatabaseModels/MonitorTest";
import IncidentInternalNoteAPI from "Common/Server/API/IncidentInternalNoteAPI";
import IncidentPublicNoteAPI from "Common/Server/API/IncidentPublicNoteAPI";
import IncidentEpisodePublicNoteAPI from "Common/Server/API/IncidentEpisodePublicNoteAPI";
import ScheduledMaintenanceInternalNoteAPI from "Common/Server/API/ScheduledMaintenanceInternalNoteAPI";
import ScheduledMaintenancePublicNoteAPI from "Common/Server/API/ScheduledMaintenancePublicNoteAPI";
import IncidentAPI from "Common/Server/API/IncidentAPI";
import IncidentEpisodeAPI from "Common/Server/API/IncidentEpisodeAPI";
import ScheduledMaintenanceAPI from "Common/Server/API/ScheduledMaintenanceAPI";
import AlertAPI from "Common/Server/API/AlertAPI";
// 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 UserIncomingCallNumberAPI from "Common/Server/API/UserIncomingCallNumberAPI";
import UserWhatsAppAPI from "Common/Server/API/UserWhatsAppAPI";
import UserPushAPI from "Common/Server/API/UserPushAPI";
import UserAPI from "Common/Server/API/UserAPI";
@@ -62,6 +66,12 @@ import DomainService, {
import EmailLogService, {
Service as EmailLogServiceType,
} from "Common/Server/Services/EmailLogService";
import ProjectSCIMLogService, {
Service as ProjectSCIMLogServiceType,
} from "Common/Server/Services/ProjectSCIMLogService";
import StatusPageSCIMLogService, {
Service as StatusPageSCIMLogServiceType,
} from "Common/Server/Services/StatusPageSCIMLogService";
import TelemetryIngestionKeyService, {
Service as TelemetryIngestionKeyServiceType,
} from "Common/Server/Services/TelemetryIngestionKeyService";
@@ -99,6 +109,70 @@ import AlertStateTimelineService, {
Service as AlertStateTimelineServiceType,
} from "Common/Server/Services/AlertStateTimelineService";
// AlertEpisode Services
import AlertEpisodeService, {
Service as AlertEpisodeServiceType,
} from "Common/Server/Services/AlertEpisodeService";
import AlertEpisodeFeedService, {
Service as AlertEpisodeFeedServiceType,
} from "Common/Server/Services/AlertEpisodeFeedService";
import AlertEpisodeInternalNoteService, {
Service as AlertEpisodeInternalNoteServiceType,
} from "Common/Server/Services/AlertEpisodeInternalNoteService";
import AlertEpisodeMemberService, {
Service as AlertEpisodeMemberServiceType,
} from "Common/Server/Services/AlertEpisodeMemberService";
import AlertEpisodeOwnerTeamService, {
Service as AlertEpisodeOwnerTeamServiceType,
} from "Common/Server/Services/AlertEpisodeOwnerTeamService";
import AlertEpisodeOwnerUserService, {
Service as AlertEpisodeOwnerUserServiceType,
} from "Common/Server/Services/AlertEpisodeOwnerUserService";
import AlertEpisodeStateTimelineService, {
Service as AlertEpisodeStateTimelineServiceType,
} from "Common/Server/Services/AlertEpisodeStateTimelineService";
// IncidentEpisode Services
import IncidentEpisodeFeedService, {
Service as IncidentEpisodeFeedServiceType,
} from "Common/Server/Services/IncidentEpisodeFeedService";
import IncidentEpisodeInternalNoteService, {
Service as IncidentEpisodeInternalNoteServiceType,
} from "Common/Server/Services/IncidentEpisodeInternalNoteService";
import IncidentEpisodeMemberService, {
Service as IncidentEpisodeMemberServiceType,
} from "Common/Server/Services/IncidentEpisodeMemberService";
import IncidentEpisodeOwnerTeamService, {
Service as IncidentEpisodeOwnerTeamServiceType,
} from "Common/Server/Services/IncidentEpisodeOwnerTeamService";
import IncidentEpisodeOwnerUserService, {
Service as IncidentEpisodeOwnerUserServiceType,
} from "Common/Server/Services/IncidentEpisodeOwnerUserService";
import IncidentEpisodeStateTimelineService, {
Service as IncidentEpisodeStateTimelineServiceType,
} from "Common/Server/Services/IncidentEpisodeStateTimelineService";
import IncidentEpisodeRoleMemberService, {
Service as IncidentEpisodeRoleMemberServiceType,
} from "Common/Server/Services/IncidentEpisodeRoleMemberService";
import AlertGroupingRuleService, {
Service as AlertGroupingRuleServiceType,
} from "Common/Server/Services/AlertGroupingRuleService";
import IncidentGroupingRuleService, {
Service as IncidentGroupingRuleServiceType,
} from "Common/Server/Services/IncidentGroupingRuleService";
import IncidentSlaService, {
Service as IncidentSlaServiceType,
} from "Common/Server/Services/IncidentSlaService";
import IncidentSlaRuleService, {
Service as IncidentSlaRuleServiceType,
} from "Common/Server/Services/IncidentSlaRuleService";
import IncidentCustomFieldService, {
Service as IncidentCustomFieldServiceType,
} from "Common/Server/Services/IncidentCustomFieldService";
@@ -120,6 +194,12 @@ import IncidentOwnerUserService, {
import IncidentSeverityService, {
Service as IncidentSeverityServiceType,
} from "Common/Server/Services/IncidentSeverityService";
import IncidentRoleService, {
Service as IncidentRoleServiceType,
} from "Common/Server/Services/IncidentRoleService";
import IncidentMemberService, {
Service as IncidentMemberServiceType,
} from "Common/Server/Services/IncidentMemberService";
import IncidentStateService, {
Service as IncidentStateServiceType,
} from "Common/Server/Services/IncidentStateService";
@@ -189,6 +269,20 @@ import OnCallDutyPolicyCustomFieldService, {
import OnCallDutyPolicyEscalationRuleScheduleService, {
Service as OnCallDutyPolicyEscalationRuleScheduleServiceType,
} from "Common/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService";
// Incoming Call Policy
import IncomingCallPolicyService, {
Service as IncomingCallPolicyServiceType,
} from "Common/Server/Services/IncomingCallPolicyService";
import IncomingCallPolicyEscalationRuleService, {
Service as IncomingCallPolicyEscalationRuleServiceType,
} from "Common/Server/Services/IncomingCallPolicyEscalationRuleService";
import IncomingCallLogService, {
Service as IncomingCallLogServiceType,
} from "Common/Server/Services/IncomingCallLogService";
import IncomingCallLogItemService, {
Service as IncomingCallLogItemServiceType,
} from "Common/Server/Services/IncomingCallLogItemService";
import OnCallDutyPolicyEscalationRuleService, {
Service as OnCallDutyPolicyEscalationRuleServiceType,
} from "Common/Server/Services/OnCallDutyPolicyEscalationRuleService";
@@ -216,6 +310,9 @@ import OnCallDutyPolicyScheduleService, {
import ProjectCallSMSConfigService, {
Service as ProjectCallSMSConfigServiceType,
} from "Common/Server/Services/ProjectCallSMSConfigService";
import ProjectUserProfileService, {
Service as ProjectUserProfileServiceType,
} from "Common/Server/Services/ProjectUserProfileService";
import ProjectSmtpConfigService, {
Service as ProjectSMTPConfigServiceType,
} from "Common/Server/Services/ProjectSmtpConfigService";
@@ -315,6 +412,9 @@ import StatusPageSSOService, {
import TeamMemberService, {
TeamMemberService as TeamMemberServiceType,
} from "Common/Server/Services/TeamMemberService";
import TeamMemberCustomFieldService, {
Service as TeamMemberCustomFieldServiceType,
} from "Common/Server/Services/TeamMemberCustomFieldService";
import TeamPermissionService, {
Service as TeamPermissionServiceType,
} from "Common/Server/Services/TeamPermissionService";
@@ -388,6 +488,8 @@ import PushNotificationLog from "Common/Models/DatabaseModels/PushNotificationLo
import WorkspaceNotificationLog from "Common/Models/DatabaseModels/WorkspaceNotificationLog";
import Domain from "Common/Models/DatabaseModels/Domain";
import EmailLog from "Common/Models/DatabaseModels/EmailLog";
import ProjectSCIMLog from "Common/Models/DatabaseModels/ProjectSCIMLog";
import StatusPageSCIMLog from "Common/Models/DatabaseModels/StatusPageSCIMLog";
import EmailVerificationToken from "Common/Models/DatabaseModels/EmailVerificationToken";
import Dashboard from "Common/Models/DatabaseModels/Dashboard";
@@ -399,12 +501,36 @@ import AlertSeverity from "Common/Models/DatabaseModels/AlertSeverity";
import AlertState from "Common/Models/DatabaseModels/AlertState";
import AlertStateTimeline from "Common/Models/DatabaseModels/AlertStateTimeline";
// AlertEpisode Models
import AlertEpisode from "Common/Models/DatabaseModels/AlertEpisode";
import AlertEpisodeFeed from "Common/Models/DatabaseModels/AlertEpisodeFeed";
import AlertEpisodeInternalNote from "Common/Models/DatabaseModels/AlertEpisodeInternalNote";
import AlertEpisodeMember from "Common/Models/DatabaseModels/AlertEpisodeMember";
import AlertEpisodeOwnerTeam from "Common/Models/DatabaseModels/AlertEpisodeOwnerTeam";
import AlertEpisodeOwnerUser from "Common/Models/DatabaseModels/AlertEpisodeOwnerUser";
import AlertEpisodeStateTimeline from "Common/Models/DatabaseModels/AlertEpisodeStateTimeline";
import AlertGroupingRule from "Common/Models/DatabaseModels/AlertGroupingRule";
import IncidentGroupingRule from "Common/Models/DatabaseModels/IncidentGroupingRule";
import IncidentSla from "Common/Models/DatabaseModels/IncidentSla";
import IncidentSlaRule from "Common/Models/DatabaseModels/IncidentSlaRule";
// IncidentEpisode Models
import IncidentEpisodeFeed from "Common/Models/DatabaseModels/IncidentEpisodeFeed";
import IncidentEpisodeInternalNote from "Common/Models/DatabaseModels/IncidentEpisodeInternalNote";
import IncidentEpisodeMember from "Common/Models/DatabaseModels/IncidentEpisodeMember";
import IncidentEpisodeOwnerTeam from "Common/Models/DatabaseModels/IncidentEpisodeOwnerTeam";
import IncidentEpisodeOwnerUser from "Common/Models/DatabaseModels/IncidentEpisodeOwnerUser";
import IncidentEpisodeStateTimeline from "Common/Models/DatabaseModels/IncidentEpisodeStateTimeline";
import IncidentEpisodeRoleMember from "Common/Models/DatabaseModels/IncidentEpisodeRoleMember";
import IncidentCustomField from "Common/Models/DatabaseModels/IncidentCustomField";
import IncidentNoteTemplate from "Common/Models/DatabaseModels/IncidentNoteTemplate";
import IncidentPostmortemTemplate from "Common/Models/DatabaseModels/IncidentPostmortemTemplate";
import IncidentOwnerTeam from "Common/Models/DatabaseModels/IncidentOwnerTeam";
import IncidentOwnerUser from "Common/Models/DatabaseModels/IncidentOwnerUser";
import IncidentSeverity from "Common/Models/DatabaseModels/IncidentSeverity";
import IncidentRole from "Common/Models/DatabaseModels/IncidentRole";
import IncidentMember from "Common/Models/DatabaseModels/IncidentMember";
import IncidentState from "Common/Models/DatabaseModels/IncidentState";
import IncidentStateTimeline from "Common/Models/DatabaseModels/IncidentStateTimeline";
import IncidentTemplate from "Common/Models/DatabaseModels/IncidentTemplate";
@@ -424,6 +550,12 @@ import MonitorStatus from "Common/Models/DatabaseModels/MonitorStatus";
import MonitorTimelineStatus from "Common/Models/DatabaseModels/MonitorStatusTimeline";
import OnCallDutyPolicyCustomField from "Common/Models/DatabaseModels/OnCallDutyPolicyCustomField";
import OnCallDutyPolicyEscalationRule from "Common/Models/DatabaseModels/OnCallDutyPolicyEscalationRule";
// Incoming Call Policy Models
import IncomingCallPolicy from "Common/Models/DatabaseModels/IncomingCallPolicy";
import IncomingCallPolicyEscalationRule from "Common/Models/DatabaseModels/IncomingCallPolicyEscalationRule";
import IncomingCallLog from "Common/Models/DatabaseModels/IncomingCallLog";
import IncomingCallLogItem from "Common/Models/DatabaseModels/IncomingCallLogItem";
import OnCallDutyPolicyEscalationRuleSchedule from "Common/Models/DatabaseModels/OnCallDutyPolicyEscalationRuleSchedule";
import OnCallDutyPolicyEscalationRuleTeam from "Common/Models/DatabaseModels/OnCallDutyPolicyEscalationRuleTeam";
import OnCallDutyPolicyEscalationRuleUser from "Common/Models/DatabaseModels/OnCallDutyPolicyEscalationRuleUser";
@@ -434,6 +566,7 @@ import OnCallDutyPolicyScheduleLayer from "Common/Models/DatabaseModels/OnCallDu
import OnCallDutyPolicyScheduleLayerUser from "Common/Models/DatabaseModels/OnCallDutyPolicyScheduleLayerUser";
import ProjectCallSMSConfig from "Common/Models/DatabaseModels/ProjectCallSMSConfig";
import ProjectSmtpConfig from "Common/Models/DatabaseModels/ProjectSmtpConfig";
import ProjectUserProfile from "Common/Models/DatabaseModels/ProjectUserProfile";
import PromoCode from "Common/Models/DatabaseModels/PromoCode";
import CodeRepository from "Common/Models/DatabaseModels/CodeRepository";
import Reseller from "Common/Models/DatabaseModels/Reseller";
@@ -462,6 +595,7 @@ import StatusPageResource from "Common/Models/DatabaseModels/StatusPageResource"
import StatusPageSSO from "Common/Models/DatabaseModels/StatusPageSso";
import Team from "Common/Models/DatabaseModels/Team";
import TeamMember from "Common/Models/DatabaseModels/TeamMember";
import TeamMemberCustomField from "Common/Models/DatabaseModels/TeamMemberCustomField";
import TeamPermission from "Common/Models/DatabaseModels/TeamPermission";
import TeamComplianceSetting from "Common/Models/DatabaseModels/TeamComplianceSetting";
import TelemetryUsageBilling from "Common/Models/DatabaseModels/TelemetryUsageBilling";
@@ -876,6 +1010,171 @@ const BaseAPIFeatureSet: FeatureSet = {
).getRouter(),
);
// AlertEpisode Routes
app.use(
`/${APP_NAME.toLocaleLowerCase()}`,
new BaseAPI<AlertEpisode, AlertEpisodeServiceType>(
AlertEpisode,
AlertEpisodeService,
).getRouter(),
);
app.use(
`/${APP_NAME.toLocaleLowerCase()}`,
new BaseAPI<AlertEpisodeFeed, AlertEpisodeFeedServiceType>(
AlertEpisodeFeed,
AlertEpisodeFeedService,
).getRouter(),
);
app.use(
`/${APP_NAME.toLocaleLowerCase()}`,
new BaseAPI<
AlertEpisodeInternalNote,
AlertEpisodeInternalNoteServiceType
>(AlertEpisodeInternalNote, AlertEpisodeInternalNoteService).getRouter(),
);
app.use(
`/${APP_NAME.toLocaleLowerCase()}`,
new BaseAPI<AlertEpisodeMember, AlertEpisodeMemberServiceType>(
AlertEpisodeMember,
AlertEpisodeMemberService,
).getRouter(),
);
app.use(
`/${APP_NAME.toLocaleLowerCase()}`,
new BaseAPI<AlertEpisodeOwnerTeam, AlertEpisodeOwnerTeamServiceType>(
AlertEpisodeOwnerTeam,
AlertEpisodeOwnerTeamService,
).getRouter(),
);
app.use(
`/${APP_NAME.toLocaleLowerCase()}`,
new BaseAPI<AlertEpisodeOwnerUser, AlertEpisodeOwnerUserServiceType>(
AlertEpisodeOwnerUser,
AlertEpisodeOwnerUserService,
).getRouter(),
);
app.use(
`/${APP_NAME.toLocaleLowerCase()}`,
new BaseAPI<
AlertEpisodeStateTimeline,
AlertEpisodeStateTimelineServiceType
>(
AlertEpisodeStateTimeline,
AlertEpisodeStateTimelineService,
).getRouter(),
);
// IncidentEpisode Routes
app.use(
`/${APP_NAME.toLocaleLowerCase()}`,
new IncidentEpisodeAPI().getRouter(),
);
app.use(
`/${APP_NAME.toLocaleLowerCase()}`,
new BaseAPI<IncidentEpisodeFeed, IncidentEpisodeFeedServiceType>(
IncidentEpisodeFeed,
IncidentEpisodeFeedService,
).getRouter(),
);
app.use(
`/${APP_NAME.toLocaleLowerCase()}`,
new BaseAPI<
IncidentEpisodeInternalNote,
IncidentEpisodeInternalNoteServiceType
>(
IncidentEpisodeInternalNote,
IncidentEpisodeInternalNoteService,
).getRouter(),
);
app.use(
`/${APP_NAME.toLocaleLowerCase()}`,
new BaseAPI<IncidentEpisodeMember, IncidentEpisodeMemberServiceType>(
IncidentEpisodeMember,
IncidentEpisodeMemberService,
).getRouter(),
);
app.use(
`/${APP_NAME.toLocaleLowerCase()}`,
new BaseAPI<
IncidentEpisodeOwnerTeam,
IncidentEpisodeOwnerTeamServiceType
>(IncidentEpisodeOwnerTeam, IncidentEpisodeOwnerTeamService).getRouter(),
);
app.use(
`/${APP_NAME.toLocaleLowerCase()}`,
new BaseAPI<
IncidentEpisodeOwnerUser,
IncidentEpisodeOwnerUserServiceType
>(IncidentEpisodeOwnerUser, IncidentEpisodeOwnerUserService).getRouter(),
);
app.use(
`/${APP_NAME.toLocaleLowerCase()}`,
new BaseAPI<
IncidentEpisodeStateTimeline,
IncidentEpisodeStateTimelineServiceType
>(
IncidentEpisodeStateTimeline,
IncidentEpisodeStateTimelineService,
).getRouter(),
);
app.use(
`/${APP_NAME.toLocaleLowerCase()}`,
new BaseAPI<
IncidentEpisodeRoleMember,
IncidentEpisodeRoleMemberServiceType
>(
IncidentEpisodeRoleMember,
IncidentEpisodeRoleMemberService,
).getRouter(),
);
app.use(
`/${APP_NAME.toLocaleLowerCase()}`,
new BaseAPI<AlertGroupingRule, AlertGroupingRuleServiceType>(
AlertGroupingRule,
AlertGroupingRuleService,
).getRouter(),
);
app.use(
`/${APP_NAME.toLocaleLowerCase()}`,
new BaseAPI<IncidentGroupingRule, IncidentGroupingRuleServiceType>(
IncidentGroupingRule,
IncidentGroupingRuleService,
).getRouter(),
);
// IncidentSla
app.use(
`/${APP_NAME.toLocaleLowerCase()}`,
new BaseAPI<IncidentSla, IncidentSlaServiceType>(
IncidentSla,
IncidentSlaService,
).getRouter(),
);
// IncidentSlaRule
app.use(
`/${APP_NAME.toLocaleLowerCase()}`,
new BaseAPI<IncidentSlaRule, IncidentSlaRuleServiceType>(
IncidentSlaRule,
IncidentSlaRuleService,
).getRouter(),
);
app.use(
`/${APP_NAME.toLocaleLowerCase()}`,
new BaseAnalyticsAPI<ExceptionInstance, ExceptionInstanceServiceType>(
@@ -1131,6 +1430,14 @@ const BaseAPIFeatureSet: FeatureSet = {
).getRouter(),
);
app.use(
`/${APP_NAME.toLocaleLowerCase()}`,
new BaseAPI<ProjectUserProfile, ProjectUserProfileServiceType>(
ProjectUserProfile,
ProjectUserProfileService,
).getRouter(),
);
app.use(
`/${APP_NAME.toLocaleLowerCase()}`,
new BaseAPI<MonitorGroupResource, MonitorGroupResourceServiceType>(
@@ -1147,6 +1454,14 @@ const BaseAPIFeatureSet: FeatureSet = {
).getRouter(),
);
app.use(
`/${APP_NAME.toLocaleLowerCase()}`,
new BaseAPI<TeamMemberCustomField, TeamMemberCustomFieldServiceType>(
TeamMemberCustomField,
TeamMemberCustomFieldService,
).getRouter(),
);
app.use(
`/${APP_NAME.toLocaleLowerCase()}`,
new BaseAPI<TeamPermission, TeamPermissionServiceType>(
@@ -1333,6 +1648,22 @@ const BaseAPIFeatureSet: FeatureSet = {
).getRouter(),
);
app.use(
`/${APP_NAME.toLocaleLowerCase()}`,
new BaseAPI<IncidentRole, IncidentRoleServiceType>(
IncidentRole,
IncidentRoleService,
).getRouter(),
);
app.use(
`/${APP_NAME.toLocaleLowerCase()}`,
new BaseAPI<IncidentMember, IncidentMemberServiceType>(
IncidentMember,
IncidentMemberService,
).getRouter(),
);
app.use(
`/${APP_NAME.toLocaleLowerCase()}`,
new BaseAPI<IncidentOwnerUser, IncidentOwnerUserServiceType>(
@@ -1545,6 +1876,22 @@ const BaseAPIFeatureSet: FeatureSet = {
).getRouter(),
);
app.use(
`/${APP_NAME.toLocaleLowerCase()}`,
new BaseAPI<ProjectSCIMLog, ProjectSCIMLogServiceType>(
ProjectSCIMLog,
ProjectSCIMLogService,
).getRouter(),
);
app.use(
`/${APP_NAME.toLocaleLowerCase()}`,
new BaseAPI<StatusPageSCIMLog, StatusPageSCIMLogServiceType>(
StatusPageSCIMLog,
StatusPageSCIMLogService,
).getRouter(),
);
app.use(
`/${APP_NAME.toLocaleLowerCase()}`,
new BaseAPI<Reseller, ResellerServiceType>(
@@ -1589,6 +1936,45 @@ const BaseAPIFeatureSet: FeatureSet = {
new OnCallDutyPolicyAPI().getRouter(),
);
// IncomingCallPolicy
app.use(
`/${APP_NAME.toLocaleLowerCase()}`,
new BaseAPI<IncomingCallPolicy, IncomingCallPolicyServiceType>(
IncomingCallPolicy,
IncomingCallPolicyService,
).getRouter(),
);
// IncomingCallPolicyEscalationRule
app.use(
`/${APP_NAME.toLocaleLowerCase()}`,
new BaseAPI<
IncomingCallPolicyEscalationRule,
IncomingCallPolicyEscalationRuleServiceType
>(
IncomingCallPolicyEscalationRule,
IncomingCallPolicyEscalationRuleService,
).getRouter(),
);
// IncomingCallLog
app.use(
`/${APP_NAME.toLocaleLowerCase()}`,
new BaseAPI<IncomingCallLog, IncomingCallLogServiceType>(
IncomingCallLog,
IncomingCallLogService,
).getRouter(),
);
// IncomingCallLogItem
app.use(
`/${APP_NAME.toLocaleLowerCase()}`,
new BaseAPI<IncomingCallLogItem, IncomingCallLogItemServiceType>(
IncomingCallLogItem,
IncomingCallLogItemService,
).getRouter(),
);
// TeamComplianceAPI
app.use(
`/${APP_NAME.toLocaleLowerCase()}`,
@@ -1622,6 +2008,10 @@ const BaseAPIFeatureSet: FeatureSet = {
`/${APP_NAME.toLocaleLowerCase()}`,
new EnterpriseLicenseAPI().getRouter(),
);
app.use(
`/${APP_NAME.toLocaleLowerCase()}`,
new OpenSourceDeploymentAPI().getRouter(),
);
app.use(`/${APP_NAME.toLocaleLowerCase()}`, new SlackAPI().getRouter());
app.use(
`/${APP_NAME.toLocaleLowerCase()}`,
@@ -1648,6 +2038,10 @@ const BaseAPIFeatureSet: FeatureSet = {
);
app.use(`/${APP_NAME.toLocaleLowerCase()}`, new UserEmailAPI().getRouter());
app.use(`/${APP_NAME.toLocaleLowerCase()}`, new UserSMSAPI().getRouter());
app.use(
`/${APP_NAME.toLocaleLowerCase()}`,
new UserIncomingCallNumberAPI().getRouter(),
);
app.use(
`/${APP_NAME.toLocaleLowerCase()}`,
new UserWhatsAppAPI().getRouter(),
@@ -1752,6 +2146,11 @@ const BaseAPIFeatureSet: FeatureSet = {
new IncidentPublicNoteAPI().getRouter(),
);
app.use(
`/${APP_NAME.toLocaleLowerCase()}`,
new IncidentEpisodePublicNoteAPI().getRouter(),
);
app.use(
`/${APP_NAME.toLocaleLowerCase()}`,
new IncidentInternalNoteAPI().getRouter(),

View File

@@ -17,9 +17,11 @@ import ObjectID from "Common/Types/ObjectID";
import PositiveNumber from "Common/Types/PositiveNumber";
import DatabaseConfig from "Common/Server/DatabaseConfig";
import {
AppVersion,
EncryptionSecret,
IsBillingEnabled,
} from "Common/Server/EnvironmentConfig";
import API from "Common/Utils/API";
import AccessTokenService from "Common/Server/Services/AccessTokenService";
import EmailVerificationTokenService from "Common/Server/Services/EmailVerificationTokenService";
import MailService from "Common/Server/Services/MailService";
@@ -29,6 +31,7 @@ import UserSessionService, {
SessionMetadata,
} from "Common/Server/Services/UserSessionService";
import CookieUtil from "Common/Server/Utils/Cookie";
import JSONWebToken from "Common/Server/Utils/JsonWebToken";
import Express, {
ExpressRequest,
ExpressResponse,
@@ -54,6 +57,11 @@ const router: ExpressRouter = Express.getRouter();
const ACCESS_TOKEN_EXPIRY_SECONDS: number = 15 * 60;
interface FinalizeUserLoginResult {
sessionMetadata: SessionMetadata;
accessToken: string;
}
type FinalizeUserLoginInput = {
req: ExpressRequest;
res: ExpressResponse;
@@ -63,9 +71,9 @@ type FinalizeUserLoginInput = {
const finalizeUserLogin: (
data: FinalizeUserLoginInput,
) => Promise<SessionMetadata> = async (
) => Promise<FinalizeUserLoginResult> = async (
data: FinalizeUserLoginInput,
): Promise<SessionMetadata> => {
): Promise<FinalizeUserLoginResult> => {
const { req, res, user, isGlobalLogin } = data;
const sessionMetadata: SessionMetadata =
@@ -87,7 +95,21 @@ const finalizeUserLogin: (
accessTokenExpiresInSeconds: ACCESS_TOKEN_EXPIRY_SECONDS,
});
return sessionMetadata;
// Generate access token for response body (used by mobile clients)
const accessToken: string = JSONWebToken.signUserLoginToken({
tokenData: {
userId: user.id!,
email: user.email!,
name: user.name!,
timezone: user.timezone || null,
isMasterAdmin: user.isMasterAdmin!,
isGlobalLogin: isGlobalLogin,
sessionId: sessionMetadata.session.id!,
},
expiresInSeconds: ACCESS_TOKEN_EXPIRY_SECONDS,
});
return { sessionMetadata, accessToken };
};
router.post(
@@ -251,6 +273,28 @@ router.post(
logger.info("User signed up: " + savedUser.email?.toString());
if (!IsBillingEnabled && miscDataProps["notifySelfHosted"] === true) {
const instanceUrl: string = new URL(httpProtocol, host).toString();
API.post({
url: URL.fromString(
"https://oneuptime.com/api/open-source-deployment/register",
),
data: {
email: savedUser.email?.toString() || "",
name: savedUser.name?.toString() || "",
companyName:
(miscDataProps["selfHostedCompanyName"] as string) || undefined,
companyPhoneNumber:
(miscDataProps["selfHostedPhoneNumber"] as string) || undefined,
oneuptimeVersion: AppVersion,
instanceUrl: instanceUrl,
},
}).catch((err: Error) => {
logger.error(err);
});
}
return Response.sendEntityResponse(req, res, savedUser, User);
}
@@ -552,8 +596,10 @@ router.post(
next: NextFunction,
): Promise<void> => {
try {
// Try cookie first, then fallback to request body (for mobile clients)
const refreshToken: string | undefined =
CookieUtil.getRefreshTokenFromExpressRequest(req);
CookieUtil.getRefreshTokenFromExpressRequest(req) ||
(req.body.refreshToken as string | undefined);
if (!refreshToken) {
CookieUtil.removeAllCookies(req, res);
@@ -658,7 +704,26 @@ router.post(
accessTokenExpiresInSeconds: ACCESS_TOKEN_EXPIRY_SECONDS,
});
return Response.sendEmptySuccessResponse(req, res);
// Generate access token for response body (used by mobile clients)
const newAccessToken: string = JSONWebToken.signUserLoginToken({
tokenData: {
userId: user.id!,
email: user.email!,
name: user.name!,
timezone: user.timezone || null,
isMasterAdmin: user.isMasterAdmin!,
isGlobalLogin: isGlobalLogin,
sessionId: renewedSession.session.id!,
},
expiresInSeconds: ACCESS_TOKEN_EXPIRY_SECONDS,
});
return Response.sendJsonObjectResponse(req, res, {
accessToken: newAccessToken,
refreshToken: renewedSession.refreshToken,
refreshTokenExpiresAt:
renewedSession.refreshTokenExpiresAt.toISOString(),
});
} catch (err) {
return next(err);
}
@@ -673,8 +738,10 @@ router.post(
next: NextFunction,
): Promise<void> => {
try {
// Try cookie first, then fallback to request body (for mobile clients)
const refreshToken: string | undefined =
CookieUtil.getRefreshTokenFromExpressRequest(req);
CookieUtil.getRefreshTokenFromExpressRequest(req) ||
(req.body.refreshToken as string | undefined);
if (refreshToken) {
await UserSessionService.revokeSessionByRefreshToken(refreshToken, {
@@ -987,14 +1054,21 @@ const login: LoginFunction = async (options: {
if (alreadySavedUser.password.toString() === user.password!.toString()) {
logger.info("User logged in: " + alreadySavedUser.email?.toString());
await finalizeUserLogin({
const loginResult: FinalizeUserLoginResult = await finalizeUserLogin({
req,
res,
user: alreadySavedUser,
isGlobalLogin: true,
});
return Response.sendEntityResponse(req, res, alreadySavedUser, User);
return Response.sendEntityResponse(req, res, alreadySavedUser, User, {
miscData: {
accessToken: loginResult.accessToken,
refreshToken: loginResult.sessionMetadata.refreshToken,
refreshTokenExpiresAt:
loginResult.sessionMetadata.refreshTokenExpiresAt.toISOString(),
},
});
}
}
return Response.sendErrorResponse(

File diff suppressed because it is too large Load Diff

View File

@@ -26,6 +26,7 @@ import UserSessionService, {
import QueryHelper from "Common/Server/Types/Database/QueryHelper";
import Select from "Common/Server/Types/Database/Select";
import CookieUtil from "Common/Server/Utils/Cookie";
import JSONWebToken from "Common/Server/Utils/JsonWebToken";
import Express, {
ExpressRequest,
ExpressResponse,
@@ -240,6 +241,8 @@ router.get(
);
}
const isMobileRequest: boolean = req.query["mobile"] === "true";
const samlRequestUrl: URL = SSOUtil.createSAMLRequestUrl({
acsUrl: URL.fromString(
`${HttpProtocol}${Host}/identity/idp-login/${projectSSO.projectId?.toString()}/${projectSSO.id?.toString()}`,
@@ -250,6 +253,10 @@ router.get(
),
});
if (isMobileRequest) {
samlRequestUrl.addQueryParam("RelayState", "mobile");
}
return Response.redirect(req, res, samlRequestUrl);
} catch (err) {
logger.error(err);
@@ -538,15 +545,11 @@ const loginUserWithSso: LoginUserWithSsoFunction = async (
}
const projectId: ObjectID = new ObjectID(req.params["projectId"] as string);
const isMobileRequest: boolean =
req.body.RelayState === "mobile" || req.query["RelayState"] === "mobile";
alreadySavedUser.email = email;
CookieUtil.setSSOCookie({
user: alreadySavedUser,
projectId: projectId,
expressResponse: res,
});
// Refresh Permissions for this user here.
await AccessTokenService.refreshUserAllPermissions(alreadySavedUser.id!);
@@ -562,6 +565,66 @@ const loginUserWithSso: LoginUserWithSsoFunction = async (
},
});
if (isMobileRequest) {
// For mobile SSO, generate an access token and redirect to the app deep link
const accessToken: string = JSONWebToken.signUserLoginToken({
tokenData: {
userId: alreadySavedUser.id!,
email: alreadySavedUser.email!,
name: alreadySavedUser.name!,
timezone: alreadySavedUser.timezone || null,
isMasterAdmin: alreadySavedUser.isMasterAdmin!,
isGlobalLogin: false,
sessionId: sessionMetadata.session.id!,
},
expiresInSeconds: ACCESS_TOKEN_EXPIRY_SECONDS,
});
// Generate SSO token for per-project authentication (same as setSSOCookie)
const ssoToken: string = JSONWebToken.sign({
data: {
userId: alreadySavedUser.id!,
projectId: projectId,
name: alreadySavedUser.name!,
email: alreadySavedUser.email,
isMasterAdmin: false,
isGeneralLogin: false,
},
expiresInSeconds: OneUptimeDate.getSecondsInDays(
new PositiveNumber(30),
),
});
const params: URLSearchParams = new URLSearchParams();
params.set("accessToken", accessToken);
params.set("refreshToken", sessionMetadata.refreshToken);
params.set(
"refreshTokenExpiresAt",
sessionMetadata.refreshTokenExpiresAt.toISOString(),
);
params.set("userId", alreadySavedUser.id!.toString());
params.set("email", alreadySavedUser.email!.toString());
params.set("name", alreadySavedUser.name?.toString() || "");
params.set(
"isMasterAdmin",
String(alreadySavedUser.isMasterAdmin || false),
);
params.set("ssoToken", ssoToken);
params.set("projectId", projectId.toString());
const deepLinkUrl: string = `oneuptime://sso-callback?${params.toString()}`;
logger.info("User logged in with SSO (mobile): " + email.toString());
return res.redirect(deepLinkUrl);
}
CookieUtil.setSSOCookie({
user: alreadySavedUser,
projectId: projectId,
expressResponse: res,
});
CookieUtil.setUserCookie({
expressResponse: res,
user: alreadySavedUser,
@@ -575,7 +638,7 @@ const loginUserWithSso: LoginUserWithSsoFunction = async (
const host: Hostname = await DatabaseConfig.getHost();
const httpProtocol: Protocol = await DatabaseConfig.getHttpProtocol();
logger.info("User logged in with SSO" + email.toString());
logger.info("User logged in with SSO: " + email.toString());
return Response.redirect(
req,

View File

@@ -13,6 +13,7 @@ import ObjectID from "Common/Types/ObjectID";
import DatabaseConfig from "Common/Server/DatabaseConfig";
import { EncryptionSecret } from "Common/Server/EnvironmentConfig";
import MailService from "Common/Server/Services/MailService";
import ProjectSMTPConfigService from "Common/Server/Services/ProjectSmtpConfigService";
import StatusPagePrivateUserService from "Common/Server/Services/StatusPagePrivateUserService";
import StatusPageService from "Common/Server/Services/StatusPageService";
import StatusPagePrivateUserSessionService, {
@@ -468,6 +469,16 @@ router.post(
logoFileId: true,
requireSsoForLogin: true,
projectId: true,
smtpConfig: {
_id: true,
hostname: true,
port: true,
username: true,
password: true,
fromEmail: true,
fromName: true,
secure: true,
},
},
},
);
@@ -547,6 +558,9 @@ router.post(
},
{
projectId: statusPage.projectId!,
mailServer: ProjectSMTPConfigService.toEmailServer(
statusPage.smtpConfig,
),
statusPageId: statusPage.id!,
},
).catch((err: Error) => {
@@ -632,6 +646,16 @@ router.post(
logoFileId: true,
requireSsoForLogin: true,
projectId: true,
smtpConfig: {
_id: true,
hostname: true,
port: true,
username: true,
password: true,
fromEmail: true,
fromName: true,
secure: true,
},
},
},
);
@@ -689,6 +713,9 @@ router.post(
},
{
projectId: statusPage.projectId!,
mailServer: ProjectSMTPConfigService.toEmailServer(
statusPage.smtpConfig,
),
statusPageId: statusPage.id!,
},
).catch((err: Error) => {

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,262 @@
import ProjectSCIMLog from "Common/Models/DatabaseModels/ProjectSCIMLog";
import StatusPageSCIMLog from "Common/Models/DatabaseModels/StatusPageSCIMLog";
import ProjectSCIMLogService from "Common/Server/Services/ProjectSCIMLogService";
import StatusPageSCIMLogService from "Common/Server/Services/StatusPageSCIMLogService";
import logger from "Common/Server/Utils/Logger";
import ObjectID from "Common/Types/ObjectID";
import SCIMLogStatus from "Common/Types/SCIM/SCIMLogStatus";
import { JSONObject, JSONValue, JSONArray } from "Common/Types/JSON";
export interface ProjectSCIMLogData {
projectId: ObjectID;
projectScimId: ObjectID;
operationType: string;
status: SCIMLogStatus;
statusMessage?: string | undefined;
httpMethod?: string | undefined;
requestPath?: string | undefined;
httpStatusCode?: number | undefined;
affectedUserEmail?: string | undefined;
affectedGroupName?: string | undefined;
requestBody?: JSONObject | undefined;
responseBody?: JSONObject | undefined;
queryParams?: JSONObject | undefined;
steps?: string[] | undefined;
userInfo?: JSONObject | undefined;
groupInfo?: JSONObject | undefined;
additionalContext?: JSONObject | undefined;
}
export interface StatusPageSCIMLogData {
projectId: ObjectID;
statusPageId: ObjectID;
statusPageScimId: ObjectID;
operationType: string;
status: SCIMLogStatus;
statusMessage?: string | undefined;
httpMethod?: string | undefined;
requestPath?: string | undefined;
httpStatusCode?: number | undefined;
affectedUserEmail?: string | undefined;
requestBody?: JSONObject | undefined;
responseBody?: JSONObject | undefined;
queryParams?: JSONObject | undefined;
steps?: string[] | undefined;
userInfo?: JSONObject | undefined;
additionalContext?: JSONObject | undefined;
}
const sanitizeSensitiveData: (
data: JSONObject | undefined,
) => JSONObject | undefined = (
data: JSONObject | undefined,
): JSONObject | undefined => {
if (!data) {
return undefined;
}
const sanitized: JSONObject = { ...data };
const sensitiveKeys: string[] = [
"password",
"bearerToken",
"bearer_token",
"authorization",
"Authorization",
"token",
"secret",
"apiKey",
"api_key",
];
const sanitizeRecursive: (obj: JSONObject) => JSONObject = (
obj: JSONObject,
): JSONObject => {
const result: JSONObject = {};
for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
const value: JSONValue = obj[key];
if (
sensitiveKeys.some((k: string) => {
return key.toLowerCase().includes(k.toLowerCase());
})
) {
result[key] = "[REDACTED]";
} else if (
typeof value === "object" &&
value !== null &&
!Array.isArray(value)
) {
result[key] = sanitizeRecursive(value as JSONObject);
} else if (Array.isArray(value)) {
result[key] = (value as JSONArray).map((item: JSONValue) => {
if (
typeof item === "object" &&
item !== null &&
!Array.isArray(item)
) {
return sanitizeRecursive(item as JSONObject);
}
return item;
}) as JSONArray;
} else {
result[key] = value;
}
}
}
return result;
};
return sanitizeRecursive(sanitized);
};
export interface LogBodyDetails {
requestBody?: JSONObject | undefined;
responseBody?: JSONObject | undefined;
timestamp: Date;
queryParams?: JSONObject | undefined;
steps?: string[] | undefined;
userInfo?: JSONObject | undefined;
groupInfo?: JSONObject | undefined;
additionalContext?: JSONObject | undefined;
}
const buildLogBody: (data: LogBodyDetails) => string = (
data: LogBodyDetails,
): string => {
const logBody: JSONObject = {
timestamp: data.timestamp.toISOString(),
executedAt: data.timestamp.toISOString(),
};
if (data.queryParams && Object.keys(data.queryParams).length > 0) {
logBody["queryParameters"] = data.queryParams;
}
if (data.requestBody) {
logBody["request"] = sanitizeSensitiveData(data.requestBody);
}
if (data.responseBody) {
logBody["response"] = sanitizeSensitiveData(data.responseBody);
}
if (data.steps && data.steps.length > 0) {
logBody["executionSteps"] = data.steps;
}
if (data.userInfo) {
logBody["userDetails"] = sanitizeSensitiveData(data.userInfo);
}
if (data.groupInfo) {
logBody["groupDetails"] = sanitizeSensitiveData(data.groupInfo);
}
if (data.additionalContext) {
logBody["additionalContext"] = sanitizeSensitiveData(
data.additionalContext,
);
}
return JSON.stringify(logBody, null, 2);
};
export const createProjectSCIMLog: (
data: ProjectSCIMLogData,
) => Promise<void> = async (data: ProjectSCIMLogData): Promise<void> => {
try {
const log: ProjectSCIMLog = new ProjectSCIMLog();
log.projectId = data.projectId;
log.projectScimId = data.projectScimId;
log.operationType = data.operationType;
log.status = data.status;
if (data.statusMessage !== undefined) {
log.statusMessage = data.statusMessage;
}
if (data.httpMethod !== undefined) {
log.httpMethod = data.httpMethod;
}
if (data.requestPath !== undefined) {
log.requestPath = data.requestPath;
}
if (data.httpStatusCode !== undefined) {
log.httpStatusCode = data.httpStatusCode;
}
if (data.affectedUserEmail !== undefined) {
log.affectedUserEmail = data.affectedUserEmail;
}
if (data.affectedGroupName !== undefined) {
log.affectedGroupName = data.affectedGroupName;
}
log.logBody = buildLogBody({
requestBody: data.requestBody,
responseBody: data.responseBody,
timestamp: new Date(),
queryParams: data.queryParams,
steps: data.steps,
userInfo: data.userInfo,
groupInfo: data.groupInfo,
additionalContext: data.additionalContext,
});
await ProjectSCIMLogService.create({
data: log,
props: { isRoot: true },
});
} catch (err) {
// Log errors silently to not affect SCIM operations
logger.error("Failed to create Project SCIM log entry:");
logger.error(err);
}
};
export const createStatusPageSCIMLog: (
data: StatusPageSCIMLogData,
) => Promise<void> = async (data: StatusPageSCIMLogData): Promise<void> => {
try {
const log: StatusPageSCIMLog = new StatusPageSCIMLog();
log.projectId = data.projectId;
log.statusPageId = data.statusPageId;
log.statusPageScimId = data.statusPageScimId;
log.operationType = data.operationType;
log.status = data.status;
if (data.statusMessage !== undefined) {
log.statusMessage = data.statusMessage;
}
if (data.httpMethod !== undefined) {
log.httpMethod = data.httpMethod;
}
if (data.requestPath !== undefined) {
log.requestPath = data.requestPath;
}
if (data.httpStatusCode !== undefined) {
log.httpStatusCode = data.httpStatusCode;
}
if (data.affectedUserEmail !== undefined) {
log.affectedUserEmail = data.affectedUserEmail;
}
log.logBody = buildLogBody({
requestBody: data.requestBody,
responseBody: data.responseBody,
timestamp: new Date(),
queryParams: data.queryParams,
steps: data.steps,
userInfo: data.userInfo,
additionalContext: data.additionalContext,
});
await StatusPageSCIMLogService.create({
data: log,
props: { isRoot: true },
});
} catch (err) {
// Log errors silently to not affect SCIM operations
logger.error("Failed to create Status Page SCIM log entry:");
logger.error(err);
}
};
export default {
createProjectSCIMLog,
createStatusPageSCIMLog,
};

View File

@@ -1,10 +1,11 @@
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="preload" href="https://fonts.googleapis.com/css2?family=Inter:wght@100;200;300;400;500;600;700;800;900&display=swap" as="style">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@100;200;300;400;500;600;700;800;900&display=swap"
rel="stylesheet">
<style>
* {
font-family: Inter;
font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
}
@@ -34,6 +35,7 @@
width: auto;
}
</style>
<link rel="preload" href="https://cdn.tailwindcss.com" as="script">
<script src="https://cdn.tailwindcss.com"></script>
<!-- Google Tag Manager -->

View File

@@ -0,0 +1,743 @@
import CallProviderFactory from "../Providers/CallProviderFactory";
import { getProjectTwilioConfig } from "../Utils/TwilioConfigHelper";
import {
DialStatusData,
ICallProvider,
IncomingCallData,
WebhookRequest,
} from "Common/Types/Call/CallProvider";
import TwilioConfig from "Common/Types/CallAndSMS/TwilioConfig";
import IncomingCallStatus from "Common/Types/IncomingCall/IncomingCallStatus";
import BadDataException from "Common/Types/Exception/BadDataException";
import ObjectID from "Common/Types/ObjectID";
import IncomingCallPolicyService from "Common/Server/Services/IncomingCallPolicyService";
import IncomingCallPolicyEscalationRuleService from "Common/Server/Services/IncomingCallPolicyEscalationRuleService";
import IncomingCallLogService from "Common/Server/Services/IncomingCallLogService";
import IncomingCallLogItemService from "Common/Server/Services/IncomingCallLogItemService";
import OnCallDutyPolicyScheduleService from "Common/Server/Services/OnCallDutyPolicyScheduleService";
import UserService from "Common/Server/Services/UserService";
import UserIncomingCallNumberService from "Common/Server/Services/UserIncomingCallNumberService";
import UserIncomingCallNumber from "Common/Models/DatabaseModels/UserIncomingCallNumber";
import Express, {
ExpressRequest,
ExpressResponse,
ExpressRouter,
NextFunction,
} from "Common/Server/Utils/Express";
import logger from "Common/Server/Utils/Logger";
import IncomingCallPolicy from "Common/Models/DatabaseModels/IncomingCallPolicy";
import IncomingCallPolicyEscalationRule from "Common/Models/DatabaseModels/IncomingCallPolicyEscalationRule";
import IncomingCallLog from "Common/Models/DatabaseModels/IncomingCallLog";
import IncomingCallLogItem from "Common/Models/DatabaseModels/IncomingCallLogItem";
import User from "Common/Models/DatabaseModels/User";
import Phone from "Common/Types/Phone";
import { Host, HttpProtocol } from "Common/Server/EnvironmentConfig";
const router: ExpressRouter = Express.getRouter();
// Handle incoming voice call - single endpoint for all phone numbers
router.post(
"/voice",
async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
try {
// Parse the called phone number from the request body (Twilio sends this)
const calledPhoneNumber: string = req.body["To"] || req.body["Called"];
if (!calledPhoneNumber) {
logger.error("No called phone number in request");
res.status(400).send("Bad Request");
return;
}
// Find the policy by the called phone number
const policy: IncomingCallPolicy | null =
await IncomingCallPolicyService.findOneBy({
query: {
routingPhoneNumber: new Phone(calledPhoneNumber),
},
select: {
_id: true,
projectId: true,
projectCallSMSConfigId: true,
isEnabled: true,
greetingMessage: true,
noAnswerMessage: true,
noOneAvailableMessage: true,
repeatPolicyIfNoOneAnswers: true,
repeatPolicyIfNoOneAnswersTimes: true,
routingPhoneNumber: true,
},
props: {
isRoot: true,
},
});
if (!policy) {
logger.error(
`Incoming call policy not found for phone number: ${calledPhoneNumber}`,
);
res.status(404).send("Policy not found");
return;
}
// Require project-level Twilio config
if (!policy.projectCallSMSConfigId) {
logger.error(
`Policy ${policy.id?.toString()} does not have a project Twilio config`,
);
res.status(400).send("Policy not configured correctly");
return;
}
// Get project Twilio config
const customTwilioConfig: TwilioConfig | null =
await getProjectTwilioConfig(policy.projectCallSMSConfigId);
if (!customTwilioConfig) {
logger.error(
`Project Twilio config not found for policy ${policy.id?.toString()}`,
);
res.status(400).send("Twilio configuration not found");
return;
}
// Get provider with project config
const provider: ICallProvider =
CallProviderFactory.getProviderWithConfig(customTwilioConfig);
// Validate webhook signature to ensure request is from the call provider
const signature: string =
(req.headers["x-twilio-signature"] as string) || "";
// Debug logging
logger.debug("=== Incoming Call Webhook Debug ===");
logger.debug(`Original URL: ${req.originalUrl}`);
logger.debug(`Base URL: ${req.baseUrl}`);
logger.debug(`Path: ${req.path}`);
logger.debug(`Protocol: ${req.protocol}`);
logger.debug(`Host header: ${req.get("host")}`);
logger.debug(`X-Forwarded-Proto: ${req.get("x-forwarded-proto")}`);
logger.debug(`X-Forwarded-Host: ${req.get("x-forwarded-host")}`);
logger.debug(`Twilio Signature: ${signature}`);
logger.debug(`Environment HOST: ${Host}`);
logger.debug(`Environment HttpProtocol: ${HttpProtocol}`);
logger.debug("=== End Debug ===");
if (
!provider.validateWebhookSignature(
req as unknown as WebhookRequest,
signature,
)
) {
logger.error("Invalid webhook signature for incoming call");
res.status(403).send("Forbidden");
return;
}
// Parse incoming call data
const callData: IncomingCallData = provider.parseIncomingCallWebhook(
req as unknown as WebhookRequest,
);
const policyId: string = policy.id!.toString();
// Create call log early so we can track all outcomes
const callLog: IncomingCallLog = new IncomingCallLog();
if (policy.projectId) {
callLog.projectId = policy.projectId;
}
callLog.incomingCallPolicyId = new ObjectID(policyId);
callLog.callerPhoneNumber = new Phone(callData.callerPhoneNumber);
if (policy.routingPhoneNumber) {
callLog.routingPhoneNumber = policy.routingPhoneNumber;
}
callLog.callProviderCallId = callData.callId;
callLog.status = IncomingCallStatus.Initiated;
callLog.startedAt = new Date();
callLog.currentEscalationRuleOrder = 1;
callLog.repeatCount = 0;
// Check if policy is enabled
if (!policy.isEnabled) {
callLog.status = IncomingCallStatus.Failed;
callLog.statusMessage = "Policy is disabled";
callLog.endedAt = new Date();
await IncomingCallLogService.create({
data: callLog,
props: { isRoot: true },
});
const twiml: string = provider.generateHangupResponse(
"Sorry, this service is currently disabled.",
);
res.type("text/xml");
return res.send(twiml);
}
// Save the call log now that initial checks passed
const createdCallLog: IncomingCallLog =
await IncomingCallLogService.create({
data: callLog,
props: {
isRoot: true,
},
});
// Get the first escalation rule
const firstRule: IncomingCallPolicyEscalationRule | null =
await IncomingCallPolicyEscalationRuleService.findOneBy({
query: {
incomingCallPolicyId: new ObjectID(policyId),
order: 1,
},
select: {
_id: true,
name: true,
escalateAfterSeconds: true,
onCallDutyPolicyScheduleId: true,
userId: true,
},
props: {
isRoot: true,
},
});
if (!firstRule) {
await IncomingCallLogService.updateOneById({
id: createdCallLog.id!,
data: {
status: IncomingCallStatus.Failed,
statusMessage: "No escalation rules configured",
endedAt: new Date(),
},
props: { isRoot: true },
});
const twiml: string = provider.generateHangupResponse(
policy.noOneAvailableMessage ||
"We're sorry, but no on-call engineer is currently available.",
);
res.type("text/xml");
return res.send(twiml);
}
// Get the user to call
const userToCall: UserToCall | null = await getUserToCall(
firstRule,
policy.projectId!,
);
if (!userToCall) {
await IncomingCallLogService.updateOneById({
id: createdCallLog.id!,
data: {
status: IncomingCallStatus.Failed,
statusMessage:
"No on-call user available or user has no phone number",
endedAt: new Date(),
},
props: { isRoot: true },
});
const twiml: string = provider.generateHangupResponse(
policy.noOneAvailableMessage ||
"We're sorry, but no on-call engineer is currently available.",
);
res.type("text/xml");
return res.send(twiml);
}
// Create call log item
const callLogItem: IncomingCallLogItem = new IncomingCallLogItem();
if (policy.projectId) {
callLogItem.projectId = policy.projectId;
}
callLogItem.incomingCallLogId = createdCallLog.id!;
if (firstRule.id) {
callLogItem.incomingCallPolicyEscalationRuleId = firstRule.id;
}
callLogItem.userId = userToCall.userId;
callLogItem.userPhoneNumber = userToCall.phoneNumber;
callLogItem.status = IncomingCallStatus.Ringing;
callLogItem.startedAt = new Date();
callLogItem.isAnswered = false;
const createdCallLogItem: IncomingCallLogItem =
await IncomingCallLogItemService.create({
data: callLogItem,
props: {
isRoot: true,
},
});
// Generate TwiML response
const greetingMessage: string =
policy.greetingMessage ||
"Please wait while we connect you to the on-call engineer.";
// Construct status callback URL
const statusCallbackUrl: string = `${HttpProtocol}${Host}/notification/incoming-call/dial-status/${createdCallLog.id?.toString()}/${createdCallLogItem.id?.toString()}`;
// Generate greeting + dial TwiML
const twiml: string = generateGreetingAndDialTwiml(
provider,
greetingMessage,
userToCall.phoneNumber.toString(),
policy.routingPhoneNumber?.toString() || callData.calledPhoneNumber,
firstRule.escalateAfterSeconds || 30,
statusCallbackUrl,
);
res.type("text/xml");
return res.send(twiml);
} catch (err) {
logger.error(err);
return next(err);
}
},
);
// Handle dial status callback
router.post(
"/dial-status/:callLogId/:callLogItemId",
async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
try {
const callLogId: string = req.params["callLogId"] as string;
const callLogItemId: string = req.params["callLogItemId"] as string;
if (!callLogId || !callLogItemId) {
throw new BadDataException("Invalid webhook URL");
}
// Get the call log to find the policy and its Twilio config
const callLog: IncomingCallLog | null =
await IncomingCallLogService.findOneById({
id: new ObjectID(callLogId),
select: {
_id: true,
currentEscalationRuleOrder: true,
repeatCount: true,
incomingCallPolicyId: true,
},
props: {
isRoot: true,
},
});
if (!callLog) {
logger.error(`Call log not found: ${callLogId}`);
res.status(404).send("Call log not found");
return;
}
// Get the policy with its Twilio config
const policy: IncomingCallPolicy | null =
await IncomingCallPolicyService.findOneById({
id: callLog.incomingCallPolicyId!,
select: {
_id: true,
projectId: true,
projectCallSMSConfigId: true,
noAnswerMessage: true,
noOneAvailableMessage: true,
repeatPolicyIfNoOneAnswers: true,
repeatPolicyIfNoOneAnswersTimes: true,
routingPhoneNumber: true,
},
props: {
isRoot: true,
},
});
if (!policy || !policy.projectCallSMSConfigId) {
logger.error("Policy or Twilio config not found");
res.status(400).send("Configuration error");
return;
}
// Get project Twilio config
const customTwilioConfig: TwilioConfig | null =
await getProjectTwilioConfig(policy.projectCallSMSConfigId);
if (!customTwilioConfig) {
logger.error("Twilio config not found for policy");
res.status(400).send("Configuration error");
return;
}
// Get provider with project config
const provider: ICallProvider =
CallProviderFactory.getProviderWithConfig(customTwilioConfig);
// Validate webhook signature to ensure request is from the call provider
const signature: string =
(req.headers["x-twilio-signature"] as string) || "";
if (
!provider.validateWebhookSignature(
req as unknown as WebhookRequest,
signature,
)
) {
logger.error("Invalid webhook signature for dial status callback");
res.status(403).send("Forbidden");
return;
}
// Parse dial status
const dialStatus: DialStatusData = provider.parseDialStatusWebhook(
req as unknown as WebhookRequest,
);
// Get the call log item
const callLogItem: IncomingCallLogItem | null =
await IncomingCallLogItemService.findOneById({
id: new ObjectID(callLogItemId),
select: {
_id: true,
incomingCallLogId: true,
},
props: {
isRoot: true,
},
});
if (!callLogItem) {
logger.error(`Call log item not found: ${callLogItemId}`);
const twiml: string = provider.generateHangupResponse();
res.type("text/xml");
return res.send(twiml);
}
// Update call log item
const now: Date = new Date();
await IncomingCallLogItemService.updateOneById({
id: new ObjectID(callLogItemId),
data: {
status:
dialStatus.dialStatus === "completed"
? IncomingCallStatus.Connected
: IncomingCallStatus.NoAnswer,
dialDurationInSeconds: dialStatus.dialDurationSeconds || 0,
endedAt: now,
isAnswered: dialStatus.dialStatus === "completed",
},
props: {
isRoot: true,
},
});
// If call was answered, mark as completed
if (dialStatus.dialStatus === "completed") {
await IncomingCallLogService.updateOneById({
id: new ObjectID(callLogId),
data: {
status: IncomingCallStatus.Completed,
endedAt: now,
},
props: {
isRoot: true,
},
});
// Hang up - the call is complete
const twiml: string = provider.generateHangupResponse();
res.type("text/xml");
return res.send(twiml);
}
// Call was not answered, try next escalation rule
const nextOrder: number = (callLog.currentEscalationRuleOrder || 1) + 1;
// Get the next escalation rule
const nextRule: IncomingCallPolicyEscalationRule | null =
await IncomingCallPolicyEscalationRuleService.findOneBy({
query: {
incomingCallPolicyId: callLog.incomingCallPolicyId!,
order: nextOrder,
},
select: {
_id: true,
name: true,
escalateAfterSeconds: true,
onCallDutyPolicyScheduleId: true,
userId: true,
},
props: {
isRoot: true,
},
});
if (!nextRule) {
// No more rules, check if we should repeat
if (
policy.repeatPolicyIfNoOneAnswers &&
(callLog.repeatCount || 0) <
(policy.repeatPolicyIfNoOneAnswersTimes || 1)
) {
// Restart from first rule
await IncomingCallLogService.updateOneById({
id: new ObjectID(callLogId),
data: {
currentEscalationRuleOrder: 1,
repeatCount: (callLog.repeatCount || 0) + 1,
},
props: {
isRoot: true,
},
});
// Get first rule again
const firstRule: IncomingCallPolicyEscalationRule | null =
await IncomingCallPolicyEscalationRuleService.findOneBy({
query: {
incomingCallPolicyId: callLog.incomingCallPolicyId!,
order: 1,
},
select: {
_id: true,
name: true,
escalateAfterSeconds: true,
onCallDutyPolicyScheduleId: true,
userId: true,
},
props: {
isRoot: true,
},
});
if (firstRule && policy.projectId) {
const userToCall: UserToCall | null = await getUserToCall(
firstRule,
policy.projectId,
);
if (userToCall) {
// Continue with the call
return await dialNextUser(
res,
provider,
policy,
callLog,
firstRule,
userToCall,
);
}
}
}
// No more options, end the call
await IncomingCallLogService.updateOneById({
id: new ObjectID(callLogId),
data: {
status: IncomingCallStatus.NoAnswer,
endedAt: now,
},
props: {
isRoot: true,
},
});
const twiml: string = provider.generateHangupResponse(
policy.noAnswerMessage ||
"No one is available. Please try again later.",
);
res.type("text/xml");
return res.send(twiml);
}
// Update call log with new escalation rule order
await IncomingCallLogService.updateOneById({
id: new ObjectID(callLogId),
data: {
currentEscalationRuleOrder: nextOrder,
status: IncomingCallStatus.Escalated,
},
props: {
isRoot: true,
},
});
// Get the user to call
const userToCall: UserToCall | null = await getUserToCall(
nextRule,
policy.projectId!,
);
if (!userToCall) {
/*
* Skip this rule and try the next one (recursive approach via TwiML redirect would be complex)
* For simplicity, end the call if no user available
*/
await IncomingCallLogService.updateOneById({
id: new ObjectID(callLogId),
data: {
status: IncomingCallStatus.Failed,
statusMessage:
"No on-call user available or user has no phone number",
endedAt: new Date(),
},
props: { isRoot: true },
});
const twiml: string = provider.generateHangupResponse(
policy.noOneAvailableMessage ||
"We're sorry, but no on-call engineer is currently available.",
);
res.type("text/xml");
return res.send(twiml);
}
// Dial the next user
return await dialNextUser(
res,
provider,
policy,
callLog,
nextRule,
userToCall,
);
} catch (err) {
logger.error(err);
return next(err);
}
},
);
// Interface for user with phone number to call
interface UserToCall {
userId: ObjectID;
phoneNumber: Phone;
name?: string | undefined;
email?: string | undefined;
}
// Helper function to get user to call from escalation rule
async function getUserToCall(
rule: IncomingCallPolicyEscalationRule,
projectId: ObjectID,
): Promise<UserToCall | null> {
let userId: ObjectID | null = null;
// If rule has a direct user, use that
if (rule.userId) {
userId = rule.userId;
} else if (rule.onCallDutyPolicyScheduleId) {
// If rule has an on-call schedule, get the current on-call user
userId = await OnCallDutyPolicyScheduleService.getCurrentUserIdInSchedule(
rule.onCallDutyPolicyScheduleId,
);
}
if (!userId) {
return null;
}
// Check if the user has a verified incoming call number for this project
const verifiedIncomingCallNumber: UserIncomingCallNumber | null =
await UserIncomingCallNumberService.findOneBy({
query: {
userId: userId,
projectId: projectId,
isVerified: true,
},
select: {
phone: true,
},
props: {
isRoot: true,
},
});
if (!verifiedIncomingCallNumber || !verifiedIncomingCallNumber.phone) {
// No verified incoming call number for this user in this project
return null;
}
// Get user details for logging
const user: User | null = await UserService.findOneById({
id: userId,
select: {
_id: true,
name: true,
email: true,
},
props: {
isRoot: true,
},
});
return {
userId: userId,
phoneNumber: verifiedIncomingCallNumber.phone,
name: user?.name?.toString(),
email: user?.email?.toString(),
};
}
// Helper function to generate greeting and dial TwiML
function generateGreetingAndDialTwiml(
provider: ICallProvider,
greetingMessage: string,
toPhoneNumber: string,
fromPhoneNumber: string,
timeoutSeconds: number,
statusCallbackUrl: string,
): string {
// Use the escalation response which says a message then dials
return provider.generateEscalationResponse(greetingMessage, {
toPhoneNumber,
fromPhoneNumber,
timeoutSeconds,
statusCallbackUrl,
});
}
// Helper function to dial the next user
async function dialNextUser(
res: ExpressResponse,
provider: ICallProvider,
policy: IncomingCallPolicy,
callLog: IncomingCallLog,
rule: IncomingCallPolicyEscalationRule,
userToCall: UserToCall,
): Promise<ExpressResponse> {
// Create call log item
const callLogItem: IncomingCallLogItem = new IncomingCallLogItem();
if (policy.projectId) {
callLogItem.projectId = policy.projectId;
}
callLogItem.incomingCallLogId = callLog.id!;
if (rule.id) {
callLogItem.incomingCallPolicyEscalationRuleId = rule.id;
}
callLogItem.userId = userToCall.userId;
callLogItem.userPhoneNumber = userToCall.phoneNumber;
callLogItem.status = IncomingCallStatus.Ringing;
callLogItem.startedAt = new Date();
callLogItem.isAnswered = false;
const createdCallLogItem: IncomingCallLogItem =
await IncomingCallLogItemService.create({
data: callLogItem,
props: {
isRoot: true,
},
});
// Construct status callback URL
const statusCallbackUrl: string = `${HttpProtocol}${Host}/notification/incoming-call/dial-status/${callLog.id?.toString()}/${createdCallLogItem.id?.toString()}`;
// Generate dial TwiML with escalation message
const escalationMessage: string = `Connecting you to the next available engineer.`;
const twiml: string = provider.generateEscalationResponse(escalationMessage, {
toPhoneNumber: userToCall.phoneNumber.toString(),
fromPhoneNumber: policy.routingPhoneNumber?.toString() || "",
timeoutSeconds: rule.escalateAfterSeconds || 30,
statusCallbackUrl,
});
res.type("text/xml");
return res.send(twiml);
}
export default router;

View File

@@ -0,0 +1,612 @@
import CallProviderFactory from "../Providers/CallProviderFactory";
import { getProjectTwilioConfig } from "../Utils/TwilioConfigHelper";
import { HttpProtocol, Host } from "Common/Server/EnvironmentConfig";
import {
AvailablePhoneNumber,
ICallProvider,
OwnedPhoneNumber,
PurchasedPhoneNumber,
} from "Common/Types/Call/CallProvider";
import TwilioConfig from "Common/Types/CallAndSMS/TwilioConfig";
import BadDataException from "Common/Types/Exception/BadDataException";
import { JSONObject } from "Common/Types/JSON";
import ObjectID from "Common/Types/ObjectID";
import IncomingCallPolicyService from "Common/Server/Services/IncomingCallPolicyService";
import ProjectService from "Common/Server/Services/ProjectService";
import Express, {
ExpressRequest,
ExpressResponse,
ExpressRouter,
NextFunction,
} from "Common/Server/Utils/Express";
import Response from "Common/Server/Utils/Response";
import logger from "Common/Server/Utils/Logger";
import IncomingCallPolicy from "Common/Models/DatabaseModels/IncomingCallPolicy";
import Project from "Common/Models/DatabaseModels/Project";
import Phone from "Common/Types/Phone";
const router: ExpressRouter = Express.getRouter();
// Search available phone numbers
router.post(
"/search",
async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
try {
const body: JSONObject = req.body as JSONObject;
const projectId: ObjectID | undefined = body["projectId"]
? new ObjectID(body["projectId"] as string)
: undefined;
const projectCallSMSConfigId: ObjectID | undefined = body[
"projectCallSMSConfigId"
]
? new ObjectID(body["projectCallSMSConfigId"] as string)
: undefined;
if (!projectId) {
throw new BadDataException("projectId is required");
}
if (!projectCallSMSConfigId) {
throw new BadDataException(
"projectCallSMSConfigId is required. Please configure a project-level Twilio configuration.",
);
}
const countryCode: string | undefined = body["countryCode"] as
| string
| undefined;
const areaCode: string | undefined = body["areaCode"] as
| string
| undefined;
const contains: string | undefined = body["contains"] as
| string
| undefined;
if (!countryCode) {
throw new BadDataException("countryCode is required");
}
// Check if project exists
const project: Project | null = await ProjectService.findOneById({
id: projectId,
select: {
_id: true,
name: true,
},
props: {
isRoot: true,
},
});
if (!project) {
throw new BadDataException("Project not found");
}
// Get project Twilio config
const customTwilioConfig: TwilioConfig | null =
await getProjectTwilioConfig(projectCallSMSConfigId);
if (!customTwilioConfig) {
throw new BadDataException("Project Call/SMS Config not found");
}
const provider: ICallProvider =
CallProviderFactory.getProviderWithConfig(customTwilioConfig);
const searchOptions: {
countryCode: string;
areaCode?: string;
contains?: string;
limit?: number;
} = {
countryCode,
limit: 10,
};
if (areaCode) {
searchOptions.areaCode = areaCode;
}
if (contains) {
searchOptions.contains = contains;
}
const numbers: AvailablePhoneNumber[] =
await provider.searchAvailableNumbers(searchOptions);
// Customer pays Twilio directly - just return the phone numbers
type ResponseNumber = {
phoneNumber: string;
friendlyName: string;
locality?: string;
region?: string;
country: string;
};
const responseNumbers: Array<ResponseNumber> = numbers.map(
(n: AvailablePhoneNumber): ResponseNumber => {
const result: ResponseNumber = {
phoneNumber: n.phoneNumber,
friendlyName: n.friendlyName,
country: n.country,
};
if (n.locality) {
result.locality = n.locality;
}
if (n.region) {
result.region = n.region;
}
return result;
},
);
return Response.sendJsonObjectResponse(req, res, {
availableNumbers: responseNumbers,
});
} catch (err) {
logger.error(err);
return next(err);
}
},
);
// List owned phone numbers (already purchased in Twilio account)
router.post(
"/list-owned",
async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
try {
const body: JSONObject = req.body as JSONObject;
const projectId: ObjectID | undefined = body["projectId"]
? new ObjectID(body["projectId"] as string)
: undefined;
const projectCallSMSConfigId: ObjectID | undefined = body[
"projectCallSMSConfigId"
]
? new ObjectID(body["projectCallSMSConfigId"] as string)
: undefined;
if (!projectId) {
throw new BadDataException("projectId is required");
}
if (!projectCallSMSConfigId) {
throw new BadDataException(
"projectCallSMSConfigId is required. Please configure a project-level Twilio configuration.",
);
}
// Check if project exists
const project: Project | null = await ProjectService.findOneById({
id: projectId,
select: {
_id: true,
name: true,
},
props: {
isRoot: true,
},
});
if (!project) {
throw new BadDataException("Project not found");
}
// Get project Twilio config
const customTwilioConfig: TwilioConfig | null =
await getProjectTwilioConfig(projectCallSMSConfigId);
if (!customTwilioConfig) {
throw new BadDataException("Project Call/SMS Config not found");
}
const provider: ICallProvider =
CallProviderFactory.getProviderWithConfig(customTwilioConfig);
const numbers: OwnedPhoneNumber[] = await provider.listOwnedNumbers();
type ResponseNumber = {
phoneNumberId: string;
phoneNumber: string;
friendlyName: string;
voiceUrl?: string | undefined;
};
const responseNumbers: Array<ResponseNumber> = numbers.map(
(n: OwnedPhoneNumber): ResponseNumber => {
return {
phoneNumberId: n.phoneNumberId,
phoneNumber: n.phoneNumber,
friendlyName: n.friendlyName,
voiceUrl: n.voiceUrl,
};
},
);
return Response.sendJsonObjectResponse(req, res, {
ownedNumbers: responseNumbers,
});
} catch (err) {
logger.error(err);
return next(err);
}
},
);
// Assign an existing phone number to a policy
router.post(
"/assign-existing",
async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
try {
const body: JSONObject = req.body as JSONObject;
const projectId: ObjectID | undefined = body["projectId"]
? new ObjectID(body["projectId"] as string)
: undefined;
const phoneNumberId: string | undefined = body["phoneNumberId"] as
| string
| undefined;
const phoneNumber: string | undefined = body["phoneNumber"] as
| string
| undefined;
const incomingCallPolicyId: ObjectID | undefined = body[
"incomingCallPolicyId"
]
? new ObjectID(body["incomingCallPolicyId"] as string)
: undefined;
if (!projectId) {
throw new BadDataException("projectId is required");
}
if (!phoneNumberId) {
throw new BadDataException("phoneNumberId is required");
}
if (!phoneNumber) {
throw new BadDataException("phoneNumber is required");
}
if (!incomingCallPolicyId) {
throw new BadDataException("incomingCallPolicyId is required");
}
// Check if project exists
const project: Project | null = await ProjectService.findOneById({
id: projectId,
select: {
_id: true,
name: true,
},
props: {
isRoot: true,
},
});
if (!project) {
throw new BadDataException("Project not found");
}
// Check if incoming call policy exists and get its project config
const incomingCallPolicy: IncomingCallPolicy | null =
await IncomingCallPolicyService.findOneById({
id: incomingCallPolicyId,
select: {
_id: true,
projectId: true,
projectCallSMSConfigId: true,
routingPhoneNumber: true,
},
props: {
isRoot: true,
},
});
if (!incomingCallPolicy) {
throw new BadDataException("Incoming Call Policy not found");
}
if (incomingCallPolicy.projectId?.toString() !== projectId.toString()) {
throw new BadDataException(
"Incoming Call Policy does not belong to this project",
);
}
if (incomingCallPolicy.routingPhoneNumber) {
throw new BadDataException(
"This policy already has a phone number. Please release it first.",
);
}
// Require project-level Twilio config
if (!incomingCallPolicy.projectCallSMSConfigId) {
throw new BadDataException(
"This policy does not have a project Twilio configuration. Please configure one first.",
);
}
// Get project Twilio config
const customTwilioConfig: TwilioConfig | null =
await getProjectTwilioConfig(incomingCallPolicy.projectCallSMSConfigId);
if (!customTwilioConfig) {
throw new BadDataException("Project Call/SMS Config not found");
}
const provider: ICallProvider =
CallProviderFactory.getProviderWithConfig(customTwilioConfig);
/*
* Construct webhook URL - single endpoint for all phone numbers
* Twilio sends the "To" phone number in every webhook, so we look up the policy by phone number
*/
const webhookUrl: string = `${HttpProtocol}${Host}/notification/incoming-call/voice`;
const assigned: PurchasedPhoneNumber =
await provider.assignExistingNumber(phoneNumberId, webhookUrl);
// Get country code from phone number
const countryCode: string =
Phone.getCountryCodeFromPhoneNumber(phoneNumber);
const areaCode: string = Phone.getAreaCodeFromPhoneNumber(phoneNumber);
/*
* Update the incoming call policy with the assigned number
*/
await IncomingCallPolicyService.updateOneById({
id: incomingCallPolicyId,
data: {
routingPhoneNumber: new Phone(assigned.phoneNumber),
callProviderPhoneNumberId: assigned.phoneNumberId,
phoneNumberCountryCode: countryCode,
phoneNumberAreaCode: areaCode,
phoneNumberPurchasedAt: new Date(),
},
props: {
isRoot: true,
},
});
return Response.sendJsonObjectResponse(req, res, {
success: true,
phoneNumberId: assigned.phoneNumberId,
phoneNumber: assigned.phoneNumber,
});
} catch (err) {
logger.error(err);
return next(err);
}
},
);
// Purchase a phone number
router.post(
"/purchase",
async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
try {
const body: JSONObject = req.body as JSONObject;
const projectId: ObjectID | undefined = body["projectId"]
? new ObjectID(body["projectId"] as string)
: undefined;
const phoneNumber: string | undefined = body["phoneNumber"] as
| string
| undefined;
const incomingCallPolicyId: ObjectID | undefined = body[
"incomingCallPolicyId"
]
? new ObjectID(body["incomingCallPolicyId"] as string)
: undefined;
if (!projectId) {
throw new BadDataException("projectId is required");
}
if (!phoneNumber) {
throw new BadDataException("phoneNumber is required");
}
if (!incomingCallPolicyId) {
throw new BadDataException("incomingCallPolicyId is required");
}
// Check if project exists
const project: Project | null = await ProjectService.findOneById({
id: projectId,
select: {
_id: true,
name: true,
},
props: {
isRoot: true,
},
});
if (!project) {
throw new BadDataException("Project not found");
}
// Check if incoming call policy exists and get its project config
const incomingCallPolicy: IncomingCallPolicy | null =
await IncomingCallPolicyService.findOneById({
id: incomingCallPolicyId,
select: {
_id: true,
projectId: true,
projectCallSMSConfigId: true,
routingPhoneNumber: true,
},
props: {
isRoot: true,
},
});
if (!incomingCallPolicy) {
throw new BadDataException("Incoming Call Policy not found");
}
if (incomingCallPolicy.projectId?.toString() !== projectId.toString()) {
throw new BadDataException(
"Incoming Call Policy does not belong to this project",
);
}
if (incomingCallPolicy.routingPhoneNumber) {
throw new BadDataException(
"This policy already has a phone number. Please release it first.",
);
}
// Require project-level Twilio config
if (!incomingCallPolicy.projectCallSMSConfigId) {
throw new BadDataException(
"This policy does not have a project Twilio configuration. Please configure one first.",
);
}
// Get project Twilio config
const customTwilioConfig: TwilioConfig | null =
await getProjectTwilioConfig(incomingCallPolicy.projectCallSMSConfigId);
if (!customTwilioConfig) {
throw new BadDataException("Project Call/SMS Config not found");
}
const provider: ICallProvider =
CallProviderFactory.getProviderWithConfig(customTwilioConfig);
/*
* Construct webhook URL - single endpoint for all phone numbers
* Twilio sends the "To" phone number in every webhook, so we look up the policy by phone number
*/
const webhookUrl: string = `${HttpProtocol}${Host}/notification/incoming-call/voice`;
const purchased: PurchasedPhoneNumber = await provider.purchaseNumber(
phoneNumber,
webhookUrl,
);
// Get country code from phone number
const countryCode: string =
Phone.getCountryCodeFromPhoneNumber(phoneNumber);
const areaCode: string = Phone.getAreaCodeFromPhoneNumber(phoneNumber);
/*
* Update the incoming call policy with the purchased number
* Customer pays Twilio directly - no billing cost stored
*/
await IncomingCallPolicyService.updateOneById({
id: incomingCallPolicyId,
data: {
routingPhoneNumber: new Phone(purchased.phoneNumber),
callProviderPhoneNumberId: purchased.phoneNumberId,
phoneNumberCountryCode: countryCode,
phoneNumberAreaCode: areaCode,
phoneNumberPurchasedAt: new Date(),
},
props: {
isRoot: true,
},
});
return Response.sendJsonObjectResponse(req, res, {
success: true,
phoneNumberId: purchased.phoneNumberId,
phoneNumber: purchased.phoneNumber,
});
} catch (err) {
logger.error(err);
return next(err);
}
},
);
// Release a phone number
router.delete(
"/release/:incomingCallPolicyId",
async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
try {
const incomingCallPolicyId: ObjectID | undefined = req.params[
"incomingCallPolicyId"
]
? new ObjectID(req.params["incomingCallPolicyId"] as string)
: undefined;
if (!incomingCallPolicyId) {
throw new BadDataException("incomingCallPolicyId is required");
}
// Get the incoming call policy with its project config
const incomingCallPolicy: IncomingCallPolicy | null =
await IncomingCallPolicyService.findOneById({
id: incomingCallPolicyId,
select: {
_id: true,
callProviderPhoneNumberId: true,
projectCallSMSConfigId: true,
routingPhoneNumber: true,
},
props: {
isRoot: true,
},
});
if (!incomingCallPolicy) {
throw new BadDataException("Incoming Call Policy not found");
}
if (!incomingCallPolicy.callProviderPhoneNumberId) {
throw new BadDataException("This policy does not have a phone number");
}
// Require project-level Twilio config
if (!incomingCallPolicy.projectCallSMSConfigId) {
throw new BadDataException(
"This policy does not have a project Twilio configuration.",
);
}
// Get project Twilio config
const customTwilioConfig: TwilioConfig | null =
await getProjectTwilioConfig(incomingCallPolicy.projectCallSMSConfigId);
if (!customTwilioConfig) {
throw new BadDataException("Project Call/SMS Config not found");
}
const provider: ICallProvider =
CallProviderFactory.getProviderWithConfig(customTwilioConfig);
await provider.releaseNumber(
incomingCallPolicy.callProviderPhoneNumberId,
);
// Update the incoming call policy to remove the phone number
await IncomingCallPolicyService.updateOneById({
id: incomingCallPolicyId,
data: {
routingPhoneNumber: null,
callProviderPhoneNumberId: null,
phoneNumberCountryCode: null,
phoneNumberAreaCode: null,
phoneNumberPurchasedAt: null,
} as any, // TypeORM allows null for nullable columns
props: {
isRoot: true,
},
});
return Response.sendJsonObjectResponse(req, res, {
success: true,
});
} catch (err) {
logger.error(err);
return next(err);
}
},
);
export default router;

View File

@@ -0,0 +1,108 @@
import Express, {
ExpressRequest,
ExpressResponse,
ExpressRouter,
NextFunction,
} from "Common/Server/Utils/Express";
import Response from "Common/Server/Utils/Response";
import BadDataException from "Common/Types/Exception/BadDataException";
import { JSONObject } from "Common/Types/JSON";
import PushNotificationService from "Common/Server/Services/PushNotificationService";
const router: ExpressRouter = Express.getRouter();
// Simple in-memory rate limiter by IP
const rateLimitMap: Map<string, { count: number; resetTime: number }> =
new Map();
const RATE_LIMIT_WINDOW_MS: number = 60 * 1000; // 1 minute
const RATE_LIMIT_MAX_REQUESTS: number = 60; // 60 requests per minute per IP
function isRateLimited(ip: string): boolean {
const now: number = Date.now();
const entry: { count: number; resetTime: number } | undefined =
rateLimitMap.get(ip);
if (!entry || now > entry.resetTime) {
rateLimitMap.set(ip, { count: 1, resetTime: now + RATE_LIMIT_WINDOW_MS });
return false;
}
entry.count++;
return entry.count > RATE_LIMIT_MAX_REQUESTS;
}
// Clean up stale rate limit entries every 5 minutes
setInterval(
() => {
const now: number = Date.now();
for (const [ip, entry] of rateLimitMap.entries()) {
if (now > entry.resetTime) {
rateLimitMap.delete(ip);
}
}
},
5 * 60 * 1000,
);
router.post(
"/send",
async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
try {
const clientIp: string =
(req.headers["x-forwarded-for"] as string)?.split(",")[0]?.trim() ||
req.socket.remoteAddress ||
"unknown";
if (isRateLimited(clientIp)) {
res.status(429).json({
message: "Rate limit exceeded. Please try again later.",
});
return;
}
if (!PushNotificationService.hasExpoAccessToken()) {
throw new BadDataException(
"Push relay is not configured. EXPO_ACCESS_TOKEN is not set on this server.",
);
}
const body: JSONObject = req.body as JSONObject;
const to: string | undefined = body["to"] as string | undefined;
if (!to || !PushNotificationService.isValidExpoPushToken(to)) {
throw new BadDataException(
"Invalid or missing push token. Must be a valid Expo push token.",
);
}
const title: string | undefined = body["title"] as string | undefined;
const messageBody: string | undefined = body["body"] as
| string
| undefined;
if (!title && !messageBody) {
throw new BadDataException(
"At least one of 'title' or 'body' must be provided.",
);
}
await PushNotificationService.sendRelayPushNotification({
to: to,
...(title !== undefined ? { title } : {}),
...(messageBody !== undefined ? { body: messageBody } : {}),
data: (body["data"] as { [key: string]: string }) || {},
sound: (body["sound"] as string) || "default",
priority: (body["priority"] as string) || "high",
channelId: (body["channelId"] as string) || "default",
});
return Response.sendJsonObjectResponse(req, res, { success: true });
} catch (err) {
return next(err);
}
},
);
export default router;

View File

@@ -312,3 +312,6 @@ export const CallDefaultCostInCentsPerMinute: number = process.env[
]
? parseInt(process.env["CALL_DEFAULT_COST_IN_CENTS_PER_MINUTE"])
: 0;
// Call provider type
export const CallProvider: string = process.env["CALL_PROVIDER"] || "twilio";

View File

@@ -4,7 +4,10 @@ import MailAPI from "./API/Mail";
import SmsAPI from "./API/SMS";
import WhatsAppAPI from "./API/WhatsApp";
import PushNotificationAPI from "./API/PushNotification";
import PushRelayAPI from "./API/PushRelay";
import SMTPConfigAPI from "./API/SMTPConfig";
import PhoneNumberAPI from "./API/PhoneNumber";
import IncomingCallAPI from "./API/IncomingCall";
import "./Utils/Handlebars";
import FeatureSet from "Common/Server/Types/FeatureSet";
import Express, { ExpressApplication } from "Common/Server/Utils/Express";
@@ -19,8 +22,11 @@ const NotificationFeatureSet: FeatureSet = {
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}/push-relay`, "/push-relay"], PushRelayAPI);
app.use([`/${APP_NAME}/call`, "/call"], CallAPI);
app.use([`/${APP_NAME}/smtp-config`, "/smtp-config"], SMTPConfigAPI);
app.use([`/${APP_NAME}/phone-number`, "/phone-number"], PhoneNumberAPI);
app.use([`/${APP_NAME}/incoming-call`, "/incoming-call"], IncomingCallAPI);
},
};

View File

@@ -0,0 +1,88 @@
import { ICallProvider } from "Common/Types/Call/CallProvider";
import CallProviderType from "Common/Types/Call/CallProviderType";
import TwilioCallProvider from "./TwilioCallProvider";
import { getTwilioConfig, CallProvider } from "../Config";
import TwilioConfig from "Common/Types/CallAndSMS/TwilioConfig";
import BadDataException from "Common/Types/Exception/BadDataException";
export default class CallProviderFactory {
private static instance: ICallProvider | null = null;
private static currentProviderType: CallProviderType | null = null;
// Get a provider with the global configuration (cached)
public static async getProvider(): Promise<ICallProvider> {
const providerType: CallProviderType = this.getProviderType();
// Return cached instance if provider type hasn't changed
if (this.instance && this.currentProviderType === providerType) {
return this.instance;
}
switch (providerType) {
case CallProviderType.Twilio: {
const twilioConfig: TwilioConfig | null = await getTwilioConfig();
if (!twilioConfig) {
throw new BadDataException(
"Twilio configuration not found. Please configure Twilio in Admin Dashboard.",
);
}
this.instance = new TwilioCallProvider(twilioConfig);
this.currentProviderType = providerType;
break;
}
default:
throw new BadDataException(`Unknown call provider: ${providerType}`);
}
return this.instance;
}
/*
* Get a provider with a custom configuration (not cached)
* Used when a project has its own Twilio configuration
*/
public static getProviderWithConfig(
customConfig: TwilioConfig,
): ICallProvider {
const providerType: CallProviderType = this.getProviderType();
switch (providerType) {
case CallProviderType.Twilio: {
/*
* Create a new provider instance with the custom config
* This is not cached since it's project-specific
*/
return new TwilioCallProvider(customConfig);
}
default:
throw new BadDataException(`Unknown call provider: ${providerType}`);
}
}
// Get a provider, using custom config if provided, otherwise global config
public static async getProviderWithOptionalConfig(
customConfig?: TwilioConfig,
): Promise<ICallProvider> {
if (customConfig) {
return this.getProviderWithConfig(customConfig);
}
return this.getProvider();
}
public static getProviderType(): CallProviderType {
switch (CallProvider.toLowerCase()) {
case "twilio":
return CallProviderType.Twilio;
default:
return CallProviderType.Twilio;
}
}
// Method to reset the cached instance (useful for testing or config changes)
public static resetProvider(): void {
this.instance = null;
this.currentProviderType = null;
}
}

View File

@@ -0,0 +1,327 @@
import {
AvailablePhoneNumber,
DialOptions,
DialStatusData,
ICallProvider,
IncomingCallData,
OwnedPhoneNumber,
PurchasedPhoneNumber,
SearchNumberOptions,
WebhookRequest,
} from "Common/Types/Call/CallProvider";
import TwilioConfig from "Common/Types/CallAndSMS/TwilioConfig";
import BadDataException from "Common/Types/Exception/BadDataException";
import Twilio, { validateRequest } from "twilio";
import logger from "Common/Server/Utils/Logger";
export default class TwilioCallProvider implements ICallProvider {
private client: Twilio.Twilio;
private config: TwilioConfig;
public constructor(config: TwilioConfig) {
this.config = config;
this.client = new Twilio.Twilio(config.accountSid, config.authToken);
}
public async searchAvailableNumbers(
options: SearchNumberOptions,
): Promise<AvailablePhoneNumber[]> {
const searchOptions: {
voiceEnabled: boolean;
limit: number;
areaCode?: number;
contains?: string;
} = {
voiceEnabled: true,
limit: options.limit || 10,
};
if (options.areaCode) {
searchOptions.areaCode = parseInt(options.areaCode);
}
if (options.contains) {
searchOptions.contains = options.contains;
}
const numbers: Array<{
phoneNumber: string;
friendlyName: string;
locality?: string;
region?: string;
}> = await this.client
.availablePhoneNumbers(options.countryCode)
.local.list(searchOptions);
return numbers.map(
(n: {
phoneNumber: string;
friendlyName: string;
locality?: string;
region?: string;
}): AvailablePhoneNumber => {
const result: AvailablePhoneNumber = {
phoneNumber: n.phoneNumber,
friendlyName: n.friendlyName,
country: options.countryCode,
};
if (n.locality) {
result.locality = n.locality;
}
if (n.region) {
result.region = n.region;
}
return result;
},
);
}
public async listOwnedNumbers(): Promise<OwnedPhoneNumber[]> {
const numbers: Array<{
sid: string;
phoneNumber: string;
friendlyName: string;
voiceUrl?: string;
}> = await this.client.incomingPhoneNumbers.list({
limit: 100,
});
return numbers.map(
(n: {
sid: string;
phoneNumber: string;
friendlyName: string;
voiceUrl?: string;
}): OwnedPhoneNumber => {
return {
phoneNumberId: n.sid,
phoneNumber: n.phoneNumber,
friendlyName: n.friendlyName,
voiceUrl: n.voiceUrl,
};
},
);
}
public async purchaseNumber(
phoneNumber: string,
webhookUrl: string,
): Promise<PurchasedPhoneNumber> {
const purchased: Twilio.Twilio["incomingPhoneNumbers"] extends {
create: (opts: Record<string, unknown>) => Promise<infer R>;
}
? R
: never = await this.client.incomingPhoneNumbers.create({
phoneNumber,
voiceUrl: webhookUrl,
voiceMethod: "POST",
});
return {
phoneNumberId: purchased.sid,
phoneNumber: purchased.phoneNumber,
};
}
public async assignExistingNumber(
phoneNumberId: string,
webhookUrl: string,
): Promise<PurchasedPhoneNumber> {
// Update the webhook URL for an existing phone number
const updated: Twilio.Twilio["incomingPhoneNumbers"] extends {
(sid: string): {
update: (opts: Record<string, unknown>) => Promise<infer R>;
};
}
? R
: never = await this.client.incomingPhoneNumbers(phoneNumberId).update({
voiceUrl: webhookUrl,
voiceMethod: "POST",
});
return {
phoneNumberId: updated.sid,
phoneNumber: updated.phoneNumber,
};
}
public async releaseNumber(phoneNumberId: string): Promise<void> {
await this.client.incomingPhoneNumbers(phoneNumberId).remove();
}
public async updateWebhookUrl(
phoneNumberId: string,
webhookUrl: string,
): Promise<void> {
await this.client.incomingPhoneNumbers(phoneNumberId).update({
voiceUrl: webhookUrl,
voiceMethod: "POST",
});
}
public generateGreetingResponse(message: string): string {
const response: Twilio.twiml.VoiceResponse =
new Twilio.twiml.VoiceResponse();
response.say({ voice: "alice" }, message);
return response.toString();
}
public generateDialResponse(options: DialOptions): string {
const response: Twilio.twiml.VoiceResponse =
new Twilio.twiml.VoiceResponse();
const dial: ReturnType<Twilio.twiml.VoiceResponse["dial"]> = response.dial({
action: options.statusCallbackUrl,
method: "POST",
timeout: options.timeoutSeconds,
callerId: options.fromPhoneNumber,
});
dial.number(options.toPhoneNumber);
return response.toString();
}
public generateHangupResponse(message?: string): string {
const response: Twilio.twiml.VoiceResponse =
new Twilio.twiml.VoiceResponse();
if (message) {
response.say({ voice: "alice" }, message);
}
response.hangup();
return response.toString();
}
public generateEscalationResponse(
message: string,
nextDialOptions: DialOptions,
): string {
const response: Twilio.twiml.VoiceResponse =
new Twilio.twiml.VoiceResponse();
response.say({ voice: "alice" }, message);
const dial: ReturnType<Twilio.twiml.VoiceResponse["dial"]> = response.dial({
action: nextDialOptions.statusCallbackUrl,
method: "POST",
timeout: nextDialOptions.timeoutSeconds,
callerId: nextDialOptions.fromPhoneNumber,
});
dial.number(nextDialOptions.toPhoneNumber);
return response.toString();
}
public parseIncomingCallWebhook(request: WebhookRequest): IncomingCallData {
const body: { CallSid?: string; From?: string; To?: string } =
request.body as { CallSid?: string; From?: string; To?: string };
if (!body.CallSid) {
throw new BadDataException("CallSid not found in webhook request");
}
if (!body.From) {
throw new BadDataException("From not found in webhook request");
}
if (!body.To) {
throw new BadDataException("To not found in webhook request");
}
return {
callId: body.CallSid,
callerPhoneNumber: body.From,
calledPhoneNumber: body.To,
};
}
public parseDialStatusWebhook(request: WebhookRequest): DialStatusData {
const body: {
CallSid?: string;
DialCallStatus?: string;
DialCallDuration?: string;
} = request.body as {
CallSid?: string;
DialCallStatus?: string;
DialCallDuration?: string;
};
if (!body.CallSid) {
throw new BadDataException("CallSid not found in webhook request");
}
return {
callId: body.CallSid,
dialStatus: this.mapTwilioStatus(body.DialCallStatus || "failed"),
dialDurationSeconds: parseInt(body.DialCallDuration || "0"),
};
}
public validateWebhookSignature(
request: WebhookRequest,
signature: string,
): boolean {
const authToken: string = this.config.authToken;
/*
* Build the full URL that Twilio used to generate the signature
* When behind a proxy, use X-Forwarded-Proto and X-Forwarded-Host headers
* These headers are set by reverse proxies (nginx, load balancers, etc.)
*/
const forwardedProto: string | undefined = request.get(
"x-forwarded-proto",
) as string | undefined;
const forwardedHost: string | undefined = request.get(
"x-forwarded-host",
) as string | undefined;
// Use forwarded headers if available, otherwise fall back to request properties
const protocol: string = forwardedProto || request.protocol || "https";
const host: string = forwardedHost || request.get("host") || "";
/*
* Nginx rewrites /notification to /api/notification internally
* But Twilio signed with the original external URL path (/notification/...)
* So we need to remove the /api prefix for signature validation
*/
let originalUrl: string = request.originalUrl;
if (originalUrl.startsWith("/api/notification")) {
originalUrl = originalUrl.replace("/api/notification", "/notification");
}
const url: string = `${protocol}://${host}${originalUrl}`;
const params: Record<string, string> = {};
const body: Record<string, unknown> = request.body as Record<
string,
unknown
>;
for (const key of Object.keys(body)) {
params[key] = String(body[key]);
}
const isValid: boolean = validateRequest(authToken, signature, url, params);
// Debug logging for signature validation
if (!isValid) {
logger.debug("Twilio Webhook Signature Validation Debug:");
logger.debug(` URL used for validation: ${url}`);
logger.debug(` Signature received: ${signature}`);
logger.debug(` Protocol: ${protocol}`);
logger.debug(` Host: ${host}`);
logger.debug(` Original URL (from request): ${request.originalUrl}`);
logger.debug(` Corrected URL path: ${originalUrl}`);
logger.debug(` X-Forwarded-Proto: ${forwardedProto}`);
logger.debug(` X-Forwarded-Host: ${forwardedHost}`);
logger.debug(` Request protocol: ${request.protocol}`);
logger.debug(` Request host header: ${request.get("host")}`);
}
return isValid;
}
private mapTwilioStatus(status: string): DialStatusData["dialStatus"] {
const map: Record<string, DialStatusData["dialStatus"]> = {
completed: "completed",
busy: "busy",
"no-answer": "no-answer",
failed: "failed",
canceled: "canceled",
};
return map[status] || "failed";
}
}

View File

@@ -70,6 +70,7 @@ export default class CallService {
customTwilioConfig?: TwilioConfig | undefined;
incidentId?: ObjectID | undefined;
alertId?: ObjectID | undefined;
monitorId?: ObjectID | undefined;
scheduledMaintenanceId?: ObjectID | undefined;
statusPageId?: ObjectID | undefined;
statusPageAnnouncementId?: ObjectID | undefined;
@@ -120,7 +121,7 @@ export default class CallService {
const fromNumber: Phone = Phone.pickPhoneNumberToSendSMSOrCallFrom({
to: callRequest.to,
primaryPhoneNumberToPickFrom: twilioConfig.primaryPhoneNumber,
seocndaryPhoneNumbersToPickFrom:
secondaryPhoneNumbersToPickFrom:
twilioConfig.secondaryPhoneNumbers || [],
});
callLog.fromNumber = fromNumber;
@@ -144,6 +145,10 @@ export default class CallService {
callLog.alertId = options.alertId;
}
if (options.monitorId) {
callLog.monitorId = options.monitorId;
}
if (options.scheduledMaintenanceId) {
callLog.scheduledMaintenanceId = options.scheduledMaintenanceId;
}

View File

@@ -5,7 +5,7 @@ import {
getSendgridConfig,
} from "../Config";
import SMTPOAuthService from "./SMTPOAuthService";
import SendgridMail, { MailDataRequired } from "@sendgrid/mail";
import SendgridMail, { ClientResponse, MailDataRequired } from "@sendgrid/mail";
import Hostname from "Common/Types/API/Hostname";
import OneUptimeDate from "Common/Types/Date";
import Dictionary from "Common/Types/Dictionary";
@@ -433,12 +433,17 @@ export default class MailService {
try {
for (let attempt: number = 1; attempt <= maxRetries; attempt++) {
try {
await mailer.sendMail({
from: `${options.emailServer.fromName.toString()} <${options.emailServer.fromEmail.toString()}>`,
to: mail.toEmail.toString(),
subject: mail.subject,
html: mail.body,
});
const sendMailResponse: SMTPTransport.SentMessageInfo =
await mailer.sendMail({
from: `${options.emailServer.fromName.toString()} <${options.emailServer.fromEmail.toString()}>`,
to: mail.toEmail.toString(),
subject: mail.subject,
html: mail.body,
});
logger.debug("SMTP Email Provider Response:");
logger.debug(JSON.stringify(sendMailResponse, null, 2));
return; // Success, exit the function
} catch (error) {
lastError = error;
@@ -478,6 +483,7 @@ export default class MailService {
timeout?: number | undefined;
incidentId?: ObjectID | undefined;
alertId?: ObjectID | undefined;
monitorId?: ObjectID | undefined;
scheduledMaintenanceId?: ObjectID | undefined;
statusPageId?: ObjectID | undefined;
statusPageAnnouncementId?: ObjectID | undefined;
@@ -511,6 +517,10 @@ export default class MailService {
emailLog.alertId = options.alertId;
}
if (options.monitorId) {
emailLog.monitorId = options.monitorId;
}
if (options.scheduledMaintenanceId) {
emailLog.scheduledMaintenanceId = options.scheduledMaintenanceId;
}
@@ -652,7 +662,21 @@ export default class MailService {
emailLog.fromEmail = sendgridConfig.fromEmail;
}
await SendgridMail.send(msg);
const sendgridResponse: [ClientResponse, Record<string, unknown>] =
await SendgridMail.send(msg);
logger.debug("SendGrid Email Provider Response:");
logger.debug(
JSON.stringify(
{
statusCode: sendgridResponse[0]?.statusCode,
headers: sendgridResponse[0]?.headers,
body: sendgridResponse[0]?.body,
},
null,
2,
),
);
if (emailLog) {
emailLog.status = MailStatus.Success;

View File

@@ -33,6 +33,7 @@ export default class SmsService {
userOnCallLogTimelineId?: ObjectID | undefined;
incidentId?: ObjectID | undefined;
alertId?: ObjectID | undefined;
monitorId?: ObjectID | undefined;
scheduledMaintenanceId?: ObjectID | undefined;
statusPageId?: ObjectID | undefined;
statusPageAnnouncementId?: ObjectID | undefined;
@@ -91,6 +92,10 @@ export default class SmsService {
smsLog.alertId = options.alertId;
}
if (options.monitorId) {
smsLog.monitorId = options.monitorId;
}
if (options.scheduledMaintenanceId) {
smsLog.scheduledMaintenanceId = options.scheduledMaintenanceId;
}
@@ -140,7 +145,7 @@ export default class SmsService {
const fromNumber: Phone = Phone.pickPhoneNumberToSendSMSOrCallFrom({
to: to,
primaryPhoneNumberToPickFrom: twilioConfig.primaryPhoneNumber,
seocndaryPhoneNumbersToPickFrom:
secondaryPhoneNumbersToPickFrom:
twilioConfig.secondaryPhoneNumbers || [],
});

View File

@@ -41,6 +41,7 @@ export default class WhatsAppService {
userOnCallLogTimelineId?: ObjectID | undefined;
incidentId?: ObjectID | undefined;
alertId?: ObjectID | undefined;
monitorId?: ObjectID | undefined;
scheduledMaintenanceId?: ObjectID | undefined;
statusPageId?: ObjectID | undefined;
statusPageAnnouncementId?: ObjectID | undefined;
@@ -96,6 +97,10 @@ export default class WhatsAppService {
whatsAppLog.alertId = options.alertId;
}
if (options.monitorId) {
whatsAppLog.monitorId = options.monitorId;
}
if (options.scheduledMaintenanceId) {
whatsAppLog.scheduledMaintenanceId = options.scheduledMaintenanceId;
}

View File

@@ -2,7 +2,7 @@
{{> Logo this}}
{{> EmailTitle title=(concat "Alert: " alertTitle) }}
{{> EmailTitle title=(concat "Alert " alertNumber ": " alertTitle) }}
{{> InfoBlock info=(concat "A new alert has been created in the project - " projectName)}}

View File

@@ -0,0 +1,66 @@
{{> Start this}}
{{> Logo this}}
{{> EmailTitle title=(concat "Alert Episode " episodeNumber ": " alertEpisodeTitle) }}
{{> InfoBlock info=(concat "A new alert episode has been created in the project - " projectName)}}
{{> InfoBlock info="Here are the details: "}}
{{> DetailBoxStart this }}
{{> DetailBoxField title="Alert Episode Title:" text=alertEpisodeTitle }}
{{> DetailBoxField title="Current State: " text=currentState }}
{{> DetailBoxField title="Resources Affected: " text=resourcesAffected }}
{{> DetailBoxField title="Severity: " text=alertEpisodeSeverity }}
{{> DetailBoxField title="Root Cause: " text=rootCause }}
{{> DetailBoxField title="Description: " text=alertEpisodeDescription }}
{{> DetailBoxEnd this }}
{{#if alertsList}}
{{> TitleBlock title=(concat "Alerts in this Episode (" alertsCount ")") }}
<!-- Alerts List Container -->
<table class="st-Copy st-Width st-Width--mobile" border="0" cellpadding="0" cellspacing="0"
width="600" style="min-width: 600px;">
<tbody>
<tr>
<td class="st-Spacer st-Spacer--gutter"
style="border: 0; margin:0; padding: 0; font-size: 1px; line-height: 1px; mso-line-height-rule: exactly;"
width="64">
<div class="st-Spacer st-Spacer--filler"></div>
</td>
<td style="border: 0; margin: 0; padding: 0;">
{{{alertsList}}}
</td>
<td class="st-Spacer st-Spacer--gutter"
style="border: 0; margin:0; padding: 0; font-size: 1px; line-height: 1px; mso-line-height-rule: exactly;"
width="64">
<div class="st-Spacer st-Spacer--filler"></div>
</td>
</tr>
<tr>
<td class="st-Spacer st-Spacer--stacked" colspan="3" height="16"
style="border: 0; margin: 0; padding: 0; font-size: 1px; line-height: 1px; mso-line-height-rule: exactly;">
<div class="st-Spacer st-Spacer--filler"></div>
</td>
</tr>
</tbody>
</table>
<!-- /Alerts List Container -->
{{/if}}
{{> InfoBlock info="ACTION REQUIRED: Please acknowledge this alert episode by clicking on the button below - "}}
{{> ButtonBlock buttonUrl=acknowledgeAlertEpisodeLink buttonText="Acknowledge Alert Episode"}}
{{> InfoBlock info="You can also copy and paste this link:"}}
{{> InfoBlock info=acknowledgeAlertEpisodeLink}}
{{> InfoBlock info="You will be notified when the status of this alert episode changes."}}
{{> TitleBlock title="Why am I receiving this email?"}}
{{> InfoBlock info="You are receiving this email because you are a member of the team that is responsible for this alert episode or you are currently on-call."}}
{{> Footer this }}
{{> End this}}

View File

@@ -2,7 +2,7 @@
{{> Logo this}}
{{> EmailTitle title=(concat "Incident: " incidentTitle) }}
{{> EmailTitle title=(concat "Incident " incidentNumber ": " incidentTitle) }}
{{> InfoBlock info=(concat "A new incident has been created in the project - " projectName)}}

View File

@@ -0,0 +1,66 @@
{{> Start this}}
{{> Logo this}}
{{> EmailTitle title=(concat "Incident Episode " episodeNumber ": " incidentEpisodeTitle) }}
{{> InfoBlock info=(concat "A new incident episode has been created in the project - " projectName)}}
{{> InfoBlock info="Here are the details: "}}
{{> DetailBoxStart this }}
{{> DetailBoxField title="Incident Episode Title:" text=incidentEpisodeTitle }}
{{> DetailBoxField title="Current State: " text=currentState }}
{{> DetailBoxField title="Resources Affected: " text=resourcesAffected }}
{{> DetailBoxField title="Severity: " text=incidentEpisodeSeverity }}
{{> DetailBoxField title="Root Cause: " text=rootCause }}
{{> DetailBoxField title="Description: " text=incidentEpisodeDescription }}
{{> DetailBoxEnd this }}
{{#if incidentsList}}
{{> TitleBlock title=(concat "Incidents in this Episode (" incidentsCount ")") }}
<!-- Incidents List Container -->
<table class="st-Copy st-Width st-Width--mobile" border="0" cellpadding="0" cellspacing="0"
width="600" style="min-width: 600px;">
<tbody>
<tr>
<td class="st-Spacer st-Spacer--gutter"
style="border: 0; margin:0; padding: 0; font-size: 1px; line-height: 1px; mso-line-height-rule: exactly;"
width="64">
<div class="st-Spacer st-Spacer--filler"></div>
</td>
<td style="border: 0; margin: 0; padding: 0;">
{{{incidentsList}}}
</td>
<td class="st-Spacer st-Spacer--gutter"
style="border: 0; margin:0; padding: 0; font-size: 1px; line-height: 1px; mso-line-height-rule: exactly;"
width="64">
<div class="st-Spacer st-Spacer--filler"></div>
</td>
</tr>
<tr>
<td class="st-Spacer st-Spacer--stacked" colspan="3" height="16"
style="border: 0; margin: 0; padding: 0; font-size: 1px; line-height: 1px; mso-line-height-rule: exactly;">
<div class="st-Spacer st-Spacer--filler"></div>
</td>
</tr>
</tbody>
</table>
<!-- /Incidents List Container -->
{{/if}}
{{> InfoBlock info="ACTION REQUIRED: Please acknowledge this incident episode by clicking on the button below - "}}
{{> ButtonBlock buttonUrl=acknowledgeIncidentEpisodeLink buttonText="Acknowledge Incident Episode"}}
{{> InfoBlock info="You can also copy and paste this link:"}}
{{> InfoBlock info=acknowledgeIncidentEpisodeLink}}
{{> InfoBlock info="You will be notified when the status of this incident episode changes."}}
{{> TitleBlock title="Why am I receiving this email?"}}
{{> InfoBlock info="You are receiving this email because you are a member of the team that is responsible for this incident episode or you are currently on-call."}}
{{> Footer this }}
{{> End this}}

View File

@@ -0,0 +1,30 @@
{{> Start this}}
{{> Logo this}}
{{> EmailTitle title=(concat "Alert Episode " episodeNumber ": " episodeTitle) }}
{{> InfoBlock info="You have been added as the owner of this alert episode."}}
{{> InfoBlock info="Here are the details: "}}
{{> DetailBoxStart this }}
{{> DetailBoxField title="Episode Title:" text=episodeTitle }}
{{> DetailBoxField title="Current State: " text=currentState }}
{{> DetailBoxField title="Severity: " text=episodeSeverity }}
{{> DetailBoxField title="Description: " text=episodeDescription }}
{{> DetailBoxEnd this }}
{{> InfoBlock info="You can view this alert episode by clicking on the button below - "}}
{{> ButtonBlock buttonUrl=episodeViewLink buttonText="View on Dashboard"}}
{{> InfoBlock info="You can also copy and paste this link:"}}
{{> InfoBlock info=episodeViewLink}}
{{> InfoBlock info="You will be notified when the status of this alert episode changes."}}
{{> Footer this }}
{{> End this}}

View File

@@ -0,0 +1,37 @@
{{> Start this}}
{{> Logo this}}
{{> EmailTitle title=(concat "Alert Episode " episodeNumber ": " episodeTitle) }}
{{> InfoBlock info="A new note has been posted on this alert episode."}}
{{> InfoBlock info="Here are the details: "}}
{{> DetailBoxStart this }}
{{> DetailBoxField title="Episode Title:" text=episodeTitle }}
{{> DetailBoxField title="Current State: " text=currentState }}
{{> DetailBoxField title="Severity: " text=episodeSeverity }}
{{#if isPrivateNote}}
{{> DetailBoxField title="Private Note: " text=note }}
{{else}}
{{> DetailBoxField title="Public Note: " text=note }}
{{/if}}
{{> DetailBoxEnd this }}
{{> InfoBlock info="You can view this alert episode by clicking on the button below - "}}
{{> ButtonBlock buttonUrl=episodeViewLink buttonText="View on Dashboard"}}
{{> InfoBlock info="You can also copy and paste this link:"}}
{{> InfoBlock info=episodeViewLink}}
{{> InfoBlock info="You will be notified when the status of this alert episode changes."}}
{{> OwnerInfo this }}
{{> UnsubscribeOwnerEmail this }}
{{> Footer this }}
{{> End this}}

View File

@@ -0,0 +1,35 @@
{{> Start this}}
{{> Logo this}}
{{> EmailTitle title=(concat "Alert Episode " episodeNumber ": " episodeTitle) }}
{{> InfoBlock info=(concat "A new alert episode has been created in the project - " projectName)}}
{{> InfoBlock info="Here are the details: "}}
{{> DetailBoxStart this }}
{{> DetailBoxField title="Episode Title:" text=episodeTitle }}
{{> DetailBoxField title="Current State: " text=currentState }}
{{> DetailBoxField title="Episode Created By: " text=declaredBy }}
{{> DetailBoxField title="Episode Created At: " text=declaredAt }}
{{> DetailBoxField title="Severity: " text=episodeSeverity }}
{{> DetailBoxField title="Description: " text=episodeDescription }}
{{> DetailBoxEnd this }}
{{> InfoBlock info="You can view this alert episode by clicking on the button below - "}}
{{> ButtonBlock buttonUrl=episodeViewLink buttonText="View on Dashboard"}}
{{> InfoBlock info="You can also copy and paste this link:"}}
{{> InfoBlock info=episodeViewLink}}
{{> InfoBlock info="You will be notified when the status of this alert episode changes."}}
{{> OwnerInfo this }}
{{> UnsubscribeOwnerEmail this }}
{{> Footer this }}
{{> End this}}

View File

@@ -0,0 +1,37 @@
{{> Start this}}
{{> Logo this}}
{{> EmailTitle title=(concat "Alert Episode " episodeNumber ": " episodeTitle) }}
{{> InfoBlock info="Alert episode state has changed"}}
{{> InfoBlock info="Here are the details: "}}
{{> DetailBoxStart this }}
{{> StateTransition this}}
{{#ifNotCond previousStateDurationText ""}}
{{> DetailBoxField title="Duration in Previous State:" text=previousStateDurationText }}
{{/ifNotCond}}
{{> DetailBoxField title="Episode Title:" text=episodeTitle }}
{{> DetailBoxField title="State changed at:" text=stateChangedAt }}
{{> DetailBoxField title="Severity:" text=episodeSeverity }}
{{> DetailBoxField title="Description:" text=episodeDescription }}
{{> DetailBoxEnd this }}
{{> InfoBlock info="You can view this alert episode by clicking on the button below - "}}
{{> ButtonBlock buttonUrl=episodeViewLink buttonText="View on Dashboard"}}
{{> InfoBlock info="You can also copy and paste this link:"}}
{{> InfoBlock info=episodeViewLink}}
{{> InfoBlock info="You will be notified when the status of this alert episode changes."}}
{{> OwnerInfo this }}
{{> UnsubscribeOwnerEmail this }}
{{> Footer this }}
{{> End this}}

View File

@@ -2,7 +2,7 @@
{{> Logo this}}
{{> EmailTitle title=(concat "Alert: " alertTitle) }}
{{> EmailTitle title=(concat "Alert " alertNumber ": " alertTitle) }}
{{> InfoBlock info="You have been added as the owner of this alert."}}

View File

@@ -2,7 +2,7 @@
{{> Logo this}}
{{> EmailTitle title=(concat "Alert: " alertTitle) }}
{{> EmailTitle title=(concat "Alert " alertNumber ": " alertTitle) }}
{{> InfoBlock info="A new note has been posted on this alert."}}

View File

@@ -2,7 +2,7 @@
{{> Logo this}}
{{> EmailTitle title=(concat "Alert: " alertTitle) }}
{{> EmailTitle title=(concat "Alert " alertNumber ": " alertTitle) }}
{{> InfoBlock info=(concat "A new alert has been created in the project - " projectName)}}

View File

@@ -2,7 +2,7 @@
{{> Logo this}}
{{> EmailTitle title=(concat "Alert: " alertTitle) }}
{{> EmailTitle title=(concat "Alert " alertNumber ": " alertTitle) }}
{{> InfoBlock info="Alert state has changed"}}

View File

@@ -0,0 +1,30 @@
{{> Start this}}
{{> Logo this}}
{{> EmailTitle title=(concat "Incident Episode " episodeNumber ": " episodeTitle) }}
{{> InfoBlock info="You have been added as the owner of this incident episode."}}
{{> InfoBlock info="Here are the details: "}}
{{> DetailBoxStart this }}
{{> DetailBoxField title="Episode Title:" text=episodeTitle }}
{{> DetailBoxField title="Current State: " text=currentState }}
{{> DetailBoxField title="Severity: " text=episodeSeverity }}
{{> DetailBoxField title="Description: " text=episodeDescription }}
{{> DetailBoxEnd this }}
{{> InfoBlock info="You can view this incident episode by clicking on the button below - "}}
{{> ButtonBlock buttonUrl=episodeViewLink buttonText="View on Dashboard"}}
{{> InfoBlock info="You can also copy and paste this link:"}}
{{> InfoBlock info=episodeViewLink}}
{{> InfoBlock info="You will be notified when the status of this incident episode changes."}}
{{> Footer this }}
{{> End this}}

View File

@@ -0,0 +1,37 @@
{{> Start this}}
{{> Logo this}}
{{> EmailTitle title=(concat "Incident Episode " episodeNumber ": " episodeTitle) }}
{{> InfoBlock info="A new note has been posted on this incident episode."}}
{{> InfoBlock info="Here are the details: "}}
{{> DetailBoxStart this }}
{{> DetailBoxField title="Episode Title:" text=episodeTitle }}
{{> DetailBoxField title="Current State: " text=currentState }}
{{> DetailBoxField title="Severity: " text=episodeSeverity }}
{{#if isPrivateNote}}
{{> DetailBoxField title="Private Note: " text=note }}
{{else}}
{{> DetailBoxField title="Public Note: " text=note }}
{{/if}}
{{> DetailBoxEnd this }}
{{> InfoBlock info="You can view this incident episode by clicking on the button below - "}}
{{> ButtonBlock buttonUrl=episodeViewLink buttonText="View on Dashboard"}}
{{> InfoBlock info="You can also copy and paste this link:"}}
{{> InfoBlock info=episodeViewLink}}
{{> InfoBlock info="You will be notified when the status of this incident episode changes."}}
{{> OwnerInfo this }}
{{> UnsubscribeOwnerEmail this }}
{{> Footer this }}
{{> End this}}

View File

@@ -0,0 +1,35 @@
{{> Start this}}
{{> Logo this}}
{{> EmailTitle title=(concat "Incident Episode " episodeNumber ": " episodeTitle) }}
{{> InfoBlock info=(concat "A new incident episode has been created in the project - " projectName)}}
{{> InfoBlock info="Here are the details: "}}
{{> DetailBoxStart this }}
{{> DetailBoxField title="Episode Title:" text=episodeTitle }}
{{> DetailBoxField title="Current State: " text=currentState }}
{{> DetailBoxField title="Episode Created By: " text=declaredBy }}
{{> DetailBoxField title="Episode Created At: " text=declaredAt }}
{{> DetailBoxField title="Severity: " text=episodeSeverity }}
{{> DetailBoxField title="Description: " text=episodeDescription }}
{{> DetailBoxEnd this }}
{{> InfoBlock info="You can view this incident episode by clicking on the button below - "}}
{{> ButtonBlock buttonUrl=episodeViewLink buttonText="View on Dashboard"}}
{{> InfoBlock info="You can also copy and paste this link:"}}
{{> InfoBlock info=episodeViewLink}}
{{> InfoBlock info="You will be notified when the status of this incident episode changes."}}
{{> OwnerInfo this }}
{{> UnsubscribeOwnerEmail this }}
{{> Footer this }}
{{> End this}}

View File

@@ -0,0 +1,37 @@
{{> Start this}}
{{> Logo this}}
{{> EmailTitle title=(concat "Incident Episode " episodeNumber ": " episodeTitle) }}
{{> InfoBlock info="Incident episode state has changed"}}
{{> InfoBlock info="Here are the details: "}}
{{> DetailBoxStart this }}
{{> StateTransition this}}
{{#ifNotCond previousStateDurationText ""}}
{{> DetailBoxField title="Duration in Previous State:" text=previousStateDurationText }}
{{/ifNotCond}}
{{> DetailBoxField title="Episode Title:" text=episodeTitle }}
{{> DetailBoxField title="State changed at:" text=stateChangedAt }}
{{> DetailBoxField title="Severity:" text=episodeSeverity }}
{{> DetailBoxField title="Description:" text=episodeDescription }}
{{> DetailBoxEnd this }}
{{> InfoBlock info="You can view this incident episode by clicking on the button below - "}}
{{> ButtonBlock buttonUrl=episodeViewLink buttonText="View on Dashboard"}}
{{> InfoBlock info="You can also copy and paste this link:"}}
{{> InfoBlock info=episodeViewLink}}
{{> InfoBlock info="You will be notified when the status of this incident episode changes."}}
{{> OwnerInfo this }}
{{> UnsubscribeOwnerEmail this }}
{{> Footer this }}
{{> End this}}

View File

@@ -0,0 +1,32 @@
{{> Start this}}
{{> Logo this}}
{{> EmailTitle title=(concat "Incident " incidentNumber ": " incidentTitle) }}
{{> InfoBlock info=(concat "You have been assigned as " incidentRole " to this incident.")}}
{{> InfoBlock info="Here are the details: "}}
{{> DetailBoxStart this }}
{{> DetailBoxField title="Incident Title:" text=incidentTitle }}
{{> DetailBoxField title="Your Role: " text=incidentRole }}
{{> DetailBoxField title="Current State: " text=currentState }}
{{> DetailBoxField title="Resources Affected: " text=resourcesAffected }}
{{> DetailBoxField title="Severity: " text=incidentSeverity }}
{{> DetailBoxField title="Description: " text=incidentDescription }}
{{> DetailBoxEnd this }}
{{> InfoBlock info="You can view this incident by clicking on the button below - "}}
{{> ButtonBlock buttonUrl=incidentViewLink buttonText="View on Dashboard"}}
{{> InfoBlock info="You can also copy and paste this link:"}}
{{> InfoBlock info=incidentViewLink}}
{{> InfoBlock info="You will be notified when the status of this incident changes."}}
{{> Footer this }}
{{> End this}}

View File

@@ -2,7 +2,7 @@
{{> Logo this}}
{{> EmailTitle title=(concat "Incident: " incidentTitle) }}
{{> EmailTitle title=(concat "Incident " incidentNumber ": " incidentTitle) }}
{{> InfoBlock info="You have been added as the owner of this incident."}}

View File

@@ -2,7 +2,7 @@
{{> Logo this}}
{{> EmailTitle title=(concat "Incident: " incidentTitle) }}
{{> EmailTitle title=(concat "Incident " incidentNumber ": " incidentTitle) }}
{{> InfoBlock info="A new note has been posted on this incident."}}

View File

@@ -2,7 +2,7 @@
{{> Logo this}}
{{> EmailTitle title=(concat "Incident: " incidentTitle) }}
{{> EmailTitle title=(concat "Incident " incidentNumber ": " incidentTitle) }}
{{> InfoBlock info=(concat "A new incident has been created in the project - " projectName)}}

View File

@@ -2,7 +2,7 @@
{{> Logo this}}
{{> EmailTitle title=(concat "Incident: " incidentTitle) }}
{{> EmailTitle title=(concat "Incident " incidentNumber ": " incidentTitle) }}
{{> InfoBlock info="Incident state has changed"}}

View File

@@ -0,0 +1,37 @@
{{> Start this}}
{{> Logo this}}
{{> EmailTitle title="Invoice from OneUptime" }}
{{> InfoBlock info="A new invoice has been generated for your account. Here are the details:"}}
{{> DetailBoxStart this }}
{{> DetailBoxField title="Invoice Number:" text=invoiceNumber }}
{{> DetailBoxField title="Invoice Date:" text=invoiceDate }}
{{> DetailBoxField title="Amount:" text=amount }}
{{#if description}}
{{> DetailBoxField title="Description:" text=description }}
{{/if}}
{{> DetailBoxEnd this }}
{{#if invoicePdfUrl}}
{{> InfoBlock info="You can view and download your invoice by clicking the button below:"}}
{{> ButtonBlock buttonUrl=invoicePdfUrl buttonText="View Invoice PDF"}}
{{> InfoBlock info="Or copy and paste this link:"}}
{{> InfoBlock info=invoicePdfUrl}}
{{/if}}
{{#if dashboardLink}}
{{> InfoBlock info="You can also view all your invoices in your dashboard:"}}
{{> ButtonBlock buttonUrl=dashboardLink buttonText="View Billing Dashboard"}}
{{/if}}
{{> InfoBlock info="You have received this email because you are subscribed to receive invoice notifications for this project."}}
{{> Footer this }}
{{> End this}}

View File

@@ -2,7 +2,7 @@
{{> Logo this}}
{{> EmailTitle title=(concat "Scheduled Maintenance: " scheduledMaintenanceTitle) }}
{{> EmailTitle title=(concat "Scheduled Maintenance " scheduledMaintenanceNumber ": " scheduledMaintenanceTitle) }}
{{> InfoBlock info="You have been added as the owner of this scheduled maintenance event."}}

View File

@@ -2,7 +2,7 @@
{{> Logo this}}
{{> EmailTitle title=(concat "Scheduled Maintenance: " scheduledMaintenanceTitle) }}
{{> EmailTitle title=(concat "Scheduled Maintenance " scheduledMaintenanceNumber ": " scheduledMaintenanceTitle) }}
{{> InfoBlock info="A new note has been posted on this scheduled maintenance event"}}

View File

@@ -2,7 +2,7 @@
{{> Logo this}}
{{> EmailTitle title=(concat "Scheduled Maintenance: " scheduledMaintenanceTitle) }}
{{> EmailTitle title=(concat "Scheduled Maintenance " scheduledMaintenanceNumber ": " scheduledMaintenanceTitle) }}
{{> InfoBlock info=(concat "A new scheduled maintenance has been created in the project - " projectName)}}

View File

@@ -2,7 +2,7 @@
{{> Logo this}}
{{> EmailTitle title=(concat "Scheduled Maintenance: " scheduledMaintenanceTitle) }}
{{> EmailTitle title=(concat "Scheduled Maintenance " scheduledMaintenanceNumber ": " scheduledMaintenanceTitle) }}
{{> InfoBlock info="Scheduled Maintenance state has changed"}}

View File

@@ -0,0 +1,36 @@
{{> Start this}}
{{> CustomLogo this}}
{{> EmailTitle title=(concat "New Incident: " episodeTitle) }}
{{> InfoBlock info="A new incident has been reported that may affect the services you're subscribed to."}}
{{> DetailBoxStart this }}
{{> DetailBoxField title="Incident" text=episodeTitle }}
{{#if episodeSeverity}}
{{> DetailBoxField title="Severity" text=episodeSeverity }}
{{/if}}
{{> DetailBoxField title="Affected Resources" text=resourcesAffected }}
{{#if episodeDescription}}
{{> DetailBoxField title="Description" text=episodeDescription }}
{{/if}}
{{> DetailBoxEnd this }}
{{#if detailsUrl}}
{{> ButtonBlock buttonUrl=detailsUrl buttonText="View Incident Details"}}
{{else}}
{{> ButtonBlock buttonUrl=statusPageUrl buttonText="View Status Page"}}
{{/if}}
{{> VerticalSpace this}}
{{#if subscriberEmailNotificationFooterText}}
{{> InfoBlock info=subscriberEmailNotificationFooterText }}
{{/if}}
{{> UnsubscribeBlock this}}
{{> Footer this}}
{{> End this}}

View File

@@ -0,0 +1,30 @@
{{> Start this}}
{{> CustomLogo this}}
{{> EmailTitle title=(concat "Incident: " episodeTitle) }}
{{> InfoBlock info="A new note has been added to the incident. Here are the details:"}}
{{> DetailBoxStart this }}
{{> DetailBoxField title="Incident Title" text=episodeTitle }}
{{> DetailBoxField title="Resources Affected" text=resourcesAffected }}
{{#if episodeSeverity}}
{{> DetailBoxField title="Severity" text=episodeSeverity }}
{{/if}}
{{> DetailBoxField title="Note" text=note }}
{{> DetailBoxEnd this }}
{{> InfoBlock info=(concat subscriberEmailNotificationFooterText "") }}
{{#if detailsUrl}}
{{> InfoBlock info=(concat "Find further information here: " detailsUrl)}}
{{else}}
{{> InfoBlock info=(concat "Find further information here: " statusPageUrl)}}
{{/if}}
{{> UnsubscribeBlock this}}
{{> VerticalSpace this}}
{{> End this}}

View File

@@ -0,0 +1,34 @@
{{> Start this}}
{{> CustomLogo this}}
{{> EmailTitle title=emailTitle }}
{{> InfoBlock info="The status of an incident affecting services you're subscribed to has been updated."}}
{{> DetailBoxStart this }}
{{> DetailBoxField title="Incident" text=episodeTitle }}
{{> DetailBoxField title="Current State" text=episodeState }}
{{#if episodeSeverity}}
{{> DetailBoxField title="Severity" text=episodeSeverity }}
{{/if}}
{{> DetailBoxField title="Affected Resources" text=resourcesAffected }}
{{> DetailBoxEnd this }}
{{#if detailsUrl}}
{{> ButtonBlock buttonUrl=detailsUrl buttonText="View Incident Details"}}
{{else}}
{{> ButtonBlock buttonUrl=statusPageUrl buttonText="View Status Page"}}
{{/if}}
{{> VerticalSpace this}}
{{#if subscriberEmailNotificationFooterText}}
{{> InfoBlock info=subscriberEmailNotificationFooterText }}
{{/if}}
{{> UnsubscribeBlock this}}
{{> Footer this}}
{{> End this}}

View File

@@ -0,0 +1,34 @@
import TwilioConfig from "Common/Types/CallAndSMS/TwilioConfig";
import ObjectID from "Common/Types/ObjectID";
import ProjectCallSMSConfigService from "Common/Server/Services/ProjectCallSMSConfigService";
import ProjectCallSMSConfig from "Common/Models/DatabaseModels/ProjectCallSMSConfig";
/**
* Helper function to get TwilioConfig from project config
* Shared between IncomingCall and PhoneNumber APIs
*/
export async function getProjectTwilioConfig(
projectCallSMSConfigId: ObjectID,
): Promise<TwilioConfig | null> {
const projectConfig: ProjectCallSMSConfig | null =
await ProjectCallSMSConfigService.findOneById({
id: projectCallSMSConfigId,
select: {
twilioAccountSID: true,
twilioAuthToken: true,
twilioPrimaryPhoneNumber: true,
twilioSecondaryPhoneNumbers: true,
},
props: {
isRoot: true,
},
});
if (!projectConfig) {
return null;
}
const twilioConfig: TwilioConfig | undefined =
ProjectCallSMSConfigService.toTwilioConfig(projectConfig);
return twilioConfig || null;
}

69
App/package-lock.json generated
View File

@@ -83,15 +83,18 @@
"ejs": "^3.1.10",
"elkjs": "^0.10.0",
"esbuild": "^0.25.5",
"expo-server-sdk": "^3.15.0",
"express": "^4.21.1",
"formik": "^2.4.6",
"history": "^5.3.0",
"ioredis": "^5.3.2",
"isolated-vm": "^6.0.2",
"json2csv": "^5.0.7",
"json5": "^2.2.3",
"jsonwebtoken": "^9.0.0",
"jwt-decode": "^4.0.0",
"marked": "^12.0.2",
"mermaid": "^11.12.2",
"moment": "^2.30.1",
"moment-timezone": "^0.5.45",
"multer": "^2.0.2",
@@ -111,7 +114,7 @@
"react-dropzone": "^14.2.2",
"react-error-boundary": "^4.0.13",
"react-highlight": "^0.15.0",
"react-markdown": "^8.0.3",
"react-markdown": "^9.0.0",
"react-router-dom": "^6.30.1",
"react-select": "^5.4.0",
"react-spinners": "^0.14.1",
@@ -121,7 +124,7 @@
"recharts": "^2.12.7",
"redis-semaphore": "^5.5.1",
"reflect-metadata": "^0.2.2",
"remark-gfm": "^3.0.1",
"remark-gfm": "^4.0.0",
"slackify-markdown": "^4.4.0",
"slugify": "^1.6.5",
"socket.io": "^4.7.4",
@@ -1559,6 +1562,7 @@
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"dependencies": {
"color-convert": "^2.0.1"
},
@@ -1597,9 +1601,10 @@
}
},
"node_modules/async": {
"version": "3.2.5",
"resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz",
"integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg=="
"version": "3.2.6",
"resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
"integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==",
"license": "MIT"
},
"node_modules/asynckit": {
"version": "0.4.0",
@@ -1608,13 +1613,13 @@
"license": "MIT"
},
"node_modules/axios": {
"version": "1.13.1",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.13.1.tgz",
"integrity": "sha512-hU4EGxxt+j7TQijx1oYdAjw4xuIp1wRQSsbMFwSthCWeBQur1eF+qJ5iQ5sN3Tw8YRzQNKb8jszgBdMDVqwJcw==",
"version": "1.13.5",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.13.5.tgz",
"integrity": "sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q==",
"license": "MIT",
"dependencies": {
"follow-redirects": "^1.15.6",
"form-data": "^4.0.4",
"follow-redirects": "^1.15.11",
"form-data": "^4.0.5",
"proxy-from-env": "^1.1.0"
}
},
@@ -1727,6 +1732,7 @@
"version": "1.1.12",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
"dev": true,
"license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0",
@@ -1870,6 +1876,7 @@
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dev": true,
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
@@ -1972,6 +1979,7 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"dependencies": {
"color-name": "~1.1.4"
},
@@ -1982,7 +1990,8 @@
"node_modules/color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
},
"node_modules/combined-stream": {
"version": "1.0.8",
@@ -2003,7 +2012,8 @@
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
"dev": true
},
"node_modules/convert-source-map": {
"version": "1.9.0",
@@ -2085,9 +2095,10 @@
}
},
"node_modules/diff": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz",
"integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==",
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.3.1"
}
@@ -2386,9 +2397,9 @@
}
},
"node_modules/form-data": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz",
"integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==",
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
"integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
"license": "MIT",
"dependencies": {
"asynckit": "^0.4.0",
@@ -2588,6 +2599,7 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true,
"engines": {
"node": ">=8"
}
@@ -2872,14 +2884,14 @@
}
},
"node_modules/jake": {
"version": "10.8.7",
"resolved": "https://registry.npmjs.org/jake/-/jake-10.8.7.tgz",
"integrity": "sha512-ZDi3aP+fG/LchyBzUM804VjddnwfSfsdeYkwt8NcbKRvo4rFkjhs456iLFn3k2ZUWvNe4i48WACDbza8fhq2+w==",
"version": "10.9.4",
"resolved": "https://registry.npmjs.org/jake/-/jake-10.9.4.tgz",
"integrity": "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==",
"license": "Apache-2.0",
"dependencies": {
"async": "^3.2.3",
"chalk": "^4.0.2",
"async": "^3.2.6",
"filelist": "^1.0.4",
"minimatch": "^3.1.2"
"picocolors": "^1.1.1"
},
"bin": {
"jake": "bin/cli.js"
@@ -3804,6 +3816,7 @@
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dev": true,
"dependencies": {
"brace-expansion": "^1.1.7"
},
@@ -4101,7 +4114,6 @@
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
"dev": true,
"license": "ISC"
},
"node_modules/picomatch": {
@@ -4189,9 +4201,9 @@
"dev": true
},
"node_modules/qs": {
"version": "6.14.1",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz",
"integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==",
"version": "6.14.2",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz",
"integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==",
"license": "BSD-3-Clause",
"dependencies": {
"side-channel": "^1.1.0"
@@ -4585,6 +4597,7 @@
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
"dependencies": {
"has-flag": "^4.0.0"
},

View File

@@ -0,0 +1,140 @@
import { Command } from "commander";
import * as ConfigManager from "../Core/ConfigManager";
import { CLIContext } from "../Types/CLITypes";
import { printSuccess, printError, printInfo } from "../Core/OutputFormatter";
import Table from "cli-table3";
import chalk from "chalk";
export function registerConfigCommands(program: Command): void {
// Login command
const loginCmd: Command = program
.command("login")
.description("Authenticate with a OneUptime instance")
.argument("<api-key>", "API key for authentication")
.argument(
"<instance-url>",
"OneUptime instance URL (e.g. https://oneuptime.com)",
)
.option("--context-name <name>", "Name for this context", "default")
.action(
(
apiKey: string,
instanceUrl: string,
options: { contextName: string },
) => {
try {
const context: CLIContext = {
name: options.contextName,
apiUrl: instanceUrl.replace(/\/+$/, ""),
apiKey: apiKey,
};
ConfigManager.addContext(context);
ConfigManager.setCurrentContext(context.name);
printSuccess(
`Logged in successfully. Context "${context.name}" is now active.`,
);
} catch (error) {
printError(
`Login failed: ${error instanceof Error ? error.message : String(error)}`,
);
process.exit(1);
}
},
);
// Suppress unused variable warning - loginCmd is used for registration
void loginCmd;
// Context commands
const contextCmd: Command = program
.command("context")
.description("Manage CLI contexts (environments/projects)");
contextCmd
.command("list")
.description("List all configured contexts")
.action(() => {
const contexts: Array<CLIContext & { isCurrent: boolean }> =
ConfigManager.listContexts();
if (contexts.length === 0) {
printInfo(
"No contexts configured. Run `oneuptime login` to create one.",
);
return;
}
const noColor: boolean =
process.env["NO_COLOR"] !== undefined ||
process.argv.includes("--no-color");
const table: Table.Table = new Table({
head: ["", "Name", "URL"].map((h: string) => {
return noColor ? h : chalk.cyan(h);
}),
style: { head: [], border: [] },
});
for (const ctx of contexts) {
table.push([ctx.isCurrent ? "*" : "", ctx.name, ctx.apiUrl]);
}
// eslint-disable-next-line no-console
console.log(table.toString());
});
contextCmd
.command("use <name>")
.description("Switch to a different context")
.action((name: string) => {
try {
ConfigManager.setCurrentContext(name);
printSuccess(`Switched to context "${name}".`);
} catch (error) {
printError(error instanceof Error ? error.message : String(error));
process.exit(1);
}
});
contextCmd
.command("current")
.description("Show the current active context")
.action(() => {
const ctx: CLIContext | null = ConfigManager.getCurrentContext();
if (!ctx) {
printInfo(
"No current context set. Run `oneuptime login` to create one.",
);
return;
}
const maskedKey: string =
ctx.apiKey.length > 8
? ctx.apiKey.substring(0, 4) +
"****" +
ctx.apiKey.substring(ctx.apiKey.length - 4)
: "****";
// eslint-disable-next-line no-console
console.log(`Context: ${ctx.name}`);
// eslint-disable-next-line no-console
console.log(`URL: ${ctx.apiUrl}`);
// eslint-disable-next-line no-console
console.log(`API Key: ${maskedKey}`);
});
contextCmd
.command("delete <name>")
.description("Delete a context")
.action((name: string) => {
try {
ConfigManager.removeContext(name);
printSuccess(`Context "${name}" deleted.`);
} catch (error) {
printError(error instanceof Error ? error.message : String(error));
process.exit(1);
}
});
}

View File

@@ -0,0 +1,356 @@
import { Command } from "commander";
import DatabaseModels from "Common/Models/DatabaseModels/Index";
import AnalyticsModels from "Common/Models/AnalyticsModels/Index";
import BaseModel from "Common/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel";
import AnalyticsBaseModel from "Common/Models/AnalyticsModels/AnalyticsBaseModel/AnalyticsBaseModel";
import { ResourceInfo, ResolvedCredentials } from "../Types/CLITypes";
import { executeApiRequest, ApiOperation } from "../Core/ApiClient";
import { CLIOptions, getResolvedCredentials } from "../Core/ConfigManager";
import { formatOutput, printSuccess } from "../Core/OutputFormatter";
import { handleError } from "../Core/ErrorHandler";
import { generateAllFieldsSelect } from "../Utils/SelectFieldGenerator";
import { JSONObject, JSONValue } from "Common/Types/JSON";
import * as fs from "fs";
function toKebabCase(str: string): string {
return str
.replace(/([a-z])([A-Z])/g, "$1-$2")
.replace(/[\s_]+/g, "-")
.toLowerCase();
}
function parseJsonArg(value: string): JSONObject {
try {
return JSON.parse(value) as JSONObject;
} catch {
throw new Error(`Invalid JSON: ${value}`);
}
}
export function discoverResources(): ResourceInfo[] {
const resources: ResourceInfo[] = [];
// Database models
for (const ModelClass of DatabaseModels) {
try {
const model: BaseModel = new ModelClass();
const tableName: string = model.tableName || ModelClass.name;
const singularName: string = model.singularName || tableName;
const pluralName: string = model.pluralName || `${singularName}s`;
const apiPath: string | undefined = model.crudApiPath?.toString();
if (tableName && model.enableMCP && apiPath) {
resources.push({
name: toKebabCase(singularName),
singularName,
pluralName,
apiPath,
tableName,
modelType: "database",
});
}
} catch {
// Skip models that fail to instantiate
}
}
// Analytics models
for (const ModelClass of AnalyticsModels) {
try {
const model: AnalyticsBaseModel = new ModelClass();
const tableName: string = model.tableName || ModelClass.name;
const singularName: string = model.singularName || tableName;
const pluralName: string = model.pluralName || `${singularName}s`;
const apiPath: string | undefined = model.crudApiPath?.toString();
if (tableName && model.enableMCP && apiPath) {
resources.push({
name: toKebabCase(singularName),
singularName,
pluralName,
apiPath,
tableName,
modelType: "analytics",
});
}
} catch {
// Skip models that fail to instantiate
}
}
return resources;
}
function getParentOptions(cmd: Command): CLIOptions {
// Walk up to root program to get global options
let current: Command | null = cmd;
while (current?.parent) {
current = current.parent;
}
const opts: Record<string, unknown> = current?.opts() || {};
return {
apiKey: opts["apiKey"] as string | undefined,
url: opts["url"] as string | undefined,
context: opts["context"] as string | undefined,
};
}
function registerListCommand(
resourceCmd: Command,
resource: ResourceInfo,
): void {
resourceCmd
.command("list")
.description(`List ${resource.pluralName}`)
.option("--query <json>", "Filter query as JSON")
.option("--limit <n>", "Max results to return", "10")
.option("--skip <n>", "Number of results to skip", "0")
.option("--sort <json>", "Sort order as JSON")
.option("-o, --output <format>", "Output format: json, table, wide")
.action(
async (options: {
query?: string;
limit: string;
skip: string;
sort?: string;
output?: string;
}) => {
try {
const parentOpts: CLIOptions = getParentOptions(resourceCmd);
const creds: ResolvedCredentials = getResolvedCredentials(parentOpts);
const select: JSONObject = generateAllFieldsSelect(
resource.tableName,
resource.modelType,
);
const result: JSONValue = await executeApiRequest({
apiUrl: creds.apiUrl,
apiKey: creds.apiKey,
apiPath: resource.apiPath,
operation: "list" as ApiOperation,
query: options.query ? parseJsonArg(options.query) : undefined,
select,
skip: parseInt(options.skip, 10),
limit: parseInt(options.limit, 10),
sort: options.sort ? parseJsonArg(options.sort) : undefined,
});
// Extract data array from response
const responseData: JSONValue =
result && typeof result === "object" && !Array.isArray(result)
? ((result as JSONObject)["data"] as JSONValue) || result
: result;
// eslint-disable-next-line no-console
console.log(formatOutput(responseData, options.output));
} catch (error) {
handleError(error);
}
},
);
}
function registerGetCommand(
resourceCmd: Command,
resource: ResourceInfo,
): void {
resourceCmd
.command("get <id>")
.description(`Get a single ${resource.singularName} by ID`)
.option("-o, --output <format>", "Output format: json, table, wide")
.action(async (id: string, options: { output?: string }) => {
try {
const parentOpts: CLIOptions = getParentOptions(resourceCmd);
const creds: ResolvedCredentials = getResolvedCredentials(parentOpts);
const select: JSONObject = generateAllFieldsSelect(
resource.tableName,
resource.modelType,
);
const result: JSONValue = await executeApiRequest({
apiUrl: creds.apiUrl,
apiKey: creds.apiKey,
apiPath: resource.apiPath,
operation: "read" as ApiOperation,
id,
select,
});
// eslint-disable-next-line no-console
console.log(formatOutput(result, options.output));
} catch (error) {
handleError(error);
}
});
}
function registerCreateCommand(
resourceCmd: Command,
resource: ResourceInfo,
): void {
resourceCmd
.command("create")
.description(`Create a new ${resource.singularName}`)
.option("--data <json>", "Resource data as JSON")
.option("--file <path>", "Read resource data from a JSON file")
.option("-o, --output <format>", "Output format: json, table, wide")
.action(
async (options: { data?: string; file?: string; output?: string }) => {
try {
let data: JSONObject;
if (options.file) {
const fileContent: string = fs.readFileSync(options.file, "utf-8");
data = JSON.parse(fileContent) as JSONObject;
} else if (options.data) {
data = parseJsonArg(options.data);
} else {
throw new Error("Either --data or --file is required for create.");
}
const parentOpts: CLIOptions = getParentOptions(resourceCmd);
const creds: ResolvedCredentials = getResolvedCredentials(parentOpts);
const result: JSONValue = await executeApiRequest({
apiUrl: creds.apiUrl,
apiKey: creds.apiKey,
apiPath: resource.apiPath,
operation: "create" as ApiOperation,
data,
});
// eslint-disable-next-line no-console
console.log(formatOutput(result, options.output));
} catch (error) {
handleError(error);
}
},
);
}
function registerUpdateCommand(
resourceCmd: Command,
resource: ResourceInfo,
): void {
resourceCmd
.command("update <id>")
.description(`Update an existing ${resource.singularName}`)
.requiredOption("--data <json>", "Fields to update as JSON")
.option("-o, --output <format>", "Output format: json, table, wide")
.action(async (id: string, options: { data: string; output?: string }) => {
try {
const data: JSONObject = parseJsonArg(options.data);
const parentOpts: CLIOptions = getParentOptions(resourceCmd);
const creds: ResolvedCredentials = getResolvedCredentials(parentOpts);
const result: JSONValue = await executeApiRequest({
apiUrl: creds.apiUrl,
apiKey: creds.apiKey,
apiPath: resource.apiPath,
operation: "update" as ApiOperation,
id,
data,
});
// eslint-disable-next-line no-console
console.log(formatOutput(result, options.output));
} catch (error) {
handleError(error);
}
});
}
function registerDeleteCommand(
resourceCmd: Command,
resource: ResourceInfo,
): void {
resourceCmd
.command("delete <id>")
.description(`Delete a ${resource.singularName}`)
.option("--force", "Skip confirmation")
.action(async (id: string, _options: { force?: boolean }) => {
try {
const parentOpts: CLIOptions = getParentOptions(resourceCmd);
const creds: ResolvedCredentials = getResolvedCredentials(parentOpts);
await executeApiRequest({
apiUrl: creds.apiUrl,
apiKey: creds.apiKey,
apiPath: resource.apiPath,
operation: "delete" as ApiOperation,
id,
});
printSuccess(`${resource.singularName} ${id} deleted successfully.`);
} catch (error) {
handleError(error);
}
});
}
function registerCountCommand(
resourceCmd: Command,
resource: ResourceInfo,
): void {
resourceCmd
.command("count")
.description(`Count ${resource.pluralName}`)
.option("--query <json>", "Filter query as JSON")
.action(async (options: { query?: string }) => {
try {
const parentOpts: CLIOptions = getParentOptions(resourceCmd);
const creds: ResolvedCredentials = getResolvedCredentials(parentOpts);
const result: JSONValue = await executeApiRequest({
apiUrl: creds.apiUrl,
apiKey: creds.apiKey,
apiPath: resource.apiPath,
operation: "count" as ApiOperation,
query: options.query ? parseJsonArg(options.query) : undefined,
});
// Count response is typically { count: number }
if (
result &&
typeof result === "object" &&
!Array.isArray(result) &&
"count" in (result as JSONObject)
) {
// eslint-disable-next-line no-console
console.log((result as JSONObject)["count"]);
} else {
// eslint-disable-next-line no-console
console.log(result);
}
} catch (error) {
handleError(error);
}
});
}
export function registerResourceCommands(program: Command): void {
const resources: ResourceInfo[] = discoverResources();
for (const resource of resources) {
const resourceCmd: Command = program
.command(resource.name)
.description(`Manage ${resource.pluralName} (${resource.modelType})`);
// Database models get full CRUD
if (resource.modelType === "database") {
registerListCommand(resourceCmd, resource);
registerGetCommand(resourceCmd, resource);
registerCreateCommand(resourceCmd, resource);
registerUpdateCommand(resourceCmd, resource);
registerDeleteCommand(resourceCmd, resource);
registerCountCommand(resourceCmd, resource);
}
// Analytics models get create, list, count
if (resource.modelType === "analytics") {
registerListCommand(resourceCmd, resource);
registerCreateCommand(resourceCmd, resource);
registerCountCommand(resourceCmd, resource);
}
}
}

View File

@@ -0,0 +1,129 @@
import { Command } from "commander";
import {
CLIContext,
ResolvedCredentials,
ResourceInfo,
} from "../Types/CLITypes";
import {
getCurrentContext,
CLIOptions,
getResolvedCredentials,
} from "../Core/ConfigManager";
import { printInfo, printError } from "../Core/OutputFormatter";
import { discoverResources } from "./ResourceCommands";
import Table from "cli-table3";
import chalk from "chalk";
export function registerUtilityCommands(program: Command): void {
// Version command
program
.command("version")
.description("Print CLI version")
.action(() => {
try {
// eslint-disable-next-line @typescript-eslint/no-var-requires, @typescript-eslint/no-require-imports
const pkg: { version: string } = require("../package.json") as {
version: string;
};
// eslint-disable-next-line no-console
console.log(pkg.version);
} catch {
// Fallback if package.json can't be loaded at runtime
// eslint-disable-next-line no-console
console.log("1.0.0");
}
});
// Whoami command
program
.command("whoami")
.description("Show current authentication info")
.action(() => {
try {
const ctx: CLIContext | null = getCurrentContext();
const opts: Record<string, unknown> = program.opts();
const cliOpts: CLIOptions = {
apiKey: opts["apiKey"] as string | undefined,
url: opts["url"] as string | undefined,
context: opts["context"] as string | undefined,
};
let creds: ResolvedCredentials;
try {
creds = getResolvedCredentials(cliOpts);
} catch {
printInfo(
"Not authenticated. Run `oneuptime login` to authenticate.",
);
return;
}
const maskedKey: string =
creds.apiKey.length > 8
? creds.apiKey.substring(0, 4) +
"****" +
creds.apiKey.substring(creds.apiKey.length - 4)
: "****";
// eslint-disable-next-line no-console
console.log(`URL: ${creds.apiUrl}`);
// eslint-disable-next-line no-console
console.log(`API Key: ${maskedKey}`);
if (ctx) {
// eslint-disable-next-line no-console
console.log(`Context: ${ctx.name}`);
}
} catch (error) {
printError(error instanceof Error ? error.message : String(error));
process.exit(1);
}
});
// Resources command
program
.command("resources")
.description("List all available resource types")
.option("--type <type>", "Filter by model type: database, analytics")
.action((options: { type?: string }) => {
const resources: ResourceInfo[] = discoverResources();
const filtered: ResourceInfo[] = options.type
? resources.filter((r: ResourceInfo) => {
return r.modelType === options.type;
})
: resources;
if (filtered.length === 0) {
printInfo("No resources found.");
return;
}
const noColor: boolean =
process.env["NO_COLOR"] !== undefined ||
process.argv.includes("--no-color");
const table: Table.Table = new Table({
head: ["Command", "Singular", "Plural", "Type", "API Path"].map(
(h: string) => {
return noColor ? h : chalk.cyan(h);
},
),
style: { head: [], border: [] },
});
for (const r of filtered) {
table.push([
r.name,
r.singularName,
r.pluralName,
r.modelType,
r.apiPath,
]);
}
// eslint-disable-next-line no-console
console.log(table.toString());
// eslint-disable-next-line no-console
console.log(`\nTotal: ${filtered.length} resources`);
});
}

150
CLI/Core/ApiClient.ts Normal file
View File

@@ -0,0 +1,150 @@
import API from "Common/Utils/API";
import URL from "Common/Types/API/URL";
import Route from "Common/Types/API/Route";
import Headers from "Common/Types/API/Headers";
import HTTPResponse from "Common/Types/API/HTTPResponse";
import HTTPErrorResponse from "Common/Types/API/HTTPErrorResponse";
import Protocol from "Common/Types/API/Protocol";
import Hostname from "Common/Types/API/Hostname";
import { JSONObject, JSONValue } from "Common/Types/JSON";
export type ApiOperation =
| "create"
| "read"
| "list"
| "update"
| "delete"
| "count";
export interface ApiRequestOptions {
apiUrl: string;
apiKey: string;
apiPath: string;
operation: ApiOperation;
id?: string | undefined;
data?: JSONObject | undefined;
query?: JSONObject | undefined;
select?: JSONObject | undefined;
skip?: number | undefined;
limit?: number | undefined;
sort?: JSONObject | undefined;
}
function buildApiRoute(
apiPath: string,
operation: ApiOperation,
id?: string,
): Route {
let fullPath: string = `/api${apiPath}`;
switch (operation) {
case "read":
if (id) {
fullPath = `/api${apiPath}/${id}/get-item`;
}
break;
case "update":
case "delete":
if (id) {
fullPath = `/api${apiPath}/${id}/`;
}
break;
case "count":
fullPath = `/api${apiPath}/count`;
break;
case "list":
fullPath = `/api${apiPath}/get-list`;
break;
case "create":
default:
fullPath = `/api${apiPath}`;
break;
}
return new Route(fullPath);
}
function buildHeaders(apiKey: string): Headers {
return {
"Content-Type": "application/json",
Accept: "application/json",
APIKey: apiKey,
};
}
function buildRequestData(options: ApiRequestOptions): JSONObject | undefined {
switch (options.operation) {
case "create":
return { data: options.data || {} } as JSONObject;
case "update":
return { data: options.data || {} } as JSONObject;
case "list":
case "count":
return {
query: options.query || {},
select: options.select || {},
skip: options.skip || 0,
limit: options.limit || 10,
sort: options.sort || {},
} as JSONObject;
case "read":
return {
select: options.select || {},
} as JSONObject;
case "delete":
default:
return undefined;
}
}
export async function executeApiRequest(
options: ApiRequestOptions,
): Promise<JSONValue> {
const url: URL = URL.fromString(options.apiUrl);
const protocol: Protocol = url.protocol;
const hostname: Hostname = url.hostname;
const api: API = new API(protocol, hostname, new Route("/"));
const route: Route = buildApiRoute(
options.apiPath,
options.operation,
options.id,
);
const headers: Headers = buildHeaders(options.apiKey);
const data: JSONObject | undefined = buildRequestData(options);
const requestUrl: URL = new URL(api.protocol, api.hostname, route);
const baseOptions: { url: URL; headers: Headers } = {
url: requestUrl,
headers,
};
let response: HTTPResponse<JSONObject> | HTTPErrorResponse;
switch (options.operation) {
case "create":
case "count":
case "list":
case "read":
response = await API.post(data ? { ...baseOptions, data } : baseOptions);
break;
case "update":
response = await API.put(data ? { ...baseOptions, data } : baseOptions);
break;
case "delete":
response = await API.delete(
data ? { ...baseOptions, data } : baseOptions,
);
break;
default:
throw new Error(`Unsupported operation: ${options.operation}`);
}
if (response instanceof HTTPErrorResponse) {
throw new Error(
`API error (${response.statusCode}): ${response.message || "Unknown error"}`,
);
}
return response.data;
}

141
CLI/Core/ConfigManager.ts Normal file
View File

@@ -0,0 +1,141 @@
import * as fs from "fs";
import * as path from "path";
import * as os from "os";
import { CLIConfig, CLIContext, ResolvedCredentials } from "../Types/CLITypes";
const CONFIG_DIR: string = path.join(os.homedir(), ".oneuptime");
const CONFIG_FILE: string = path.join(CONFIG_DIR, "config.json");
function getDefaultConfig(): CLIConfig {
return {
currentContext: "",
contexts: {},
defaults: {
output: "table",
limit: 10,
},
};
}
export function load(): CLIConfig {
try {
if (!fs.existsSync(CONFIG_FILE)) {
return getDefaultConfig();
}
const raw: string = fs.readFileSync(CONFIG_FILE, "utf-8");
return JSON.parse(raw) as CLIConfig;
} catch {
return getDefaultConfig();
}
}
export function save(config: CLIConfig): void {
if (!fs.existsSync(CONFIG_DIR)) {
fs.mkdirSync(CONFIG_DIR, { recursive: true });
}
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), {
mode: 0o600,
});
}
export function getCurrentContext(): CLIContext | null {
const config: CLIConfig = load();
if (!config.currentContext) {
return null;
}
return config.contexts[config.currentContext] || null;
}
export function setCurrentContext(name: string): void {
const config: CLIConfig = load();
if (!config.contexts[name]) {
throw new Error(`Context "${name}" does not exist.`);
}
config.currentContext = name;
save(config);
}
export function addContext(context: CLIContext): void {
const config: CLIConfig = load();
config.contexts[context.name] = context;
if (!config.currentContext) {
config.currentContext = context.name;
}
save(config);
}
export function removeContext(name: string): void {
const config: CLIConfig = load();
if (!config.contexts[name]) {
throw new Error(`Context "${name}" does not exist.`);
}
delete config.contexts[name];
if (config.currentContext === name) {
const remaining: string[] = Object.keys(config.contexts);
config.currentContext = remaining[0] || "";
}
save(config);
}
export function listContexts(): Array<CLIContext & { isCurrent: boolean }> {
const config: CLIConfig = load();
return Object.values(config.contexts).map(
(ctx: CLIContext): CLIContext & { isCurrent: boolean } => {
return {
...ctx,
isCurrent: ctx.name === config.currentContext,
};
},
);
}
export interface CLIOptions {
apiKey?: string | undefined;
url?: string | undefined;
context?: string | undefined;
}
export function getResolvedCredentials(
cliOptions: CLIOptions,
): ResolvedCredentials {
// Priority 1: CLI flags
if (cliOptions.apiKey && cliOptions.url) {
return { apiKey: cliOptions.apiKey, apiUrl: cliOptions.url };
}
// Priority 2: Environment variables
const envApiKey: string | undefined = process.env["ONEUPTIME_API_KEY"];
const envUrl: string | undefined = process.env["ONEUPTIME_URL"];
if (envApiKey && envUrl) {
return { apiKey: envApiKey, apiUrl: envUrl };
}
// Priority 3: Specific context if specified via --context flag
if (cliOptions.context) {
const config: CLIConfig = load();
const ctx: CLIContext | undefined = config.contexts[cliOptions.context];
if (ctx) {
return { apiKey: ctx.apiKey, apiUrl: ctx.apiUrl };
}
throw new Error(`Context "${cliOptions.context}" does not exist.`);
}
// Priority 4: Current context in config file
const currentCtx: CLIContext | null = getCurrentContext();
if (currentCtx) {
return { apiKey: currentCtx.apiKey, apiUrl: currentCtx.apiUrl };
}
// Partial env vars + partial context
if (envApiKey || envUrl) {
const ctx: CLIContext | null = getCurrentContext();
return {
apiKey: envApiKey || ctx?.apiKey || "",
apiUrl: envUrl || ctx?.apiUrl || "",
};
}
throw new Error(
"No credentials found. Run `oneuptime login` or set ONEUPTIME_API_KEY and ONEUPTIME_URL environment variables.",
);
}

43
CLI/Core/ErrorHandler.ts Normal file
View File

@@ -0,0 +1,43 @@
import { printError } from "./OutputFormatter";
export enum ExitCode {
Success = 0,
GeneralError = 1,
AuthError = 2,
NotFound = 3,
}
export function handleError(error: unknown): never {
if (error instanceof Error) {
const message: string = error.message;
// Check for auth-related errors
if (
message.includes("API key") ||
message.includes("credentials") ||
message.includes("Unauthorized") ||
message.includes("401")
) {
printError(`Authentication error: ${message}`);
process.exit(ExitCode.AuthError);
}
// Check for not found errors
if (message.includes("404") || message.includes("not found")) {
printError(`Not found: ${message}`);
process.exit(ExitCode.NotFound);
}
// General API errors
if (message.includes("API error")) {
printError(message);
process.exit(ExitCode.GeneralError);
}
printError(`Error: ${message}`);
} else {
printError(`Error: ${String(error)}`);
}
process.exit(ExitCode.GeneralError);
}

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