mirror of
https://github.com/OneUptime/oneuptime.git
synced 2026-01-11 19:56:44 +00:00
1829 lines
51 KiB
TypeScript
Executable file
1829 lines
51 KiB
TypeScript
Executable file
// improt API
|
|
import "./API/BlogAPI";
|
|
import { StaticPath, ViewsPath } from "./Utils/Config";
|
|
import NotFoundUtil from "./Utils/NotFound";
|
|
import ProductCompare, { Product } from "./Utils/ProductCompare";
|
|
import generateSitemapXml from "./Utils/Sitemap";
|
|
import { getPageSEO, PageSEOData } from "./Utils/PageSEO";
|
|
import DatabaseConfig from "Common/Server/DatabaseConfig";
|
|
import HTTPErrorResponse from "Common/Types/API/HTTPErrorResponse";
|
|
import HTTPResponse from "Common/Types/API/HTTPResponse";
|
|
import URL from "Common/Types/API/URL";
|
|
// Removed unused imports after dynamic sitemap refactor
|
|
import { JSONObject } from "Common/Types/JSON";
|
|
import API from "Common/Utils/API";
|
|
import FeatureSet from "Common/Server/Types/FeatureSet";
|
|
import Express, {
|
|
ExpressApplication,
|
|
ExpressRequest,
|
|
ExpressResponse,
|
|
ExpressStatic,
|
|
} from "Common/Server/Utils/Express";
|
|
import "ejs";
|
|
// xmlbuilder imports removed (handled inside Sitemap util)
|
|
import OSSFriends, { OSSFriend, OSSCategory } from "./Utils/OSSFriends";
|
|
import Reviews from "./Utils/Reviews";
|
|
|
|
// import jobs.
|
|
import "./Jobs/UpdateBlog";
|
|
import { getGitHubStarsCount, formatStarCount } from "./Jobs/FetchGitHubStars";
|
|
import {
|
|
getGitHubContributorsCount,
|
|
getGitHubCommitsCount,
|
|
formatCount,
|
|
} from "./Jobs/FetchGitHubStats";
|
|
import { Host, IsBillingEnabled } from "Common/Server/EnvironmentConfig";
|
|
import LocalCache from "Common/Server/Infrastructure/LocalCache";
|
|
|
|
// Helper to get SEO data and merge with homeUrl for templates
|
|
const getSEOForPath: (
|
|
path: string,
|
|
homeUrl: string,
|
|
) => PageSEOData & { fullCanonicalUrl: string } = (
|
|
path: string,
|
|
homeUrl: string,
|
|
): PageSEOData & { fullCanonicalUrl: string } => {
|
|
const seo: PageSEOData = getPageSEO(path);
|
|
const baseUrl: string = homeUrl.replace(/\/$/, "");
|
|
return {
|
|
...seo,
|
|
fullCanonicalUrl: `${baseUrl}${seo.canonicalPath}`,
|
|
};
|
|
};
|
|
|
|
const HomeFeatureSet: FeatureSet = {
|
|
init: async (): Promise<void> => {
|
|
const app: ExpressApplication = Express.getExpressApp();
|
|
|
|
/*
|
|
* Routes
|
|
* Middleware to inject baseUrl and SEO data for templates
|
|
*/
|
|
app.use(
|
|
async (req: ExpressRequest, res: ExpressResponse, next: () => void) => {
|
|
if (!res.locals["homeUrl"]) {
|
|
try {
|
|
// Try to get cached home URL first.
|
|
let homeUrl: string | undefined = LocalCache.getString(
|
|
"home",
|
|
"url",
|
|
);
|
|
|
|
if (!homeUrl) {
|
|
homeUrl = (await DatabaseConfig.getHomeUrl())
|
|
.toString()
|
|
.replace(/\/$/, "");
|
|
LocalCache.setString("home", "url", homeUrl);
|
|
}
|
|
|
|
res.locals["homeUrl"] = homeUrl;
|
|
} catch {
|
|
// Fallback hard-coded production domain if env misconfigured
|
|
res.locals["homeUrl"] = "https://oneuptime.com";
|
|
}
|
|
}
|
|
// Inject SEO data for current path
|
|
res.locals["seo"] = getSEOForPath(
|
|
req.path,
|
|
res.locals["homeUrl"] as string,
|
|
);
|
|
next();
|
|
},
|
|
);
|
|
|
|
app.get("/", (_req: ExpressRequest, res: ExpressResponse) => {
|
|
const { reviewsList1, reviewsList2, reviewsList3 } = Reviews;
|
|
const seo: PageSEOData & { fullCanonicalUrl: string } = getSEOForPath(
|
|
"/",
|
|
res.locals["homeUrl"] as string,
|
|
);
|
|
|
|
const githubStars: string | null = formatStarCount(getGitHubStarsCount());
|
|
const githubContributors: string = formatCount(
|
|
getGitHubContributorsCount(),
|
|
);
|
|
const githubCommits: string = formatCount(getGitHubCommitsCount());
|
|
|
|
res.render(`${ViewsPath}/index`, {
|
|
support: false,
|
|
enableGoogleTagManager: IsBillingEnabled,
|
|
footerCards: true,
|
|
cta: true,
|
|
blackLogo: false,
|
|
requestDemoCta: false,
|
|
reviewsList1,
|
|
reviewsList2,
|
|
reviewsList3,
|
|
seo,
|
|
githubStars,
|
|
githubContributors,
|
|
githubCommits,
|
|
});
|
|
});
|
|
|
|
app.get(
|
|
"/infrastructure-agent/install.sh",
|
|
(_req: ExpressRequest, res: ExpressResponse) => {
|
|
// fetch the file from https://raw.githubusercontent.com/oneuptime/infrastructure-agent/release/Scripts/Install/Linux.sh and send it as response
|
|
res.redirect(
|
|
"https://raw.githubusercontent.com/OneUptime/oneuptime/release/InfrastructureAgent/Scripts/Install/Linux.sh",
|
|
);
|
|
},
|
|
);
|
|
|
|
app.get("/install.sh", (_req: ExpressRequest, res: ExpressResponse) => {
|
|
res.redirect(
|
|
"https://raw.githubusercontent.com/OneUptime/oneuptime/release/Home/Scripts/Install.sh",
|
|
);
|
|
});
|
|
|
|
app.get("/support", async (_req: ExpressRequest, res: ExpressResponse) => {
|
|
const seo: PageSEOData & { fullCanonicalUrl: string } = getSEOForPath(
|
|
"/support",
|
|
res.locals["homeUrl"] as string,
|
|
);
|
|
res.render(`${ViewsPath}/support`, {
|
|
enableGoogleTagManager: IsBillingEnabled,
|
|
seo,
|
|
});
|
|
});
|
|
|
|
app.get(
|
|
"/oss-friends",
|
|
async (_req: ExpressRequest, res: ExpressResponse) => {
|
|
const seo: PageSEOData & { fullCanonicalUrl: string } = getSEOForPath(
|
|
"/oss-friends",
|
|
res.locals["homeUrl"] as string,
|
|
);
|
|
|
|
// Get unique categories in the order they appear
|
|
const categories: OSSCategory[] = [];
|
|
for (const friend of OSSFriends) {
|
|
if (!categories.includes(friend.category)) {
|
|
categories.push(friend.category);
|
|
}
|
|
}
|
|
|
|
res.render(`${ViewsPath}/oss-friends`, {
|
|
ossFriends: OSSFriends.map((friend: OSSFriend) => {
|
|
return {
|
|
...friend,
|
|
repositoryUrl: friend.repositoryUrl.toString(),
|
|
websiteUrl: friend.websiteUrl.toString(),
|
|
};
|
|
}),
|
|
categories,
|
|
enableGoogleTagManager: IsBillingEnabled,
|
|
seo,
|
|
});
|
|
},
|
|
);
|
|
|
|
app.get("/pricing", (_req: ExpressRequest, res: ExpressResponse) => {
|
|
const pricing: Array<JSONObject> = [
|
|
{
|
|
name: "Status Page",
|
|
data: [
|
|
{
|
|
name: "Public Status Page",
|
|
plans: {
|
|
free: "1",
|
|
growth: "Unlimited",
|
|
scale: "Unlimited",
|
|
enterprise: "Unlimited",
|
|
},
|
|
},
|
|
{
|
|
name: "Subscribers",
|
|
plans: {
|
|
free: "100",
|
|
growth: "Unlimited",
|
|
scale: "Unlimited",
|
|
enterprise: "Unlimited",
|
|
},
|
|
},
|
|
{
|
|
name: "Custom Branding",
|
|
plans: {
|
|
free: true,
|
|
growth: true,
|
|
scale: true,
|
|
enterprise: true,
|
|
},
|
|
},
|
|
{
|
|
name: "SSL Certificate",
|
|
plans: {
|
|
free: true,
|
|
growth: true,
|
|
scale: true,
|
|
enterprise: true,
|
|
},
|
|
},
|
|
{
|
|
name: "Custom Domain",
|
|
plans: {
|
|
free: true,
|
|
growth: true,
|
|
scale: true,
|
|
enterprise: true,
|
|
},
|
|
},
|
|
{
|
|
name: "Private Status Page",
|
|
plans: {
|
|
free: false,
|
|
growth: "Unlimited",
|
|
scale: "Unlimited",
|
|
enterprise: "Unlimited",
|
|
},
|
|
},
|
|
{
|
|
name: "Private Status Page Users",
|
|
plans: {
|
|
free: false,
|
|
growth: "Unlimited",
|
|
scale: "Unlimited",
|
|
enterprise: "Unlimited",
|
|
},
|
|
},
|
|
],
|
|
},
|
|
{
|
|
name: "Incident Management",
|
|
data: [
|
|
{
|
|
name: "Basic Incident Management",
|
|
plans: {
|
|
free: true,
|
|
growth: true,
|
|
scale: true,
|
|
enterprise: true,
|
|
},
|
|
},
|
|
{
|
|
name: "Public Postmortem Notes",
|
|
plans: {
|
|
free: true,
|
|
growth: true,
|
|
scale: true,
|
|
enterprise: true,
|
|
},
|
|
},
|
|
{
|
|
name: "Private Postmortem Notes",
|
|
plans: {
|
|
free: false,
|
|
growth: true,
|
|
scale: true,
|
|
enterprise: true,
|
|
},
|
|
},
|
|
{
|
|
name: "Incident Workflows",
|
|
plans: {
|
|
free: false,
|
|
growth: true,
|
|
scale: true,
|
|
enterprise: true,
|
|
},
|
|
},
|
|
{
|
|
name: "Custom Incident State",
|
|
plans: {
|
|
free: false,
|
|
growth: true,
|
|
scale: true,
|
|
enterprise: true,
|
|
},
|
|
},
|
|
{
|
|
name: "Custom Incident Severity",
|
|
plans: {
|
|
free: false,
|
|
growth: true,
|
|
scale: true,
|
|
enterprise: true,
|
|
},
|
|
},
|
|
],
|
|
},
|
|
{
|
|
name: "Monitoring",
|
|
data: [
|
|
{
|
|
name: "Static / Manual Monitors",
|
|
plans: {
|
|
free: true,
|
|
growth: true,
|
|
scale: true,
|
|
enterprise: true,
|
|
},
|
|
},
|
|
{
|
|
name: "Website Monitoring",
|
|
plans: {
|
|
free: true,
|
|
growth: true,
|
|
scale: true,
|
|
enterprise: true,
|
|
},
|
|
},
|
|
{
|
|
name: "API Monitoring",
|
|
plans: {
|
|
free: true,
|
|
growth: true,
|
|
scale: true,
|
|
enterprise: true,
|
|
},
|
|
},
|
|
{
|
|
name: "Synthetic Monitoring (with Playwright)",
|
|
plans: {
|
|
free: true,
|
|
growth: true,
|
|
scale: true,
|
|
enterprise: true,
|
|
},
|
|
},
|
|
|
|
{
|
|
name: "IPv4 Monitoring",
|
|
plans: {
|
|
free: true,
|
|
growth: true,
|
|
scale: true,
|
|
enterprise: true,
|
|
},
|
|
},
|
|
|
|
{
|
|
name: "IPv6 Monitoring",
|
|
plans: {
|
|
free: true,
|
|
growth: true,
|
|
scale: true,
|
|
enterprise: true,
|
|
},
|
|
},
|
|
{
|
|
name: "Inbound Webhook / Heartbeat Monitoring",
|
|
plans: {
|
|
free: true,
|
|
growth: true,
|
|
scale: true,
|
|
enterprise: true,
|
|
},
|
|
},
|
|
{
|
|
name: "VM or Server Monitoring",
|
|
plans: {
|
|
free: true,
|
|
growth: true,
|
|
scale: true,
|
|
enterprise: true,
|
|
},
|
|
},
|
|
{
|
|
name: "Network Monitoring",
|
|
plans: {
|
|
free: true,
|
|
growth: true,
|
|
scale: true,
|
|
enterprise: true,
|
|
},
|
|
},
|
|
{
|
|
name: "Container Monitoring",
|
|
plans: {
|
|
free: "Coming Soon",
|
|
growth: "Coming Soon",
|
|
scale: "Coming Soon",
|
|
enterprise: "Coming Soon",
|
|
},
|
|
},
|
|
{
|
|
name: "Kubernetes Cluster Monitoring",
|
|
plans: {
|
|
free: "Coming Soon",
|
|
growth: "Coming Soon",
|
|
scale: "Coming Soon",
|
|
enterprise: "Coming Soon",
|
|
},
|
|
},
|
|
],
|
|
},
|
|
{
|
|
name: "On-Call and Alerts",
|
|
data: [
|
|
{
|
|
name: "SMS Alerts",
|
|
plans: {
|
|
free: "$0.10/SMS",
|
|
growth: "$0.10/SMS",
|
|
scale: "$0.10/SMS",
|
|
enterprise: "$0.10/SMS",
|
|
},
|
|
},
|
|
{
|
|
name: "Phone Call Alerts",
|
|
plans: {
|
|
free: "$0.10/min",
|
|
growth: "$0.10/min",
|
|
scale: "$0.10/min",
|
|
enterprise: "$0.10/min",
|
|
},
|
|
},
|
|
{
|
|
name: "Bring Your Own Twilio",
|
|
plans: {
|
|
free: true,
|
|
growth: true,
|
|
scale: true,
|
|
enterprise: true,
|
|
},
|
|
},
|
|
{
|
|
name: "Email Alerts",
|
|
plans: {
|
|
free: "Free",
|
|
growth: "Free",
|
|
scale: "Free",
|
|
enterprise: "Free",
|
|
},
|
|
},
|
|
{
|
|
name: "On-Call Escalation",
|
|
plans: {
|
|
free: false,
|
|
growth: true,
|
|
scale: true,
|
|
enterprise: true,
|
|
},
|
|
},
|
|
{
|
|
name: "On-Call Rotation",
|
|
plans: {
|
|
free: false,
|
|
growth: true,
|
|
scale: true,
|
|
enterprise: true,
|
|
},
|
|
},
|
|
{
|
|
name: "Advanced Workflows",
|
|
plans: {
|
|
free: false,
|
|
growth: true,
|
|
scale: true,
|
|
enterprise: true,
|
|
},
|
|
},
|
|
{
|
|
name: "Logs and Events",
|
|
plans: {
|
|
free: false,
|
|
growth: true,
|
|
scale: true,
|
|
enterprise: true,
|
|
},
|
|
},
|
|
{
|
|
name: "Webhook Alerts",
|
|
plans: {
|
|
free: "Coming Soon",
|
|
growth: "Coming Soon",
|
|
scale: "Coming Soon",
|
|
enterprise: "Coming Soon",
|
|
},
|
|
},
|
|
{
|
|
name: "Vacation and OOO Policy",
|
|
plans: {
|
|
free: "Coming Soon",
|
|
growth: "Coming Soon",
|
|
scale: "Coming Soon",
|
|
enterprise: "Coming Soon",
|
|
},
|
|
},
|
|
{
|
|
name: "On-Call Pay",
|
|
plans: {
|
|
free: "Coming Soon",
|
|
growth: "Coming Soon",
|
|
scale: "Coming Soon",
|
|
enterprise: "Coming Soon",
|
|
},
|
|
},
|
|
{
|
|
name: "Reports",
|
|
plans: {
|
|
free: "Coming Soon",
|
|
growth: "Coming Soon",
|
|
scale: "Coming Soon",
|
|
enterprise: "Coming Soon",
|
|
},
|
|
},
|
|
],
|
|
},
|
|
{
|
|
name: "Logs Management",
|
|
data: [
|
|
{
|
|
name: "Ingest with OpenTelemetry",
|
|
plans: {
|
|
free: true,
|
|
growth: true,
|
|
scale: true,
|
|
enterprise: true,
|
|
},
|
|
},
|
|
{
|
|
name: "Ingest with Fluentd",
|
|
plans: {
|
|
free: true,
|
|
growth: true,
|
|
scale: true,
|
|
enterprise: true,
|
|
},
|
|
},
|
|
{
|
|
name: "Ingest +1000 Sources",
|
|
plans: {
|
|
free: true,
|
|
growth: true,
|
|
scale: true,
|
|
enterprise: true,
|
|
},
|
|
},
|
|
{
|
|
name: "Application Logs",
|
|
plans: {
|
|
free: true,
|
|
growth: true,
|
|
scale: true,
|
|
enterprise: true,
|
|
},
|
|
},
|
|
{
|
|
name: "Container Logs",
|
|
plans: {
|
|
free: true,
|
|
growth: true,
|
|
scale: true,
|
|
enterprise: true,
|
|
},
|
|
},
|
|
{
|
|
name: "Data Rentention",
|
|
plans: {
|
|
free: "15 days",
|
|
growth: "Custom",
|
|
scale: "Custom",
|
|
enterprise: "Custom",
|
|
},
|
|
},
|
|
{
|
|
name: "Workflows",
|
|
plans: {
|
|
free: false,
|
|
growth: true,
|
|
scale: true,
|
|
enterprise: true,
|
|
},
|
|
},
|
|
{
|
|
name: "Advanced Team Permissions",
|
|
plans: {
|
|
free: false,
|
|
growth: true,
|
|
scale: true,
|
|
enterprise: true,
|
|
},
|
|
},
|
|
],
|
|
},
|
|
{
|
|
name: "Telemetry / APM",
|
|
data: [
|
|
{
|
|
name: "Metrics",
|
|
plans: {
|
|
free: true,
|
|
growth: true,
|
|
scale: true,
|
|
enterprise: true,
|
|
},
|
|
},
|
|
{
|
|
name: "Traces",
|
|
plans: {
|
|
free: true,
|
|
growth: true,
|
|
scale: true,
|
|
enterprise: true,
|
|
},
|
|
},
|
|
{
|
|
name: "Error Tracking",
|
|
plans: {
|
|
free: true,
|
|
growth: true,
|
|
scale: true,
|
|
enterprise: true,
|
|
},
|
|
},
|
|
{
|
|
name: "Ingest Pricing",
|
|
plans: {
|
|
free: "$0.10/GB",
|
|
growth: "$0.10/GB",
|
|
scale: "$0.10/GB",
|
|
enterprise: "$0.10/GB",
|
|
},
|
|
},
|
|
{
|
|
name: "Data Rentention",
|
|
plans: {
|
|
free: "15 days",
|
|
growth: "Custom",
|
|
scale: "Custom",
|
|
enterprise: "Custom",
|
|
},
|
|
},
|
|
{
|
|
name: "Workflows",
|
|
plans: {
|
|
free: false,
|
|
growth: true,
|
|
scale: true,
|
|
enterprise: true,
|
|
},
|
|
},
|
|
{
|
|
name: "Advanced Team Permissions",
|
|
plans: {
|
|
free: false,
|
|
growth: true,
|
|
scale: true,
|
|
enterprise: true,
|
|
},
|
|
},
|
|
],
|
|
},
|
|
{
|
|
name: "Error Tracking",
|
|
data: [
|
|
{
|
|
name: "Track Errors and Exceptions",
|
|
plans: {
|
|
free: true,
|
|
growth: true,
|
|
scale: true,
|
|
enterprise: true,
|
|
},
|
|
},
|
|
{
|
|
name: "Cross Microservice Issues",
|
|
plans: {
|
|
free: true,
|
|
growth: true,
|
|
scale: true,
|
|
enterprise: true,
|
|
},
|
|
},
|
|
{
|
|
name: "Distributed Tracing",
|
|
plans: {
|
|
free: true,
|
|
growth: true,
|
|
scale: true,
|
|
enterprise: true,
|
|
},
|
|
},
|
|
{
|
|
name: "Stack Traces",
|
|
plans: {
|
|
free: true,
|
|
growth: true,
|
|
scale: true,
|
|
enterprise: true,
|
|
},
|
|
},
|
|
{
|
|
name: "Version Management",
|
|
plans: {
|
|
free: true,
|
|
growth: true,
|
|
scale: true,
|
|
enterprise: true,
|
|
},
|
|
},
|
|
{
|
|
name: "Data Rentention",
|
|
plans: {
|
|
free: "15 days",
|
|
growth: "Custom",
|
|
scale: "Custom",
|
|
enterprise: "Custom",
|
|
},
|
|
},
|
|
{
|
|
name: "Workflows",
|
|
plans: {
|
|
free: false,
|
|
growth: true,
|
|
scale: true,
|
|
enterprise: true,
|
|
},
|
|
},
|
|
{
|
|
name: "Advanced Team Permissions",
|
|
plans: {
|
|
free: false,
|
|
growth: true,
|
|
scale: true,
|
|
enterprise: true,
|
|
},
|
|
},
|
|
],
|
|
},
|
|
{
|
|
name: "AI Agent",
|
|
data: [
|
|
{
|
|
name: "LLM Token Pricing",
|
|
plans: {
|
|
free: "$0.02/1K tokens",
|
|
growth: "$0.02/1K tokens",
|
|
scale: "$0.02/1K tokens",
|
|
enterprise: "$0.02/1K tokens",
|
|
},
|
|
},
|
|
{
|
|
name: "Bring Your Own LLM",
|
|
plans: {
|
|
free: true,
|
|
growth: true,
|
|
scale: true,
|
|
enterprise: true,
|
|
},
|
|
},
|
|
{
|
|
name: "Incident Analysis & Insights",
|
|
plans: {
|
|
free: false,
|
|
growth: true,
|
|
scale: true,
|
|
enterprise: true,
|
|
},
|
|
},
|
|
{
|
|
name: "Root Cause Suggestions",
|
|
plans: {
|
|
free: false,
|
|
growth: true,
|
|
scale: true,
|
|
enterprise: true,
|
|
},
|
|
},
|
|
{
|
|
name: "Automated Runbook Generation",
|
|
plans: {
|
|
free: false,
|
|
growth: true,
|
|
scale: true,
|
|
enterprise: true,
|
|
},
|
|
},
|
|
{
|
|
name: "Log Analysis & Anomaly Detection",
|
|
plans: {
|
|
free: false,
|
|
growth: true,
|
|
scale: true,
|
|
enterprise: true,
|
|
},
|
|
},
|
|
{
|
|
name: "Fix Errors Automatically",
|
|
plans: {
|
|
free: false,
|
|
growth: true,
|
|
scale: true,
|
|
enterprise: true,
|
|
},
|
|
},
|
|
{
|
|
name: "Integrate with GitHub, GitLab",
|
|
plans: {
|
|
free: false,
|
|
growth: true,
|
|
scale: true,
|
|
enterprise: true,
|
|
},
|
|
},
|
|
{
|
|
name: "Integrate with Slack / Teams",
|
|
plans: {
|
|
free: false,
|
|
growth: true,
|
|
scale: true,
|
|
enterprise: true,
|
|
},
|
|
},
|
|
],
|
|
},
|
|
{
|
|
name: "Support and More",
|
|
data: [
|
|
{
|
|
name: "Support",
|
|
plans: {
|
|
free: "Email Support",
|
|
growth: "Email Support",
|
|
scale: "Email and Chat Support",
|
|
enterprise: "Email, Chat, Phone Support",
|
|
},
|
|
},
|
|
{
|
|
name: "Support SLA",
|
|
plans: {
|
|
free: "5 business day",
|
|
growth: "1 business day",
|
|
scale: "6 hours",
|
|
enterprise: "1 hour priority",
|
|
},
|
|
},
|
|
{
|
|
name: "Service SLA",
|
|
plans: {
|
|
free: "99.00%",
|
|
growth: "99.90%",
|
|
scale: "99.95%",
|
|
enterprise: "99.99%",
|
|
},
|
|
},
|
|
],
|
|
},
|
|
{
|
|
name: "Advanced Features",
|
|
data: [
|
|
{
|
|
name: "API Access",
|
|
plans: {
|
|
free: false,
|
|
growth: true,
|
|
scale: true,
|
|
enterprise: true,
|
|
},
|
|
},
|
|
{
|
|
name: "Advanced Workflows",
|
|
plans: {
|
|
free: false,
|
|
growth: "500 Runs / month",
|
|
scale: "2000 Runs /month",
|
|
enterprise: "Unlimited Runs",
|
|
},
|
|
},
|
|
{
|
|
name: "5000+ Integrations",
|
|
plans: {
|
|
free: false,
|
|
growth: true,
|
|
scale: true,
|
|
enterprise: true,
|
|
},
|
|
},
|
|
],
|
|
},
|
|
{
|
|
name: "Billing",
|
|
data: [
|
|
{
|
|
name: "Billing Period",
|
|
plans: {
|
|
free: "Free",
|
|
growth: "Monthly or Yearly",
|
|
scale: "Monthly or Yearly",
|
|
enterprise: "Custom",
|
|
},
|
|
},
|
|
{
|
|
name: "Payment Method",
|
|
plans: {
|
|
free: false,
|
|
growth: "Visa / Mastercard / Amex / Bitcoin",
|
|
scale: "Visa / Mastercard / Amex / Bitcoin",
|
|
enterprise:
|
|
"Visa / Mastercard / Amex / ACH / Invoices / Bitcoin",
|
|
},
|
|
},
|
|
{
|
|
name: "Cancel Anytime",
|
|
plans: {
|
|
free: true,
|
|
growth: true,
|
|
scale: true,
|
|
enterprise: true,
|
|
},
|
|
},
|
|
],
|
|
},
|
|
];
|
|
|
|
const seo: PageSEOData & { fullCanonicalUrl: string } = getSEOForPath(
|
|
"/pricing",
|
|
res.locals["homeUrl"] as string,
|
|
);
|
|
res.render(`${ViewsPath}/pricing`, {
|
|
pricing,
|
|
enableGoogleTagManager: IsBillingEnabled,
|
|
seo,
|
|
});
|
|
});
|
|
|
|
app.get(
|
|
"/enterprise/demo",
|
|
(_req: ExpressRequest, res: ExpressResponse) => {
|
|
const seo: PageSEOData & { fullCanonicalUrl: string } = getSEOForPath(
|
|
"/enterprise/demo",
|
|
res.locals["homeUrl"] as string,
|
|
);
|
|
const { reviewsList1, reviewsList2, reviewsList3 } = Reviews;
|
|
res.render(`${ViewsPath}/demo`, {
|
|
support: false,
|
|
enableGoogleTagManager: IsBillingEnabled,
|
|
footerCards: false,
|
|
cta: false,
|
|
blackLogo: true,
|
|
requestDemoCta: false,
|
|
seo,
|
|
reviewsList1,
|
|
reviewsList2,
|
|
reviewsList3,
|
|
});
|
|
},
|
|
);
|
|
|
|
app.get(
|
|
"/product/status-page",
|
|
(_req: ExpressRequest, res: ExpressResponse) => {
|
|
const seo: PageSEOData & { fullCanonicalUrl: string } = getSEOForPath(
|
|
"/product/status-page",
|
|
res.locals["homeUrl"] as string,
|
|
);
|
|
res.render(`${ViewsPath}/status-page`, {
|
|
enableGoogleTagManager: IsBillingEnabled,
|
|
seo,
|
|
});
|
|
},
|
|
);
|
|
|
|
app.get(
|
|
"/product/logs-management",
|
|
(_req: ExpressRequest, res: ExpressResponse) => {
|
|
const seo: PageSEOData & { fullCanonicalUrl: string } = getSEOForPath(
|
|
"/product/logs-management",
|
|
res.locals["homeUrl"] as string,
|
|
);
|
|
res.render(`${ViewsPath}/logs-management`, {
|
|
enableGoogleTagManager: IsBillingEnabled,
|
|
seo,
|
|
});
|
|
},
|
|
);
|
|
|
|
app.get("/status-page", (_req: ExpressRequest, res: ExpressResponse) => {
|
|
res.redirect("/product/status-page");
|
|
});
|
|
|
|
app.get(
|
|
"/logs-management",
|
|
(_req: ExpressRequest, res: ExpressResponse) => {
|
|
res.redirect("/product/logs-management");
|
|
},
|
|
);
|
|
|
|
let gitHubContributors: Array<JSONObject> = [];
|
|
let gitHubBasicInfo: JSONObject | null = null;
|
|
let gitHubCommits: string = "-";
|
|
|
|
app.get("/about", async (_req: ExpressRequest, res: ExpressResponse) => {
|
|
if (gitHubContributors.length === 0) {
|
|
let contributors: Array<JSONObject> = [];
|
|
|
|
let hasMoreContributors: boolean = true;
|
|
|
|
let pageNumber: number = 1;
|
|
|
|
while (hasMoreContributors) {
|
|
const response: HTTPResponse<Array<JSONObject>> | HTTPErrorResponse =
|
|
await API.get<Array<JSONObject>>({
|
|
url: URL.fromString(
|
|
"https://api.github.com/repos/oneuptime/oneuptime/contributors?page=" +
|
|
pageNumber,
|
|
),
|
|
});
|
|
pageNumber++;
|
|
if ((response.data as Array<JSONObject>).length < 30) {
|
|
hasMoreContributors = false;
|
|
}
|
|
|
|
contributors = contributors.concat(
|
|
response.data as Array<JSONObject>,
|
|
);
|
|
}
|
|
|
|
//cache it.
|
|
gitHubContributors = [...contributors];
|
|
}
|
|
|
|
const response: HTTPResponse<JSONObject> = await API.get({
|
|
url: URL.fromString(
|
|
"https://api.github.com/repos/oneuptime/oneuptime/commits?sha=master&per_page=1&page=1",
|
|
),
|
|
});
|
|
|
|
if (gitHubCommits === "-") {
|
|
// this is of type: '<https://api.github.com/repositories/380744866/commits?sha=master&per_page=1&page=2>; rel="next", <https://api.github.com/repositories/380744866/commits?sha=master&per_page=1&page=22486>; rel="last"',
|
|
const link: string | undefined = response.headers["link"];
|
|
const urlString: string | undefined = link
|
|
?.split(",")[1]
|
|
?.split(";")[0]
|
|
?.replace("<", "")
|
|
.replace(">", "")
|
|
.trim();
|
|
const url: URL = URL.fromString(urlString!);
|
|
const commits: string = Number.parseInt(
|
|
url.getQueryParam("page") as string,
|
|
).toLocaleString();
|
|
|
|
if (!gitHubBasicInfo) {
|
|
const basicInfo: HTTPResponse<JSONObject> = await API.get({
|
|
url: URL.fromString(
|
|
"https://api.github.com/repos/oneuptime/oneuptime",
|
|
),
|
|
});
|
|
|
|
gitHubBasicInfo = basicInfo.data as JSONObject;
|
|
}
|
|
|
|
gitHubCommits = commits;
|
|
}
|
|
|
|
const seo: PageSEOData & { fullCanonicalUrl: string } = getSEOForPath(
|
|
"/about",
|
|
res.locals["homeUrl"] as string,
|
|
);
|
|
res.render(`${ViewsPath}/about`, {
|
|
contributors: gitHubContributors,
|
|
basicInfo: gitHubBasicInfo,
|
|
commits: gitHubCommits,
|
|
enableGoogleTagManager: IsBillingEnabled,
|
|
seo,
|
|
});
|
|
});
|
|
|
|
app.get(
|
|
"/product/status-page",
|
|
(_req: ExpressRequest, res: ExpressResponse) => {
|
|
res.render(`${ViewsPath}/status-page`, {
|
|
support: false,
|
|
enableGoogleTagManager: IsBillingEnabled,
|
|
footerCards: true,
|
|
cta: true,
|
|
blackLogo: false,
|
|
requestDemoCta: false,
|
|
footerCtaText:
|
|
"Start with Status Pages, expand into everything else. Sign up today.",
|
|
});
|
|
},
|
|
);
|
|
|
|
app.get("/status-page", (_req: ExpressRequest, res: ExpressResponse) => {
|
|
res.redirect("/product/status-page");
|
|
});
|
|
|
|
app.get("/workflows", (_req: ExpressRequest, res: ExpressResponse) => {
|
|
res.redirect("/product/workflows");
|
|
});
|
|
|
|
app.get("/on-call", (_req: ExpressRequest, res: ExpressResponse) => {
|
|
res.redirect("/product/on-call");
|
|
});
|
|
|
|
app.get(
|
|
"/product/monitoring",
|
|
(_req: ExpressRequest, res: ExpressResponse) => {
|
|
const seo: PageSEOData & { fullCanonicalUrl: string } = getSEOForPath(
|
|
"/product/monitoring",
|
|
res.locals["homeUrl"] as string,
|
|
);
|
|
res.render(`${ViewsPath}/monitoring`, {
|
|
enableGoogleTagManager: IsBillingEnabled,
|
|
seo,
|
|
});
|
|
},
|
|
);
|
|
|
|
app.get(
|
|
"/product/on-call",
|
|
(_req: ExpressRequest, res: ExpressResponse) => {
|
|
const seo: PageSEOData & { fullCanonicalUrl: string } = getSEOForPath(
|
|
"/product/on-call",
|
|
res.locals["homeUrl"] as string,
|
|
);
|
|
res.render(`${ViewsPath}/on-call`, {
|
|
enableGoogleTagManager: IsBillingEnabled,
|
|
seo,
|
|
});
|
|
},
|
|
);
|
|
|
|
app.get(
|
|
"/product/workflows",
|
|
(_req: ExpressRequest, res: ExpressResponse) => {
|
|
const seo: PageSEOData & { fullCanonicalUrl: string } = getSEOForPath(
|
|
"/product/workflows",
|
|
res.locals["homeUrl"] as string,
|
|
);
|
|
res.render(`${ViewsPath}/workflows`, {
|
|
enableGoogleTagManager: IsBillingEnabled,
|
|
seo,
|
|
});
|
|
},
|
|
);
|
|
|
|
app.get(
|
|
"/product/incident-management",
|
|
(_req: ExpressRequest, res: ExpressResponse) => {
|
|
const seo: PageSEOData & { fullCanonicalUrl: string } = getSEOForPath(
|
|
"/product/incident-management",
|
|
res.locals["homeUrl"] as string,
|
|
);
|
|
res.render(`${ViewsPath}/incident-management`, {
|
|
enableGoogleTagManager: IsBillingEnabled,
|
|
seo,
|
|
});
|
|
},
|
|
);
|
|
|
|
app.get(
|
|
"/product/ai-agent",
|
|
(_req: ExpressRequest, res: ExpressResponse) => {
|
|
const seo: PageSEOData & { fullCanonicalUrl: string } = getSEOForPath(
|
|
"/product/ai-agent",
|
|
res.locals["homeUrl"] as string,
|
|
);
|
|
res.render(`${ViewsPath}/ai-agent`, {
|
|
enableGoogleTagManager: IsBillingEnabled,
|
|
seo,
|
|
});
|
|
},
|
|
);
|
|
|
|
app.get(
|
|
"/product/metrics",
|
|
(_req: ExpressRequest, res: ExpressResponse) => {
|
|
const seo: PageSEOData & { fullCanonicalUrl: string } = getSEOForPath(
|
|
"/product/metrics",
|
|
res.locals["homeUrl"] as string,
|
|
);
|
|
res.render(`${ViewsPath}/metrics`, {
|
|
enableGoogleTagManager: IsBillingEnabled,
|
|
seo,
|
|
});
|
|
},
|
|
);
|
|
|
|
app.get("/product/traces", (_req: ExpressRequest, res: ExpressResponse) => {
|
|
const seo: PageSEOData & { fullCanonicalUrl: string } = getSEOForPath(
|
|
"/product/traces",
|
|
res.locals["homeUrl"] as string,
|
|
);
|
|
res.render(`${ViewsPath}/traces`, {
|
|
enableGoogleTagManager: IsBillingEnabled,
|
|
seo,
|
|
});
|
|
});
|
|
|
|
app.get(
|
|
"/product/exceptions",
|
|
(_req: ExpressRequest, res: ExpressResponse) => {
|
|
const seo: PageSEOData & { fullCanonicalUrl: string } = getSEOForPath(
|
|
"/product/exceptions",
|
|
res.locals["homeUrl"] as string,
|
|
);
|
|
res.render(`${ViewsPath}/exceptions`, {
|
|
enableGoogleTagManager: IsBillingEnabled,
|
|
seo,
|
|
});
|
|
},
|
|
);
|
|
|
|
app.get(
|
|
"/product/dashboards",
|
|
(_req: ExpressRequest, res: ExpressResponse) => {
|
|
const seo: PageSEOData & { fullCanonicalUrl: string } = getSEOForPath(
|
|
"/product/dashboards",
|
|
res.locals["homeUrl"] as string,
|
|
);
|
|
res.render(`${ViewsPath}/dashboards`, {
|
|
enableGoogleTagManager: IsBillingEnabled,
|
|
seo,
|
|
});
|
|
},
|
|
);
|
|
|
|
app.get(
|
|
"/incident-management",
|
|
(_req: ExpressRequest, res: ExpressResponse) => {
|
|
res.redirect("/product/incident-management");
|
|
},
|
|
);
|
|
|
|
app.get("/ai-agent", (_req: ExpressRequest, res: ExpressResponse) => {
|
|
res.redirect("/product/ai-agent");
|
|
});
|
|
|
|
app.get(
|
|
"/enterprise/overview",
|
|
(_req: ExpressRequest, res: ExpressResponse) => {
|
|
const { reviewsList1, reviewsList2, reviewsList3 } = Reviews;
|
|
const seo: PageSEOData & { fullCanonicalUrl: string } = getSEOForPath(
|
|
"/enterprise/overview",
|
|
res.locals["homeUrl"] as string,
|
|
);
|
|
res.render(`${ViewsPath}/enterprise-overview.ejs`, {
|
|
support: false,
|
|
enableGoogleTagManager: IsBillingEnabled,
|
|
footerCards: true,
|
|
cta: true,
|
|
blackLogo: false,
|
|
requestDemoCta: true,
|
|
reviewsList1,
|
|
reviewsList2,
|
|
reviewsList3,
|
|
seo,
|
|
});
|
|
},
|
|
);
|
|
|
|
// Solutions pages
|
|
app.get(
|
|
"/solutions/devops",
|
|
(_req: ExpressRequest, res: ExpressResponse) => {
|
|
const seo: PageSEOData & { fullCanonicalUrl: string } = getSEOForPath(
|
|
"/solutions/devops",
|
|
res.locals["homeUrl"] as string,
|
|
);
|
|
res.render(`${ViewsPath}/solutions/devops`, {
|
|
enableGoogleTagManager: IsBillingEnabled,
|
|
seo,
|
|
});
|
|
},
|
|
);
|
|
|
|
app.get("/solutions/sre", (_req: ExpressRequest, res: ExpressResponse) => {
|
|
const seo: PageSEOData & { fullCanonicalUrl: string } = getSEOForPath(
|
|
"/solutions/sre",
|
|
res.locals["homeUrl"] as string,
|
|
);
|
|
res.render(`${ViewsPath}/solutions/sre`, {
|
|
enableGoogleTagManager: IsBillingEnabled,
|
|
seo,
|
|
});
|
|
});
|
|
|
|
app.get(
|
|
"/solutions/platform",
|
|
(_req: ExpressRequest, res: ExpressResponse) => {
|
|
const seo: PageSEOData & { fullCanonicalUrl: string } = getSEOForPath(
|
|
"/solutions/platform",
|
|
res.locals["homeUrl"] as string,
|
|
);
|
|
res.render(`${ViewsPath}/solutions/platform`, {
|
|
enableGoogleTagManager: IsBillingEnabled,
|
|
seo,
|
|
});
|
|
},
|
|
);
|
|
|
|
app.get(
|
|
"/solutions/developers",
|
|
(_req: ExpressRequest, res: ExpressResponse) => {
|
|
const seo: PageSEOData & { fullCanonicalUrl: string } = getSEOForPath(
|
|
"/solutions/developers",
|
|
res.locals["homeUrl"] as string,
|
|
);
|
|
res.render(`${ViewsPath}/solutions/developers`, {
|
|
enableGoogleTagManager: IsBillingEnabled,
|
|
seo,
|
|
});
|
|
},
|
|
);
|
|
|
|
app.get(
|
|
"/solutions/incident-response",
|
|
(_req: ExpressRequest, res: ExpressResponse) => {
|
|
const seo: PageSEOData & { fullCanonicalUrl: string } = getSEOForPath(
|
|
"/solutions/incident-response",
|
|
res.locals["homeUrl"] as string,
|
|
);
|
|
res.render(`${ViewsPath}/solutions/incident-response`, {
|
|
enableGoogleTagManager: IsBillingEnabled,
|
|
seo,
|
|
});
|
|
},
|
|
);
|
|
|
|
app.get(
|
|
"/solutions/uptime-monitoring",
|
|
(_req: ExpressRequest, res: ExpressResponse) => {
|
|
const seo: PageSEOData & { fullCanonicalUrl: string } = getSEOForPath(
|
|
"/solutions/uptime-monitoring",
|
|
res.locals["homeUrl"] as string,
|
|
);
|
|
res.render(`${ViewsPath}/solutions/uptime-monitoring`, {
|
|
enableGoogleTagManager: IsBillingEnabled,
|
|
seo,
|
|
});
|
|
},
|
|
);
|
|
|
|
app.get(
|
|
"/solutions/observability",
|
|
(_req: ExpressRequest, res: ExpressResponse) => {
|
|
const seo: PageSEOData & { fullCanonicalUrl: string } = getSEOForPath(
|
|
"/solutions/observability",
|
|
res.locals["homeUrl"] as string,
|
|
);
|
|
res.render(`${ViewsPath}/solutions/observability`, {
|
|
enableGoogleTagManager: IsBillingEnabled,
|
|
seo,
|
|
});
|
|
},
|
|
);
|
|
|
|
app.get(
|
|
"/solutions/status-communication",
|
|
(_req: ExpressRequest, res: ExpressResponse) => {
|
|
const seo: PageSEOData & { fullCanonicalUrl: string } = getSEOForPath(
|
|
"/solutions/status-communication",
|
|
res.locals["homeUrl"] as string,
|
|
);
|
|
res.render(`${ViewsPath}/solutions/status-communication`, {
|
|
enableGoogleTagManager: IsBillingEnabled,
|
|
seo,
|
|
});
|
|
},
|
|
);
|
|
|
|
// Industries pages
|
|
app.get(
|
|
"/industries/fintech",
|
|
(_req: ExpressRequest, res: ExpressResponse) => {
|
|
const seo: PageSEOData & { fullCanonicalUrl: string } = getSEOForPath(
|
|
"/industries/fintech",
|
|
res.locals["homeUrl"] as string,
|
|
);
|
|
res.render(`${ViewsPath}/industries/fintech`, {
|
|
enableGoogleTagManager: IsBillingEnabled,
|
|
seo,
|
|
});
|
|
},
|
|
);
|
|
|
|
app.get(
|
|
"/industries/saas",
|
|
(_req: ExpressRequest, res: ExpressResponse) => {
|
|
const seo: PageSEOData & { fullCanonicalUrl: string } = getSEOForPath(
|
|
"/industries/saas",
|
|
res.locals["homeUrl"] as string,
|
|
);
|
|
res.render(`${ViewsPath}/industries/saas`, {
|
|
enableGoogleTagManager: IsBillingEnabled,
|
|
seo,
|
|
});
|
|
},
|
|
);
|
|
|
|
app.get(
|
|
"/industries/healthcare",
|
|
(_req: ExpressRequest, res: ExpressResponse) => {
|
|
const seo: PageSEOData & { fullCanonicalUrl: string } = getSEOForPath(
|
|
"/industries/healthcare",
|
|
res.locals["homeUrl"] as string,
|
|
);
|
|
res.render(`${ViewsPath}/industries/healthcare`, {
|
|
enableGoogleTagManager: IsBillingEnabled,
|
|
seo,
|
|
});
|
|
},
|
|
);
|
|
|
|
app.get(
|
|
"/industries/ecommerce",
|
|
(_req: ExpressRequest, res: ExpressResponse) => {
|
|
const seo: PageSEOData & { fullCanonicalUrl: string } = getSEOForPath(
|
|
"/industries/ecommerce",
|
|
res.locals["homeUrl"] as string,
|
|
);
|
|
res.render(`${ViewsPath}/industries/ecommerce`, {
|
|
enableGoogleTagManager: IsBillingEnabled,
|
|
seo,
|
|
});
|
|
},
|
|
);
|
|
|
|
app.get(
|
|
"/industries/media",
|
|
(_req: ExpressRequest, res: ExpressResponse) => {
|
|
const seo: PageSEOData & { fullCanonicalUrl: string } = getSEOForPath(
|
|
"/industries/media",
|
|
res.locals["homeUrl"] as string,
|
|
);
|
|
res.render(`${ViewsPath}/industries/media`, {
|
|
enableGoogleTagManager: IsBillingEnabled,
|
|
seo,
|
|
});
|
|
},
|
|
);
|
|
|
|
app.get(
|
|
"/industries/government",
|
|
(_req: ExpressRequest, res: ExpressResponse) => {
|
|
const seo: PageSEOData & { fullCanonicalUrl: string } = getSEOForPath(
|
|
"/industries/government",
|
|
res.locals["homeUrl"] as string,
|
|
);
|
|
res.render(`${ViewsPath}/industries/government`, {
|
|
enableGoogleTagManager: IsBillingEnabled,
|
|
seo,
|
|
});
|
|
},
|
|
);
|
|
|
|
app.get("/legal", (_req: ExpressRequest, res: ExpressResponse) => {
|
|
res.render(`${ViewsPath}/legal.ejs`, {
|
|
support: false,
|
|
enableGoogleTagManager: IsBillingEnabled,
|
|
footerCards: true,
|
|
cta: true,
|
|
blackLogo: false,
|
|
section: "terms",
|
|
requestDemoCta: false,
|
|
});
|
|
});
|
|
|
|
app.get("/legal/terms", (_req: ExpressRequest, res: ExpressResponse) => {
|
|
res.render(`${ViewsPath}/legal.ejs`, {
|
|
support: false,
|
|
enableGoogleTagManager: IsBillingEnabled,
|
|
footerCards: true,
|
|
cta: true,
|
|
blackLogo: false,
|
|
section: "terms",
|
|
requestDemoCta: false,
|
|
});
|
|
});
|
|
|
|
app.get("/legal/privacy", (_req: ExpressRequest, res: ExpressResponse) => {
|
|
res.render(`${ViewsPath}/legal.ejs`, {
|
|
support: false,
|
|
enableGoogleTagManager: IsBillingEnabled,
|
|
footerCards: true,
|
|
cta: true,
|
|
blackLogo: false,
|
|
section: "privacy",
|
|
requestDemoCta: false,
|
|
});
|
|
});
|
|
|
|
app.get(
|
|
"/legal/code-of-conduct",
|
|
(_req: ExpressRequest, res: ExpressResponse) => {
|
|
res.render(`${ViewsPath}/legal.ejs`, {
|
|
support: false,
|
|
enableGoogleTagManager: IsBillingEnabled,
|
|
footerCards: true,
|
|
cta: true,
|
|
blackLogo: false,
|
|
section: "code-of-conduct",
|
|
requestDemoCta: false,
|
|
});
|
|
},
|
|
);
|
|
|
|
app.get("/legal/contact", (_req: ExpressRequest, res: ExpressResponse) => {
|
|
res.render(`${ViewsPath}/legal.ejs`, {
|
|
support: false,
|
|
enableGoogleTagManager: IsBillingEnabled,
|
|
footerCards: true,
|
|
cta: true,
|
|
blackLogo: false,
|
|
section: "contact",
|
|
requestDemoCta: false,
|
|
});
|
|
});
|
|
|
|
app.get(
|
|
"/legal/subprocessors",
|
|
(_req: ExpressRequest, res: ExpressResponse) => {
|
|
res.render(`${ViewsPath}/legal.ejs`, {
|
|
support: false,
|
|
enableGoogleTagManager: IsBillingEnabled,
|
|
footerCards: true,
|
|
cta: true,
|
|
blackLogo: false,
|
|
section: "subprocessors",
|
|
requestDemoCta: false,
|
|
});
|
|
},
|
|
);
|
|
|
|
app.get("/legal/ccpa", (_req: ExpressRequest, res: ExpressResponse) => {
|
|
res.render(`${ViewsPath}/legal.ejs`, {
|
|
support: false,
|
|
enableGoogleTagManager: IsBillingEnabled,
|
|
footerCards: true,
|
|
cta: true,
|
|
blackLogo: false,
|
|
section: "ccpa",
|
|
requestDemoCta: false,
|
|
});
|
|
});
|
|
|
|
app.get("/legal/hipaa", (_req: ExpressRequest, res: ExpressResponse) => {
|
|
res.render(`${ViewsPath}/legal.ejs`, {
|
|
support: false,
|
|
enableGoogleTagManager: IsBillingEnabled,
|
|
footerCards: true,
|
|
cta: true,
|
|
blackLogo: false,
|
|
section: "hipaa",
|
|
requestDemoCta: false,
|
|
});
|
|
});
|
|
|
|
app.get("/legal/dmca", (_req: ExpressRequest, res: ExpressResponse) => {
|
|
res.render(`${ViewsPath}/legal.ejs`, {
|
|
support: false,
|
|
enableGoogleTagManager: IsBillingEnabled,
|
|
footerCards: true,
|
|
cta: true,
|
|
blackLogo: false,
|
|
section: "dmca",
|
|
requestDemoCta: false,
|
|
});
|
|
});
|
|
|
|
app.get("/legal/pci", (_req: ExpressRequest, res: ExpressResponse) => {
|
|
res.render(`${ViewsPath}/legal.ejs`, {
|
|
support: false,
|
|
enableGoogleTagManager: IsBillingEnabled,
|
|
footerCards: true,
|
|
cta: true,
|
|
blackLogo: false,
|
|
section: "pci",
|
|
requestDemoCta: false,
|
|
});
|
|
});
|
|
|
|
app.get(
|
|
"/legal/iso-27001",
|
|
(_req: ExpressRequest, res: ExpressResponse) => {
|
|
res.render(`${ViewsPath}/legal.ejs`, {
|
|
support: false,
|
|
enableGoogleTagManager: IsBillingEnabled,
|
|
footerCards: true,
|
|
cta: true,
|
|
blackLogo: false,
|
|
section: "iso-27001",
|
|
requestDemoCta: false,
|
|
});
|
|
},
|
|
);
|
|
|
|
app.get(
|
|
"/legal/iso-27017",
|
|
(_req: ExpressRequest, res: ExpressResponse) => {
|
|
res.render(`${ViewsPath}/legal.ejs`, {
|
|
footerCards: true,
|
|
support: false,
|
|
enableGoogleTagManager: IsBillingEnabled,
|
|
cta: true,
|
|
blackLogo: false,
|
|
section: "iso-27017",
|
|
requestDemoCta: false,
|
|
});
|
|
},
|
|
);
|
|
|
|
app.get(
|
|
"/legal/iso-27018",
|
|
(_req: ExpressRequest, res: ExpressResponse) => {
|
|
res.render(`${ViewsPath}/legal.ejs`, {
|
|
footerCards: true,
|
|
support: false,
|
|
enableGoogleTagManager: IsBillingEnabled,
|
|
cta: true,
|
|
blackLogo: false,
|
|
section: "iso-27018",
|
|
requestDemoCta: false,
|
|
});
|
|
},
|
|
);
|
|
|
|
app.get(
|
|
"/legal/iso-27017",
|
|
(_req: ExpressRequest, res: ExpressResponse) => {
|
|
res.render(`${ViewsPath}/legal.ejs`, {
|
|
footerCards: true,
|
|
support: false,
|
|
enableGoogleTagManager: IsBillingEnabled,
|
|
cta: true,
|
|
blackLogo: false,
|
|
section: "iso-27017",
|
|
requestDemoCta: false,
|
|
});
|
|
},
|
|
);
|
|
|
|
app.get(
|
|
"/legal/iso-27018",
|
|
(_req: ExpressRequest, res: ExpressResponse) => {
|
|
res.render(`${ViewsPath}/legal.ejs`, {
|
|
footerCards: true,
|
|
support: false,
|
|
enableGoogleTagManager: IsBillingEnabled,
|
|
cta: true,
|
|
blackLogo: false,
|
|
section: "iso-27018",
|
|
requestDemoCta: false,
|
|
});
|
|
},
|
|
);
|
|
|
|
app.get("/legal/soc-2", (_req: ExpressRequest, res: ExpressResponse) => {
|
|
res.render(`${ViewsPath}/legal.ejs`, {
|
|
footerCards: true,
|
|
support: false,
|
|
enableGoogleTagManager: IsBillingEnabled,
|
|
cta: true,
|
|
blackLogo: false,
|
|
section: "soc-2",
|
|
requestDemoCta: false,
|
|
});
|
|
});
|
|
|
|
app.get("/legal/soc-3", (_req: ExpressRequest, res: ExpressResponse) => {
|
|
res.render(`${ViewsPath}/legal.ejs`, {
|
|
footerCards: true,
|
|
support: false,
|
|
enableGoogleTagManager: IsBillingEnabled,
|
|
cta: true,
|
|
blackLogo: false,
|
|
section: "soc-3",
|
|
requestDemoCta: false,
|
|
});
|
|
});
|
|
|
|
app.get(
|
|
"/legal/data-residency",
|
|
(_req: ExpressRequest, res: ExpressResponse) => {
|
|
res.render(`${ViewsPath}/legal.ejs`, {
|
|
footerCards: true,
|
|
support: false,
|
|
enableGoogleTagManager: IsBillingEnabled,
|
|
cta: true,
|
|
blackLogo: false,
|
|
section: "data-residency",
|
|
requestDemoCta: false,
|
|
});
|
|
},
|
|
);
|
|
|
|
app.get("/legal/gdpr", (_req: ExpressRequest, res: ExpressResponse) => {
|
|
res.render(`${ViewsPath}/legal.ejs`, {
|
|
footerCards: true,
|
|
support: false,
|
|
enableGoogleTagManager: IsBillingEnabled,
|
|
cta: true,
|
|
blackLogo: false,
|
|
section: "gdpr",
|
|
requestDemoCta: false,
|
|
});
|
|
});
|
|
|
|
app.get("/legal/sla", (_req: ExpressRequest, res: ExpressResponse) => {
|
|
res.render(`${ViewsPath}/legal.ejs`, {
|
|
footerCards: true,
|
|
support: false,
|
|
enableGoogleTagManager: IsBillingEnabled,
|
|
cta: true,
|
|
blackLogo: false,
|
|
section: "sla",
|
|
requestDemoCta: false,
|
|
});
|
|
});
|
|
|
|
app.get(
|
|
"/compare/:product",
|
|
(req: ExpressRequest, res: ExpressResponse) => {
|
|
const productConfig: Product = ProductCompare(
|
|
req.params["product"] as string,
|
|
);
|
|
|
|
if (!productConfig) {
|
|
return NotFoundUtil.renderNotFound(res);
|
|
}
|
|
const seo: PageSEOData & { fullCanonicalUrl: string } = getSEOForPath(
|
|
`/compare/${req.params["product"]}`,
|
|
res.locals["homeUrl"] as string,
|
|
);
|
|
res.render(`${ViewsPath}/product-compare.ejs`, {
|
|
support: false,
|
|
enableGoogleTagManager: IsBillingEnabled,
|
|
footerCards: true,
|
|
cta: true,
|
|
blackLogo: false,
|
|
requestDemoCta: false,
|
|
productConfig,
|
|
onlyShowCompareTable: false,
|
|
seo,
|
|
});
|
|
},
|
|
);
|
|
|
|
// Dynamic Sitemap
|
|
app.get(
|
|
"/sitemap.xml",
|
|
async (_req: ExpressRequest, res: ExpressResponse) => {
|
|
try {
|
|
const xml: string = await generateSitemapXml();
|
|
res.setHeader("Content-Type", "text/xml");
|
|
res.send(xml);
|
|
} catch {
|
|
// Fallback minimal static sitemap if dynamic generation fails
|
|
const fallback: string = `<?xml version="1.0" encoding="UTF-8"?>\n<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">\n <url><loc>https://oneuptime.com/</loc></url>\n</urlset>`;
|
|
res.setHeader("Content-Type", "text/xml");
|
|
res.status(200).send(fallback);
|
|
}
|
|
},
|
|
);
|
|
|
|
// robots.txt (dynamic) - If domain is not oneuptime.com, disallow all.
|
|
app.get("/robots.txt", (_req: ExpressRequest, res: ExpressResponse) => {
|
|
let body: string = "";
|
|
|
|
if (Host !== "oneuptime.com") {
|
|
// Disallow everything on non-production / preview / on-prem domains so they are not indexed.
|
|
body = [
|
|
"User-agent: *",
|
|
"Disallow: /",
|
|
"# Disallowed because host is not oneuptime.com",
|
|
].join("\n");
|
|
} else {
|
|
/*
|
|
* Allow all and point to sitemap
|
|
* res.locals.homeUrl is set earlier middleware; fallback to canonical domain.
|
|
*/
|
|
const homeUrl: string = (
|
|
res.locals["homeUrl"] || "https://oneuptime.com"
|
|
).replace(/\/$/, "");
|
|
body = [
|
|
"User-agent: *",
|
|
"Allow: /",
|
|
`Sitemap: ${homeUrl}/sitemap.xml`,
|
|
].join("\n");
|
|
}
|
|
|
|
res.setHeader("Content-Type", "text/plain; charset=utf-8");
|
|
// Encourage caches to revalidate often in case environment changes.
|
|
res.setHeader("Cache-Control", "public, max-age=300"); // 5 minutes
|
|
return res.status(200).send(body + "\n");
|
|
});
|
|
|
|
/*
|
|
* Cache policy for static contents
|
|
* Loads up the site faster
|
|
*/
|
|
app.use(
|
|
ExpressStatic(StaticPath, {
|
|
setHeaders(res: ExpressResponse) {
|
|
res.setHeader("Cache-Control", "public,max-age=31536000,immutable");
|
|
},
|
|
}),
|
|
);
|
|
|
|
app.get("/*", (_req: ExpressRequest, res: ExpressResponse) => {
|
|
return NotFoundUtil.renderNotFound(res);
|
|
});
|
|
},
|
|
};
|
|
|
|
export default HomeFeatureSet;
|